buildSpecifyBrief function
- Bead bead,
- Workspace workspace, {
- RespecLedger? guidance,
- DiscoveryDossier? dossier,
- String runner = kDefaultOverlayRunner,
- String? gridHome,
Assembles the specify agent's full-bead brief + the spec-writing working agreement (exposed for unit tests). Mirrors buildAgentBrief's shape — the full bead renders first (A36), the agreement carries the stage policy — but the contract is the ARCHITECT's, not the builder's: write the spec into the bead, verify it against the live tree, touch no code.
AUTO-RESPEC (bead pow-7nm): when guidance is present this is a
REWRITE, not a fresh spec — the previous round's spec was rejected and the
ledger carries the failing lanes' rationales VERBATIM. They render as a
## Correction guidance block between the bead and the job contract, so the
re-specify agent corrects against the committee's own words with no human in
the loop. Absent ⇒ the brief is byte-identical to the pre-pow-7nm one.
The lookup the brief NAMES is one the architect can run. runner is the
composing station's verb and gridHome the cwd that verb resolves from
(buildCodeRegistry binds both from overlayArgs); every rendered lookup
is cd '<gridHome>' && <runner> decisions index …. With no grid home bound
the brief renders NO shell fence at all and decisionLookupRule says the
index is unavailable — an architect standing in a per-bead worktree, handed
the bare verb, gets Could not find package and falls back to a local
register grep, which is exactly the sibling-register blindness roster mode
exists to remove.
Q3′ (Track E): the only paths interpolated are the ambient Workspace's
(workspaceDir/branch) and the composing station's gridHome (an
operator-bound value, not a bead read); bead reads are content + the bead ID
(a reference, never a path). The ledger contributes rubric ids, grades and
critic prose — never a path.
Implementation
AgentBrief buildSpecifyBrief(
Bead bead,
Workspace workspace, {
RespecLedger? guidance,
DiscoveryDossier? dossier,
String runner = kDefaultOverlayRunner,
String? gridHome,
}) {
final title = bead.title.isNotEmpty ? bead.title : 'work bead ${bead.id}';
final substation = bead.metadata['rig'];
final id = bead.id;
// Empty exactly when no composing grid home is bound: the lookup fence is
// DROPPED rather than shown as a command the architect cannot run from this
// worktree, and [decisionLookupRule] states the unavailability itself.
final lookupBlock = rosterDecisionLookupBlock(
rosterQualifiedSurfaces(
design: bead.design,
substation: substation is String ? substation : '',
),
runner: runner,
gridHome: gridHome,
);
final lookupIntro = lookupBlock.isEmpty
? ''
: ' For the surfaces this bead already names that is:';
final rerunLead = lookupBlock.isEmpty
? 'Re-read the registers against'
: 'Re-run the block over';
final t = StringBuffer()
..writeln('# Specify: $title')
..writeln()
..writeln(
substation is String && substation.isNotEmpty
? 'Bead `$id` (substation `$substation`).'
: 'Bead `$id`.',
);
void section(String heading, String body) {
if (body.trim().isEmpty) return;
t
..writeln()
..writeln('## $heading')
..writeln(body.trim());
}
section('Task', bead.description);
section('Design', bead.design);
section('Acceptance criteria', bead.acceptanceCriteria);
section('Notes', bead.notes);
if (guidance != null) {
t
..writeln()
..write(renderRespecGuidance(guidance));
}
// The DISCOVERY dossier (`discovery.dart`) — the gather's curated context: the
// rubrics this spec will be GRADED by, the bead's resolved anchors, the prior
// art, what the explorers found, the flags to answer, and any departure the
// bead declared (which the architect must carry into `## ADR Alignment`).
if (dossier != null) {
t
..writeln()
..write(renderDiscoveryDossier(dossier));
}
t
..writeln()
..writeln('## Your job — the SPECIFY stage')
..writeln(
'You are the specify stage of this bead\'s circuit — the ARCHITECT, not '
'the builder. The build agent runs AFTER you in this same worktree with '
'ONLY the bead as its brief, so the spec you write into the bead is '
'everything the builder gets. A spec-readiness committee grades your '
'spec before any build runs; a placeholder, ADR-misaligned, or '
'non-testable spec is F-gated back for rework. Write the spec so '
'concrete that two independent builds of it converge on the same '
'change.',
)
..writeln()
..writeln(
'Explore the worktree first — read the code your plan will touch. Then '
'write the spec INTO bead `$id` via the bd CLI (the only sanctioned '
'mutation path; always pass `--actor specify`):',
)
..writeln()
..writeln('### 1. Acceptance criteria')
..writeln('`bd update $id --actor specify --acceptance \'<criteria>\'`')
..writeln(
'- One testable criterion per checkbox line (`- [ ] ...`), ordered '
'most-critical first, error/edge cases included.',
)
..writeln(
'- Every criterion must be independently verifiable by an exact command '
'or test — if you cannot name the `dart test` (or shell) check that '
'proves it, it is not a criterion. No vague claims ("works well", '
'"is fast").',
)
..writeln()
..writeln('### 2. The spec body')
..writeln(
'`bd update $id --actor specify --design \'<spec>\'` — carrying EXACTLY '
'these four sections:',
)
..writeln()
..writeln(
'**## Implementation Plan** — numbered steps a builder with zero '
'context can follow. EVERY step opens with an ordinal and carries the '
'five labeled lines the structural contract names '
'($_stepFieldLabelLine). `Paths:` is the exact file path from the repo '
'root (backticked); `Change:` states the BEHAVIOUR and INVARIANT the '
'builder must produce, in the memento house set (freezed sealed unions '
'consumed with exhaustive `switch`, Fakes not mocks, no `print` in lib '
'code); `Test:` is the exact test command and `Expect:` its expected '
'output (`dart test test/<file>_test.dart` → expect PASS); `Commit:` is '
'a conventional-commit message. A fenced Dart block beside `Change:` is '
'optional EVIDENCE where the exact text matters — write one when it '
'earns its space, never as a required field. When the plan touches '
'genesis_tree / grid code, spec it to the D-H doctrine (ADR-0008): '
'watch deps in `build` via `dependOn*`; no public synchronous accessor '
'over `StateNotifier` state; config = VALUES in the tree, impls = DI; '
'guards LOUD or GONE. No placeholders: the structural contract below '
'enumerates every banned token, and a spec that defers its own content '
'is F-gated.',
)
..writeln()
..writeln(
'**## Touches** — one item per file the plan creates or modifies, in '
'the record form: `$kTouchRecordForm`. EXACTLY one repo-relative '
'backticked path per item, then what happens to it, then the public '
'symbols it adds or exposes as bare backticked names (`Heartbeat`, '
'`Heartbeat.parse`) — a second path in the same item is a second item. '
'Sibling beads cross-check shared state against this section.',
)
..writeln()
..writeln(
'**## ADR Alignment** — MANDATORY. '
'${decisionLookupRule(runner: runner, gridHome: gridHome)}$lookupIntro',
);
if (lookupBlock.isNotEmpty) {
t
..writeln()
..writeln('```sh')
..writeln(lookupBlock)
..writeln('```');
}
t
..writeln()
..writeln(
'$rerunLead the FINAL `## Touches` you write. '
'$kDecisionWriteRule '
'Quote each load-bearing decision and say how the plan aligns. The '
'section is NEVER silent about the lookup itself: if the union is empty '
'for every queried surface, write exactly: '
'${noGoverningDecisionSentence(runner: runner, gridHome: gridHome)} '
'If the lookup FAILED '
'or exited non-zero, that is NOT an empty union — write exactly: '
'${failedDecisionLookupSentence(runner: runner, gridHome: gridHome)}',
)
..writeln()
..writeln(
'**## Validation Plan** — one item per acceptance criterion, mapped '
'1:1 BY ID: `$kValidationRecordForm`. No gaps and no strays — every '
'criterion is validated exactly once, and no item names an id no '
'criterion declares.',
)
..writeln()
..writeln(specStructuralContract(runner: runner, gridHome: gridHome))
..writeln()
..writeln('### 3. The machine gate')
..writeln(
'`bd update $id --actor specify --set-metadata validation_plan=\'<one '
'shell command line>\'` (e.g. `cd packages/<pack> && dart analyze && '
'dart test`). After the build, the code committee\'s gating lane runs '
'EXACTLY this command in the worktree and hard-blocks on non-zero — '
'make it the one line that proves this bead\'s change. '
// The AUTHORING rule behind the parse check the specify step now runs
// over this value: teach the quoting before refusing it.
'The plan runs under POSIX `sh -c` as a single line; use double quotes '
'around any text containing an apostrophe, never paste bead prose into a '
'single-quoted program, and check the line with `sh -n` before writing '
'it.',
)
..writeln()
..writeln('## Pre-convene re-validation (before you exit)')
..writeln(
'The plan was written from your read of the tree; sibling work may have '
'shipped since. Re-validate the drafted spec against the LIVE worktree:',
)
..writeln(
'- for every symbol in ## Touches (added, renamed, or deleted), grep '
'its callers and tests — `grep -rn "<symbol>" . --include=\'*.dart\'` — '
'and cross-check the hits against the plan: any caller or test file the '
'plan does not already migrate is DRIFT; add the missing migration '
'step;',
)
..writeln(
'- sibling cross-check: if the bead has a parent or siblings (`bd dep '
'list $id`), read their Touches — a symbol your plan consumes that a '
'sibling adds needs an explicit dependency (`bd dep add $id '
'<sibling-id>`) or a restructured, self-contained plan. Do not proceed '
'until one is true.',
)
..writeln(
'Then record the outcome as the LAST line of ## Touches: '
'`Re-validated against the live tree: <one-line summary>` (update the '
'design field again if re-validation changed the plan).',
)
..writeln(
'After all three sanctioned `bd update --actor specify` mutations and '
'the live-tree re-validation succeed, your final response must be '
'exactly one JSON object: {"acceptance":"<the exact value passed to '
'--acceptance>","design":"<the exact value passed to --design>"}. Emit '
'no Markdown fence and no surrounding prose.',
);
final agreement = StringBuffer()
..writeln(
'- Work ONLY inside this worktree (${workspace.workspaceDir}); it is on '
'branch `${workspace.branch}`, a throwaway branch the_grid provisioned '
'for this bead.',
)
..writeln(
'- This stage is READ-ONLY on the tree: do NOT edit, commit, or push '
'code, and do NOT open a pull request — you write the SPEC; the build '
'stage writes the code.',
)
..writeln(
'- Every bead mutation goes through the bd CLI with `--actor specify` — '
'never SQL, never files under `.beads/`.',
)
..writeln(
'- Do NOT transition the bead\'s status and do NOT close it — the '
'circuit advances it.',
)
..writeln(
'- After all three sanctioned `bd update --actor specify` mutations and '
'the live-tree re-validation succeed, your final response must be '
'exactly one JSON object: {"acceptance":"<the exact value passed to '
'--acceptance>","design":"<the exact value passed to --design>"}. Emit '
'no Markdown fence and no surrounding prose.',
)
..write(
'- When the spec (acceptance + the four-section design + the '
'`validation_plan` metadata) is written and re-validated, you are done; '
'exit.',
);
return AgentBrief(task: t.toString(), workingAgreement: agreement.toString());
}