feat(agents): W4 handover two-mode — synthesize opus / render sonnet, run-scoped draft handoff (BDR-077)

handover-doc-writer: MODE synthesize (model="opus" call-site — STEP
9/10/12 → .audit/handover-draft-<RUNID>.md + DRAFT COMPLETE sentinel) /
MODE render (sonnet pin — STEP 13-16 from the draft, fail-closed on
absent/mismatched RUNID). Mode-based, not a file split: the §9
name+dispatch census locks survive untouched. client-handover-writer 9.6
dispatches twice with the FULL PACKAGE both times (LRN-126) + RUNID mint
+ post-run draft cleanup. In-wave smokes PASSED disk-verified: draft
written+sentinel+gitignored; render BLOCKED on wrong RUNID (no phantom
synthesis); render consumed draft + honored skip-write, report grammar
intact. Census §17 (111 pass), make test exit 0.
This commit is contained in:
Bastien Chanot
2026-07-19 22:59:34 +02:00
parent d3d5e3802c
commit d2a10de08b
3 changed files with 67 additions and 5 deletions
+28 -4
View File
@@ -1072,11 +1072,30 @@ If `OUTPUT` resolved to `skip-write`, still dispatch — the doc-writer
reports `MD: skipped` and stops before rendering, per its own
contract.
Dispatch:
Dispatch the two-mode pipeline (BDR-077 — synthesis on opus, render on the
sonnet pin, full PACKAGE both times per LRN-126). Mint a RUNID first
(`RUNID=$(date +%s)`); the draft crosses via the run-scoped, gitignored
`.audit/handover-draft-<RUNID>.md`; clean it after 9.7.
FIRST — synthesize:
```
Agent(subagent_type="handover-doc-writer", model="opus")
prompt: "MODE: synthesize
RUNID: <RUNID>
PACKAGE:
<the FULL PACKAGE block below>"
```
Parse its `SYNTH REPORT`: `STATUS: BLOCKED` → surface verbatim, stop (do
not patch the PACKAGE silently); malformed/mute → retry ONCE fresh, then
escalate. `STATUS: DONE` → THEN render:
```
Agent(subagent_type="handover-doc-writer")
prompt: "PACKAGE:
prompt: "MODE: render
RUNID: <RUNID>
PACKAGE:
LANG: <LANG>
PROJECT: name=<name> root=<root> type=<type> sub-type=<sub-type>
is_local_business=<bool> deployed_url=<url> period=<first→last>
@@ -1092,10 +1111,15 @@ PRECHECK_DONE: <list>
CLIENT_NAME: <name|—>
OUTPUT: <overwrite <path> | versioned <path> | skip-write>
Synthesize + write + render the deliverable per your steps. Report the
HANDOVER-DOC REPORT."
Render the deliverable from the draft per your render-mode steps. Report
the HANDOVER-DOC REPORT."
```
(The PACKAGE block is IDENTICAL in both dispatches — write it once,
paste it twice. A render `STATUS: BLOCKED` on draft absence/RUNID
mismatch means the synthesize leg failed silently: re-run 9.6 from the
synthesize dispatch, never hand-write the draft.)
### 9.7 — Parse the report, tell the user
Parse the returned `HANDOVER-DOC REPORT`:
+30 -1
View File
@@ -1,6 +1,6 @@
---
name: handover-doc-writer
description: Deliverable writer — dispatched by client-handover with a resolved PACKAGE. Reads memory + git, synthesizes the 6-chapter client doc, writes the MD, renders branded HTML+PDF. No audits, no questions, no dispatch.
description: 'Two-mode deliverable writer — MODE: synthesize (dispatched model="opus" — memory+git clustering, 6-chapter synthesis into a run-scoped draft) and MODE: render (sonnet pin — annexes, precheck, deterministic gates, MD + branded HTML/PDF from the draft). Dispatched twice by client-handover with the resolved PACKAGE. No audits, no questions, no dispatch.'
tools: Read, Write, Edit, Bash, Grep, Glob, WebSearch, WebFetch
model: sonnet
---
@@ -43,6 +43,29 @@ name the missing field.
---
## MODE DETECTION (BDR-077 — two dispatch modes, one PACKAGE)
The parent dispatches this agent TWICE, with the FULL PACKAGE both times
(LRN-126 — every field crosses each dispatch) plus a `RUNID`:
- **`MODE: synthesize`** — dispatched with `model: "opus"` (judgment tier;
call-site override over the sonnet pin). Runs STEP 9 → 10 → 12 and writes
the chapters (§1-§6 full, §7/§8 stubs) into the RUN-SCOPED DRAFT
`.audit/handover-draft-<RUNID>.md`, ending the file with the line
`DRAFT COMPLETE — RUNID: <RUNID>`. Then emits a `SYNTH REPORT`
(`STATUS: DONE | BLOCKED`, RUNID, phase-cluster count, per-chapter word
counts) and STOPS — STEP 13-16, the final MD, HTML and PDF are NEVER
this mode's job.
- **`MODE: render`** — runs on the sonnet frontmatter pin. FIRST loads the
draft: absent file, RUNID mismatch, or missing `DRAFT COMPLETE` sentinel
→ `STATUS: BLOCKED` naming the cause (fail closed — never synthesize a
missing draft, never render a partial one). Then runs STEP 13 → 14 →
14.5 → 15 → 16 on the draft + PACKAGE and emits the `HANDOVER-DOC
REPORT`. `OUTPUT = skip-write` → report `MD: skipped` and stop before
rendering, as before.
---
## STEP 9 — LOAD MEMORY REGISTRIES
```bash
@@ -452,6 +475,12 @@ des audits de santé. Pour toute question, contactez [contact].*
---
> **MODE BOUNDARY.** STEP 12 is the last synthesize-mode step: write the
> drafted chapters to `.audit/handover-draft-<RUNID>.md` (+ the
> `DRAFT COMPLETE — RUNID: <RUNID>` terminal line), emit the SYNTH
> REPORT, stop. Everything below (STEP 13-16) is `MODE: render` and
> operates ON that draft.
## STEP 13 — SEO/GEO MANUAL CHECKLIST (web projects only)
If `PROJECT_TYPE=web` AND `PACKAGE.SKIP_SEO` is not `yes`, append this chapter