← All posts

The briefing is a list of your decisions now, not a report on the agents

The morning brief used to be one markdown string a model wrote end to end, which meant every content rule — section order, whether an empty section renders, how many things may need you, whether "On track" is allowed next to "nothing ran" — was a sentence in a prompt. It is now a typed document with nine sections, an attention budget enforced in code, deltas joined against yesterday's brief, and system vocabulary moved into a footnote. Release notes 2026-09-16 — vocion-core#371.

Vocion Teamvocion-v2.88.0

Status: shipped. #371, commit 86b2ba3e, cut as v2.87.0. Migration 0110_briefing_document.sql — a nullable jsonb column, so briefs written before this release keep rendering their markdown and nothing needs backfilling. Part of Vocion 2.88. One gap up front: no live agent has produced a typed briefing end to end yet. The contract, the budget, the deltas, the redaction and the pages are all exercised against fixtures and a scripted model. That is stated again at the bottom rather than buried.

The old briefing was one markdown string a model wrote from top to bottom. Everything a brief ought to guarantee was therefore a sentence in a prompt: what order the sections come in, whether a section with nothing in it still draws a heading, how many things may claim to need you, whether "On track" is allowed to sit two lines above "nothing ran, so there is nothing to judge yet."

A prompt is not a guarantee. This release makes those rules a contract.

The brief is nine sections, in one order

A briefing is now a typed document — BriefingV2 — with exactly nine named sections, every one optional:

SectionWhat it is for
1Todaythe state of the business now, at most five metrics, each with its delta
2Needs your decisionwhat only you can decide — at most three, unless one is a genuine incident
3Changed since the last briefwhat is materially different since yesterday
4Today's critical pathwhat is on the clock, in time order
5Risks & exceptionswhat is off-plan, contradictory, stalled or missing
6Pipeline detailthe compact table you open when you want depth
7Agent activitydid the workforce operate correctly — only when it did not
8Sources & run detailsprovenance, collapsed
9Previous briefingsthe last few, then the archive

The order is not a convention anybody has to remember. The renderer walks a filter over the section list itself, so a section cannot be moved, renamed or added without changing the one constant that defines what a section is.

A section with nothing in it is absent

Not collapsed, not greyed, not "No exceptions today." Absent. The renderer has no branch that can draw an empty state, and a document that carries an empty section fails validation rather than rendering one.

This is the change with the largest effect on how the page reads. A brief on a quiet day is short. A brief on a loud day is long. The length of the page is now information.

The attention budget is code

Five constants, with tests:

The composer applies the budget before the document is stored and the renderer applies it again on the way out, so neither a model-written nor a hand-written document can spend more of your attention than the product allows.

Deltas, not state

"$3.52M open pipeline" is useful exactly once. Tomorrow what matters is whether it became $3.72M.

The previous value is now a typed join on the metric's key against the prior brief in the same scope — not a number the model remembered to mention. Two rules the tests hold:

Changed since the last brief is computed first and narrated second: the list is the keys whose value or status actually moved, and the model's contribution is one clause explaining each move. A clause for a key that did not move is dropped.

"On track" is not available without evidence

A green status next to "nothing ran" is the single fastest way for a briefing to stop being believed.

A coloured verdict now requires at least one measure that the system can actually see — provenance verified or observed, the two kinds that come from a system of record or from Vocion's own tables — and that carries a target to be judged against. Without one, the answer is Not enough evidence, and the section is omitted entirely unless you have set a target, in which case the brief says exactly that and nothing more: "A target is set, but nothing the system can verify has been measured against it yet."

Worth being precise about how this is enforced, because it is easy to overclaim. One function derives the verdict and it is the only writer; a status assembled by hand does not survive validation; and the field is simply not on the agent's tool — there is no argument the model can pass that produces a colour. That is a runtime guard plus a missing field, not an impossibility the type system proves. It holds because three things agree, not because one of them is unforgeable.

The cards are inbox rows

Needs your decision is a split across the inbox's own kinds plus the alignment ledger, and the headline is derived from the split rather than from the raw queue depth: "3 decisions need you today · 658 lower-priority items queued." The big number cannot become the headline.

The cards themselves are inbox rows — the same component, with one new line carrying the brief's why-now. Approving from the brief and approving from the inbox are one code path. There is no second decision UI to keep in sync.

And the queue counts are links: "612 safe to batch" opens the proposals, "46 background" opens the queue.

The chip that was a prompt wearing an action's clothes

Every bullet in the old needs you section carried a chip that opened the conversation with the bullet's own text as the message. Clicking the one beside "4 learning candidates to adopt or reject" sent exactly that string back as a chat message and left the work undone.

The chip is gone, along with the heading heuristic that decided which bullets got one. Every actionable item on a typed brief now routes to the surface that does the thing. A markdown brief from before this release cannot know where its bullets route, so it offers no action rather than a fake one — and Ask about this stays, as what it always was. There is a test that clicks every control on the page and asserts that the conversation surface is never reached and that every link is a dashboard route.

System vocabulary moved, not deleted

Agent slugs, job names, tool names, token counts, run ids, connector field names and schema identifiers are admissible in exactly two sections: Agent activity and Sources & run details. Everywhere else, a pass pulls them out of the prose and parks them in a numbered footnote under Sources & run details.

"hs_deal_stage_probability exists in the CRM's schema but is not returned by the counting tools available" is good transparency for debugging and does not belong in the first paragraph of a morning brief. It is still there, one disclosure away, with the footnote saying what kind of token it was.

The workspace brief is composed, not concatenated

A cross-team brief is built from the team briefs: claims de-duplicated, then ranked across teams on provenance first, then movement, then size. Team briefs are sources. A briefing never contains another briefing.

The page

/dashboard/briefings is the current brief, /dashboard/briefings/[id] an individual one, and /dashboard/briefings/archive is where search and filter live — both held in the URL, so a filtered archive is a link you can send someone.

The brief also moved onto the shared pattern library: the page frame, the decision rows, the history rows and the archive all render through the same components as the rest of the dashboard, and every disclosure on the page — the Why? behind an unavailable metric, the folded changes, the pipeline detail, agent activity, sources — is the same control, so "there is more underneath" looks identical in all five places.

One caveat on the archive: it reads the newest 500 briefs and filters the title in the browser. That is honest for the volumes it holds today and it is not a scalable search; a workspace past 500 briefings will need a real query behind it.

What is not done

No live agent has produced a typed briefing end to end. Everything above — the contract, the budget, the delta join, the redaction, the pages — is verified against fixtures and a scripted model. What has not been watched is a real agent reading a real workspace and publishing a document that passes the validator on the first try.

Where this lives