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.

Table The required frontmatter fields
FieldWhat it holdsWhy 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 in depends:.
  • Dependencies are typed and inverse. A brief in depends: must list this brief in its unblocks: (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: model and any risk yes fails 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.