dream synthesize (oneshot): a legitimate Task-D skip is dead-lettered by require_writes, so routine transcripts are re-synthesized every cycle
Observed on gbrain 0.50.5.0 (Postgres engine); the code path is unchanged on master at v0.51.0.0.
Summary
In dream.synthesize.mode = oneshot (the default), the synthesis prompt's Task D tells the model to skip a transcript when nothing meets the bar, and the oneshot runner accepts that answer as success. finalizeWriteAccounting then kills the same job because synthesize submits every child with require_writes: true. The two halves of the contract disagree, so every legitimate skip ends dead with:
job produced zero required put_page writes — a clean model finish does not satisfy require_writesWhere
src/core/minions/handlers/subagent-oneshot.ts — the skip is explicitly a completion:
if (parsed.pages.length === 0) {
if (!parsed.skipped) return fb('empty_no_skip');
// Task-D skip: a legitimate zero-write completion.
const skipText = `oneshot: nothing met the bar — ${parsed.skip_reason ?? 'no reason given'}`;
...
return { kind: 'done', result: { result: skipText, stop_reason: 'end_turn', synth_mode_used: 'oneshot', written_refs: [] } };
}src/core/minions/handlers/subagent.ts — the handler then applies the write requirement unconditionally:
return finalizeWriteAccounting(engine, ctx.id, stamped, {
requireWrites: dataForAccounting.require_writes === true,
...
});src/core/minions/handlers/subagent-persistence.ts — attempted === 0 throws UnrecoverableError, so the job goes straight to dead.
src/core/cycle/synthesize.ts sets require_writes: true on every synthesis child and its prompt contains Task D ("If nothing in this transcript meets the bar … return without writing anything"), so this is reachable on any brain, with any provider.
Impact
- A model answer of
{"pages": [], "skipped": true, "skip_reason": "…"}— valid JSON, valid contract — produces a dead job. On my brain that was 19 dead synthesis children (of 128 submitted) in the 48 hours after upgrading across v0.50.2.0 (where therequire_writesenforcement from #4217 landed). - Dead jobs release their idempotency key (by design, so the next cycle can retry). For a skip that means the same routine transcript is re-submitted and re-billed on every cycle, and is never recorded as done.
- The dead count trips health monitoring even though nothing is wrong, and hides real write failures among false ones.
Repro
dream.synthesize.modeunset (oneshot), any chat provider.- Run
gbrain dream --phase synthesizeon a corpus containing a transcript that passes triage but is routine (pure code review / debugging with no reflection or original idea is reliable). - The child's persisted assistant message is the Task-D skip JSON; the job row is
status = deadwith the error above;subagent_tool_executionshas no rows for it.
Suggested fix
Treat a validated oneshot skip as satisfying the requirement; keep dead-lettering every other zero-write finish (prose answers, empty-without-skip, agentic loops that never called put_page):
const legitOneshotSkip =
stamped.synth_mode_used === 'oneshot' &&
stamped.stop_reason === 'end_turn' &&
typeof stamped.result === 'string' &&
stamped.result.startsWith('oneshot: nothing met the bar');
return finalizeWriteAccounting(engine, ctx.id, stamped, {
requireWrites: dataForAccounting.require_writes === true && !legitOneshotSkip,
...
});A structural marker on the oneshot result (e.g. skipped: true) would be cleaner than matching the result text; I used the string because it needed no type change. With this applied locally: 23 synthesis children completed, 0 dead, skips recorded as completed with pages_written: 0. test/subagent-required-writes.test.ts still passes (12/12); it has no case for the oneshot skip, which is probably why this slipped through.
Related but different: #5098 (zero tool calls recorded as completed on the agentic path) and #4217 (the original write-accounting issue). This one is the opposite failure — a correct completion recorded as dead.
Source: garrytan/gbrain