Summary
The durable-log-gate Stop hook — the forcing function that is supposed to catch "a review intended a durable log but never wrote it" — has a structural blind spot: it cannot fire in exactly the scenario where the log write is skipped by the orchestrator ending its turn early (e.g. under claude -p). The guard and the thing it guards are armed by the same step, so if that step is skipped, both are.
Mechanism
hooks/durable-log-gate.sh blocks turn-end when a session-scoped breadcrumb is armed but the expected log file is absent. But the breadcrumb it keys on —
/tmp/claude-<session_id>/durable-log-expected.json — is written by Step 3.6 itself (SKILL.md ~1182), i.e. after the review-core Workflow returns.
So the ordering is:
Workflow returns → Step 3.6: (a) arm breadcrumb (b) write durable log
If the turn ends before the Workflow returns (the -p teardown described in the sibling issue), Step 3.6 never runs → breadcrumb never armed → gate reads "no breadcrumb → inert (the common case)" and exits 0. The forcing function designed to catch the missing write is disarmed by the very same skipped step that causes the missing write.
Why it matters
The gate gives a false sense of coverage: "if a log was meant to be written and wasn't, the Stop hook will catch it and re-drive Step 3.6." That guarantee holds only when Step 3.6 started (armed the breadcrumb) and then the write failed — not when Step 3.6 never ran at all.
Impact
Low in practice today — full_log is default-OFF and only the A/B harness enables it, and the harness is moving to harvest the bundle from the Workflow journal directly rather than depend on Step 3.6. Recorded because it is a non-obvious gap in a mechanism whose whole purpose is "make the durable log reliable", and it would resurface if durable logging is ever made default-on or relied upon by a headless caller.
Possible fix
Arm the breadcrumb before the Workflow dispatch (Step 3.5), keyed on full_log being resolved-on, rather than inside Step 3.6. Then a turn that dies mid-Workflow still leaves an armed breadcrumb, and the gate can block turn-end / re-drive. (Needs care: the breadcrumb TTL + session-scoping logic already guards against stranded markers, so arming earlier is mostly a reordering.)
Provenance
Surfaced 2026-07-12 during panel-vs-classic A/B arm-tell capture (spec #3, Follow-up B), while diagnosing why both arms produced HARVEST_MISS. Sibling issue: review-gh-pr not -p-safe past the Workflow dispatch.
Summary
The
durable-log-gateStop hook — the forcing function that is supposed to catch "a review intended a durable log but never wrote it" — has a structural blind spot: it cannot fire in exactly the scenario where the log write is skipped by the orchestrator ending its turn early (e.g. underclaude -p). The guard and the thing it guards are armed by the same step, so if that step is skipped, both are.Mechanism
hooks/durable-log-gate.shblocks turn-end when a session-scoped breadcrumb is armed but the expected log file is absent. But the breadcrumb it keys on —/tmp/claude-<session_id>/durable-log-expected.json— is written by Step 3.6 itself (SKILL.md ~1182), i.e. after the review-core Workflow returns.So the ordering is:
If the turn ends before the Workflow returns (the
-pteardown described in the sibling issue), Step 3.6 never runs → breadcrumb never armed → gate reads "no breadcrumb → inert (the common case)" and exits 0. The forcing function designed to catch the missing write is disarmed by the very same skipped step that causes the missing write.Why it matters
The gate gives a false sense of coverage: "if a log was meant to be written and wasn't, the Stop hook will catch it and re-drive Step 3.6." That guarantee holds only when Step 3.6 started (armed the breadcrumb) and then the write failed — not when Step 3.6 never ran at all.
Impact
Low in practice today —
full_logis default-OFF and only the A/B harness enables it, and the harness is moving to harvest the bundle from the Workflow journal directly rather than depend on Step 3.6. Recorded because it is a non-obvious gap in a mechanism whose whole purpose is "make the durable log reliable", and it would resurface if durable logging is ever made default-on or relied upon by a headless caller.Possible fix
Arm the breadcrumb before the Workflow dispatch (Step 3.5), keyed on
full_logbeing resolved-on, rather than inside Step 3.6. Then a turn that dies mid-Workflow still leaves an armed breadcrumb, and the gate can block turn-end / re-drive. (Needs care: the breadcrumb TTL + session-scoping logic already guards against stranded markers, so arming earlier is mostly a reordering.)Provenance
Surfaced 2026-07-12 during panel-vs-classic A/B arm-tell capture (spec #3, Follow-up B), while diagnosing why both arms produced HARVEST_MISS. Sibling issue:
review-gh-prnot-p-safe past the Workflow dispatch.