assay · the unit of work
Briefs
A brief is a self-contained scope-and-DoD contract — the fundamental unit of work in Assay. One agent must be able to execute it without reading the rest of the plan. Every field is designed so a script can check it.
Why briefs
When a fleet of agents does the implementation, work units written as prose in a task tracker fail silently. Dependencies written as "after the auth brief" break on a rename with no error. A brief is a machine-readable contract: typed IDs, explicit dependencies, a parseable Verify table. The form forces the structure that makes drift visible.
Anatomy of a brief
Every brief carries YAML frontmatter and a structured body. The frontmatter is the parseable record; the body motivates but never solely carries a fact a script needs. These eleven fields are required — a brief missing any of them fails lint.
| Field | What it holds | Why it is there |
|---|---|---|
schema |
brief-v1 |
The opt-in marker. A document without it is not read as a brief at all, so adding the format to an existing repository is additive rather than a migration. |
brief |
Typed ID: stream/NN |
Links the brief to its stream README table row. Never a prose name. |
title |
One-line summary | Human-readable, for the board and Next-up display. |
wave |
Integer: 0 = no deps, N = deps in waves < N | Derived from the dependency graph. Lets the board schedule parallel work. |
depends / unblocks |
Typed ID arrays, mutual inverses | A typed ID survives renumbering and greps cleanly. No prose arrows. |
effort |
S, M, or L |
Scheduling tier. S may run inline; M/L plan then dispatch to implementers. |
gate |
model or human |
Derived from risk answers. Any yes in risk: forces human. |
risk |
Four booleans: regulatory, customer, irreversible, sensitive-data | Record all four. The gate is their conclusion — not a separate choice. |
issues |
Integer array of tracked issue numbers (may be empty) | Ties the brief to whatever was filed against it. Required even when empty, so "no issues" is a recorded answer rather than an omission. |
authored |
When the brief was written, and by what | A brief is a claim about the world at a moment. Without its date, a reader cannot tell whether its facts have since gone stale. |
sources |
Typed IDs: scoping docs, findings, intake entries | Provenance. An empty list is untraceable — no one can tell why the work exists. |
Several optional fields sit alongside them. The most consequential is exec-tier, which asserts a minimum class of model for the work and is derived from three complexity questions rather than chosen — see Model tiering. Others let a brief record why its gate is what it is, what is blocking it, what it is worth, and which downstream consumers a shared value change has to reach.
One honest note about the schema check: the validator confirms that every required field is present and well-formed, but it does not reject an unrecognised top-level key. A field name typed slightly wrong is silently ignored rather than flagged — which is why the required set is checked by presence, and why a brief's real contract is the fields listed above rather than whatever it happens to contain.
The Task section
The Task is the implementer's instructions: numbered steps, exact paths, specific actions. It is accompanied by a files: listing (exact paths — no repo exploration needed) and facts: (the 3–5 project facts required to execute, in key: value form). A brief that touches a shared value — a party, env var, config key, field meaning, wire format — enumerates every consumer under consumers: with a disposition (fixed-here / follow-up / out-of-scope).
The Verify table
Every brief carries a Verify table: a list of executable checks. Each row has a literal command and an expected exit code or output. A row without both is not a DoD item; it is a hope. Evidence over claims is the whole discipline, and it starts here.
For prose deliverables (docs, articles), Verify rows assert presence: a file exists, a section appears, a token is present. Quality is owned by the human review gate — presence gates do not prove quality, and claiming they do is the exact anti-pattern the system exists to catch.
Rules enforced by lint
- Typed IDs only. References are
stream/NN,F-NN,I-NN— never fuzzy names. - Self-contained. If executing needs knowledge from another brief, link it in
facts:or state it independs:. - Dependencies are typed and inverse. A brief in
depends:must list this brief in itsunblocks:(and vice versa). - Wave consistency. A brief at wave N must only depend on briefs at waves < N.
- Provenance required. An empty
sources:fails lint. - Gate derived from risk. A brief with
gate: modeland any riskyesfails lint.
Splitting and sizing
Briefs should be roughly equal-sized. A brief doing two distinct roles becomes two briefs. Rough test: if the Task needs more than about five steps or touches more than two subsystems, split at authoring time. Uneven briefs stall a wave and hide the real critical path.
Splitting a brief mid-execution counts as authoring, and it carries a rule that sounds petty until you have watched it go wrong: the session doing the split keeps only the piece it was actively working on, and every other piece returns to the board as an unclaimed item. Keeping the whole split set is the rabbit-hole reflex at brief granularity — invisible to the board, and never re-examined for what tier or gate the new pieces should carry.
What it looks like
A short brief in full, frontmatter first:
schema: brief-v1
brief: billing/04
title: Retire the legacy invoice serializer
wave: 1
depends: ["billing/02"]
unblocks: ["billing/07"]
effort: M
gate: human
risk: {regulatory: no, customer: yes, irreversible: no, sensitive-data: no}
issues: [412]
authored: 2026-08-13
sources: ["billing/02 (introduced the replacement)", "F-invoice-rounding-drift"]
Read the fields against each other and the record is self-checking. customer: yes is why gate reads human — a brief claiming a model gate with that answer set fails lint, so the gate cannot drift from the risk it was derived from. wave: 1 is consistent only because its one dependency sits at wave 0. The unblocks entry has to appear as a depends entry on the other side, or the graph is broken and lint says so. And sources names a finding, so a reader who wants to know why this work exists at all has somewhere to go.
The body then carries the Task, the facts an implementer needs, and the Verify table — for a change like this one, rows asserting that the old code path is gone, that the replacement is exercised, and that a neighbouring consumer of the same serializer still works.
Reading
Specifications that converge
Why a work unit written to be checked reaches agreement faster than one written to be read.
Article — publication pending
Why software teams need machine-checkable gates
What multi-agent work breaks, and which mechanism answers each failure mode.
Article — publication pending
These articles are drafted and referenced from the toolkit. Links activate on publication.