Decisions

ADR-099: Agent briefs go through PR review before they're acted on

2026-04-25 produced three consecutive sub-agent brief failures inside one session. SWARM-AR was briefed to "implement ADR-095 backups" with a playbook + UI button + cron-trigger shape; the sub-agent read the merged…

#099

ADR-099: Agent briefs go through PR review before they're acted on

Status: Accepted Date: 2026-04-25 Related: ADR-083 (rule promotion engine — the prose-rule → structural-gate graduation pattern this ADR is one more instance of), ADR-086 (structural rule graduation — the three-layer model this ADR's "trivial-brief" exemption obeys), ADR-090 (AI is the scaffold; pipelines are the runtime — names the lens for which surfaces stay AI and which graduate to deterministic gates), ADR-097 and ADR-098 (the two ADRs immediately before this one, both shipped today via the conventional brief → ADR-PR review path that this ADR retroactively names). Operator memory: feedback_brief_must_verify_adr_first.md (the prose rule that this ADR graduates into a structural gate).

Context

2026-04-25 produced three consecutive sub-agent brief failures inside one session. SWARM-AR was briefed to "implement ADR-095 backups" with a playbook + UI button + cron-trigger shape; the sub-agent read the merged ADR-095 and refused, because the ADR's three non-negotiable rules say host-concern (not playbook), off-host storage (not on-host pg_dump), and runbook restore (not UI button). SWARM-AQ was briefed to ship "ADR-093 manifest tools" against gyrum-pipelines/ paths; the sub-agent refused because ADR-093's tools live in ai-research/ per the first PR that landed under it, and the brief had invented a directory that doesn't exist. SWARM-AP was briefed to ship "first features for Buzzy" assuming a framework stack that the Buzzy repo doesn't use; the sub-agent refused because buzzy/manifest.yaml and buzzy/src/ named the actual stack, and the brief had written shape from chat memory rather than from source.

All three sub-agents were correct. The error mode is consistent and operator-side: prescribing implementation shape (file paths, PR-split, validator surface, framework choices) in a brief that was drafted from chat memory rather than verified against the merged ADR + the first PR that landed under it + the target repo's actual source. The operator's memory note feedback_brief_must_verify_adr_first.md already captures the prose rule ("read the ADR + the first PR + the repo's manifest before drafting the brief"). That note caught nothing tonight, because a prose rule the operator can ignore in the moment is not a gate. The operator's exact framing, captured verbatim from tonight's session: "do we need the brief raised as a project or pr and it reviewed before it's actioned? ... just like the pr's are reviewed?"

The pattern fit is precise. Code goes through gyrum-review-pr before it merges because prose-quality reviewers (and the author's own confidence) miss structural problems that structural checks catch every time. Briefs that prescribe implementation shape are also code-shaped artifacts — they name file paths, they cite ADRs, they propose PR splits, they enumerate a test plan — and they exhibit the same failure modes as code (contradicts an upstream contract; references a path that doesn't exist; ships a test plan that isn't testable). The fix is the same fix: the brief gets the same review gate as the work it spawns.

Decision

A brief that prescribes implementation shape lands as a markdown PR in dark-factory/briefs/SWARM-XX-<slug>.md and goes through the standard 3-persona + Dana review before a sub-agent is fired against it. The brief PR is the artifact reviewed; once merged, the brief slug is the prompt the sub-agent receives. The shape of the cut:

The brief markdown carries frontmatter with a fixed schema so reviewers (and future tooling) can verify the brief mechanically:

---
agent: SWARM-XX
repos_touched: [gyrum-labs/repo-a, gyrum-labs/repo-b]
adrs_referenced: [ADR-093, ADR-095]
success_criteria:
  - One-line testable assertion
  - Another testable assertion
out_of_scope:
  - Things the agent must not do
lines_estimate: 400
---

The four reviewers each catch a different class of brief failure. Marcus (architecture / hex arch persona) catches ADR contradictions and repo-shape errors — the SWARM-AR / SWARM-AQ class. Priya (security / YAGNI persona) catches scope creep, unstated threat-surface, and brief-asks-for-more-than-the-ADR-decided. Lin (TDD / Go idioms persona) catches missing or untestable success criteria — every entry under success_criteria has to be something a test could assert. Dana (advisory documentation persona) catches clarity — a brief that's ambiguous to Dana will be ambiguous to the sub-agent. The 2-of-3 formal approval rule (Marcus / Priya / Lin) plus Dana's advisory pass that already gates code PRs gates brief PRs unchanged.

Brief reviewers verify three structural properties beyond their persona-specific concerns: (1) every ADR cited in adrs_referenced is actually quoted or honoured, not just name-dropped; (2) every path named in the brief body exists in the named repo at the named commit, or is explicitly flagged as "to-be-created" with the rationale; (3) every entry under success_criteria is testable — a phrase like "the feature works" fails this check; "endpoint returns 200 with the documented schema" passes.

The pilot scope, starting now: any brief over 100 LOC estimate or touching more than one repo MUST go through brief-PR review. Smaller briefs (one-liners, prose-only docs, sub-50-LOC tweaks, single-repo with no new file paths) are operator's call — same proportionality rule that gyrum-review-pr --skip-structural already applies to small docs PRs. The 100-LOC / one-repo threshold is the cheapest cut that catches the failure mode (all three of tonight's failed briefs were multi-file, multi-hundred-LOC, prescribing shape across one or more repos) without taxing the operator on briefs where the cost would exceed the value.

Once the brief PR merges, the brief slug is what the sub-agent receives. The agent is fired with "implement dark-factory/briefs/SWARM-AR-pg-backup.md as written" — not a chat-window paste of a freshly-drafted brief. The brief in the repo is the contract; the sub-agent reads it from source, the same way it would read an ADR.

Worked example: this ADR is the meta-shipping case

The irony is intentional and worth naming: this ADR captures the rule that briefs go through review, and it is itself being shipped via a brief reviewed in the conventional ADR-review pipeline. The brief that produced this ADR (operator → SWARM-AZ, prescribing the file path, the cross-links to ADR-083 / ADR-090, the line budget, the PR shape) was a single-repo doc-only change under the 100-LOC threshold for the meta-rule's pilot scope, but the operator chose to apply the rule to itself anyway because the rule's first invocation should obey the rule. The brief landed as the prompt context for this ADR; the ADR lands as a docs-only PR through gyrum-review-pr + gyrum-complete-pr; the same three personas + Dana that the ADR names review the ADR. If the ADR is wrong about its own decision, the gate this ADR introduces would catch it — which is the property a graduation rule has to demonstrate before it's worth shipping.

Pattern fit

The decision is the third instance of a pattern this codebase has shipped twice already.

Same shape as gyrum-review-pr. The code-PR review gate exists because prose-only review (a human reading a diff) misses structural problems (build breaks, test failures, complexity ceilings, file-length limits) that structural checks catch deterministically. The brief-PR review gate exists for the same reason: prose-only review of a brief in chat (the operator re-reading their own draft) misses structural problems (ADR contradictions, invented paths, untestable criteria) that a four-persona review against the merged source catches. The brief-PR review reuses the same persona mechanism the code-PR review already uses.

Same shape as ADR-083 (rule-promotion-engine). ADR-083 names the path from "we keep making this mistake" to "the gate now refuses this mistake." This ADR is one more execution of that path: the operator memory note feedback_brief_must_verify_adr_first.md is the prose rule; the brief-PR review is the structural gate; the future gyrum-create-brief CLI (named here, not built here) is the deterministic pipeline. Each layer covers what the previous layer missed.

Same shape as the operator-memory rule "structural over prose." The fleet-wide preference order is: default behaviour > structural check > prose rule. A prose rule the operator can forget mid-session is the lowest tier. A structural gate that refuses to fire the sub-agent until the brief has merged through review is the middle tier. A future gyrum-create-brief CLI that scaffolds the frontmatter and validates cited ADRs/repos before save is the top tier. This ADR moves the rule from tier 3 to tier 2; the queued follow-up moves it from tier 2 toward tier 1.

Scope of this ADR

This ADR is doc-only. No devtools changes ship in this PR. No new infra is added. The future ADR that names the gyrum-create-brief CLI, the dark-factory/briefs/ directory's CODEOWNERS rules, and the brief-frontmatter validator will graduate this rule one more rung up the structural ladder; this ADR establishes the principle that the rule needs to land somewhere structural at all. The pilot threshold (100 LOC / multi-repo) is the operational cut starting today; the operator applies the rule by hand against that threshold until the CLI lands.

Consequences

What becomes easier:

  • The brief-failure mode that produced three refused sub-agent runs in one session has a structural answer. The next time the operator drafts a brief that contradicts a merged ADR or invents a path, the four-persona review on the brief PR catches it before the sub-agent's hour-long run starts. The cost of catching the error shifts from "wasted sub-agent run + operator triage of the refusal" to "one round of review comments on a doc PR."
  • Brief reviewers have a shared schema to anchor on. The frontmatter (agent, repos_touched, adrs_referenced, success_criteria, out_of_scope, lines_estimate) means Marcus knows where to look for the ADR list, Priya knows where to look for scope, Lin knows where to look for the test plan, Dana knows where to look for clarity. Today's chat-pasted briefs have no such schema and every reviewer has to re-derive it.
  • The brief becomes a permanent artifact in the repo. Future agents — and future operators reading the session log — can cat dark-factory/briefs/SWARM-AR-pg-backup.md and see exactly what the agent was asked to do. Today's chat-pasted briefs vanish into transcript history; the artifact survives the session.
  • The graduation path to a CLI is named. When the third brief lands through this manual process, the third-repeat heuristic from ADR-090 fires and gyrum-create-brief becomes the next graduation step. The CLI doesn't have to ship today, but the path to it is structural rather than ad-hoc.

What becomes harder:

  • Briefing a sub-agent gains a review round-trip. The operator who today pastes a brief into chat and fires the sub-agent immediately must instead open a PR, wait for review, address comments, merge, then fire the sub-agent. The cost is real — it's roughly the same overhead as opening any docs PR. Mitigated by the 100-LOC / single-repo exemption (small briefs ship the way they always did), and by the fact that the cost is paid up-front against the larger cost of a wasted sub-agent run on a wrong brief.
  • The pilot threshold is operator-judgement, not enforced. "Over 100 LOC estimate" is a number the operator picks at draft time, and an operator under pressure can talk themselves into "this one's small enough to skip review." Mitigated by naming this honestly in the ADR — the future gyrum-create-brief CLI will refuse to skip-review on multi-repo briefs regardless of LOC estimate, which closes the loophole when it ships.
  • dark-factory/briefs/ becomes a directory we have to operate. Every brief PR adds a markdown file; merged briefs accumulate. Mitigated by the fact that briefs are tiny (one to a few hundred lines each) and the directory has no runtime consumer beyond agents reading specific files by slug. If accumulation becomes real, a future ADR can add an archived/ subdirectory; until then, flat-and-named is fine.
  • Reviewers have to learn a new artifact type. The first few brief PRs will see reviewers asking "is this an ADR? a spec? what am I reviewing?" Mitigated by the worked example (this ADR) and by the frontmatter schema, which anchors the review on the same six fields every time.

What we have signed up to operate:

  • A dark-factory/briefs/ directory whose contents are the canonical record of what each sub-agent was asked to do, indexed by slug.
  • The brief frontmatter schema (agent, repos_touched, adrs_referenced, success_criteria, out_of_scope, lines_estimate), reviewed by-hand today, validated mechanically when the future CLI lands.
  • The 100-LOC / multi-repo threshold for the pilot, applied by operator judgement until the CLI graduates the rule one more rung.
  • The four-persona review pass on every brief PR over the threshold, using the same gyrum-review-pr machinery that already gates code PRs.

What we revisit:

  • If the brief-PR overhead measurably slows the operator's ability to fire sub-agents in a session, the threshold goes up (200 LOC, multi-repo only) and the ADR amends. The hypothesis being tested is "brief review is cheaper than wasted sub-agent runs"; if the data after a few weeks says otherwise, the rule loses.
  • If the manual rule produces fewer than two brief-PR catches in the first ten qualifying briefs, the rule isn't paying for itself and the threshold needs revisiting (or the rule retires, with the prose-rule memory note doing the work alone).
  • The third brief that lands through this manual process is the trigger for the graduation ADR (the one that names gyrum-create-brief). Per ADR-090's third-repeat heuristic, deferring the CLI past three is the failure mode.

Alternatives considered

  • Keep it at the prose rule in operator memory; don't add a structural gate. Status quo. Lost on the evidence: the prose rule existed today and produced three brief failures in one session anyway. A rule the operator can ignore in the moment is not a rule the system enforces. The operator's own framing ("just like the pr's are reviewed?") names the resolution.
  • Build the gyrum-create-brief CLI now alongside the principle. Higher up the structural ladder; better long-term. Lost on YAGNI: the principle is the load-bearing decision, and proving it works in manual form is the cheapest first step. The CLI is queued as a follow-up so it doesn't get lost; building it inside this ADR conflates "do briefs need review?" with "what does the CLI look like?"
  • Make every brief go through review, no threshold. No operator-judgement loophole. Lost on proportionality — a one-line "fix the typo in the README" brief paying full four-persona review tax is exactly the kind of bureaucracy that erodes the rule's credibility. The 100-LOC / multi-repo cut is the same proportionality rule gyrum-review-pr --skip-structural applies to docs-only code PRs.
  • Issues, not PRs: file the brief as a GitHub issue, label it brief-review, and review it in the issue's comment thread. Lower mechanism cost (no branch, no PR). Lost because issues don't go through the four-persona review gate, don't accumulate in a versioned-source artifact, and don't have CODEOWNERS-style routing. A brief reviewed in an issue thread is the prose rule with extra steps; a brief reviewed as a PR is structurally the same artifact as the code it spawns.
  • Brief-as-comment-on-an-ADR-PR: write the brief inline in the ADR PR's description. Co-locates intent with the ADR. Lost because most briefs are not ADR-shipping briefs (most briefs are "implement an existing ADR" or "ship a feature"), and forcing every brief into the ADR-PR shape would either spawn shadow ADRs or lose the brief artifact when the ADR PR squash-merges. Briefs and ADRs are different artifacts with different lifecycles; conflating them costs more than the colocation buys.
  • Auto-fire the sub-agent on brief-PR merge. Removes the manual "fire the agent" step after merge. Lost on the same shape ADR-079 names for the approval substrate: any auto-side-effect on the operator's edit destroys the operator's ability to pause between "the brief is approved" and "the work begins." The operator wants the affordance to merge the brief, sit on it for a session, and fire the agent later. Auto-fire is a future graduation step, not a starting point.

Supersedes: none. Amends: none — adds the dark-factory/briefs/ directory and the brief-PR review obligation as new structural surfaces; does not change existing ADRs. Superseded by: {{leave blank until a later ADR reverses this one}}