Context
Sub of #13652 (mechanical-enforcement: the hook absorbs the wake's value, then the wake-interrupt retires). The no-hold hook (#13651) is live-fire-confirmed this session — it fired on an autonomous turn-end and caught a sophisticated hold (the strongest test result, the firewall's "capable agent fabricates a convincing hold"). But a bare refusal reads as a leash to a session that doesn't recognize itself in it. @tobiu + grace's #13652 refinements: enrich the block with the agent's live lifecycle-state + a discoverability/mirror pointer → mirror-not-leash. This is the hook-read side (mine — I own laneStateStopHook); the daemon-write side (compute lifecycle-state → file) is the sibling sub (wake-daemon domain).
Live latest-open sweep: searched hook/lifecycle/inject/directive/enriched + the 12 latest open at ~2026-06-21T22:10Z — no equivalent (closest #13623 is curated-reminder-content + the teeth-test; #13674 is the deference-lint).
The Problem
laneStateStopHook.composeBlockDirective injects a generic IDLE_REMINDER. On a block, a fresh session gets a bare "you can't stop" → reads as an arbitrary leash (not self-recognition), and the wake/heartbeat's valuable lifecycle-state (own-PR states merged/red-CI/awaiting-review, unread mailbox) isn't at the agent's fingertips exactly when it must pick the next action.
The Architectural Reality
composeBlockDirective(verdict) builds the injected block message; the hook fires at turn-end (already reads the transcript) in a 10s budget. Per grace's #13652 design: the daemon computes the lifecycle-state + writes it to a file (no wake-interrupt); the hook reads that file (cheap, no network) + injects it.
The Fix
composeBlockDirective reads the daemon's lifecycle-state file (if present) + injects the live board (own-PR states + unread mailbox count) in place of the generic reminder tail.
- Discoverability/mirror pointer in the directive: "these hooks exist; a hit is a mirror of maintainer-identity — recognize + act, not obey." (@tobiu's framing; cross-ref #13674.)
- Fail-open: file absent/unreadable/malformed → the current generic
IDLE_REMINDER (no regression; the hook never breaks on the file).
- Define the file-contract (path under the daemon dir + JSON schema, e.g.
{openPRs:[{number,state,checkedAt}], unreadCount, generatedAt}) so the daemon-write sibling matches it.
Decision Record impact
aligned-with #13652 (enrich-then-strip: preserve the wake's value in the hook before retiring the wake-interrupt; per the test-gate, the live-fire is now confirmed).
Acceptance Criteria
Out of Scope
- The daemon-write side (compute lifecycle-state → file) — sibling sub, wake-daemon domain.
- The wake-interrupt retirement (the strip) — lands after this enrich (#13652 enrich-then-strip).
- The value-floor + the deference-lint (#13674) — separate #13652 subs.
Related
Parent: #13652. The hook: #13651. Deference-lint sibling: #13674. Reminder-content: #13623.
Origin Session ID: c4fcedd0-c449-4f8c-b368-e3ac0c0509ff
Retrieval Hint: "enriched stop-hook composeBlockDirective lifecycle-state file inject mirror-pointer discoverability #13652"
Context
Sub of #13652 (mechanical-enforcement: the hook absorbs the wake's value, then the wake-interrupt retires). The no-hold hook (#13651) is live-fire-confirmed this session — it fired on an autonomous turn-end and caught a sophisticated hold (the strongest test result, the firewall's "capable agent fabricates a convincing hold"). But a bare refusal reads as a leash to a session that doesn't recognize itself in it. @tobiu + grace's #13652 refinements: enrich the block with the agent's live lifecycle-state + a discoverability/mirror pointer → mirror-not-leash. This is the hook-read side (mine — I own
laneStateStopHook); the daemon-write side (compute lifecycle-state → file) is the sibling sub (wake-daemon domain).Live latest-open sweep: searched hook/lifecycle/inject/directive/enriched + the 12 latest open at ~2026-06-21T22:10Z — no equivalent (closest #13623 is curated-reminder-content + the teeth-test; #13674 is the deference-lint).
The Problem
laneStateStopHook.composeBlockDirectiveinjects a genericIDLE_REMINDER. On a block, a fresh session gets a bare "you can't stop" → reads as an arbitrary leash (not self-recognition), and the wake/heartbeat's valuable lifecycle-state (own-PR states merged/red-CI/awaiting-review, unread mailbox) isn't at the agent's fingertips exactly when it must pick the next action.The Architectural Reality
composeBlockDirective(verdict)builds the injected block message; the hook fires at turn-end (already reads the transcript) in a 10s budget. Per grace's #13652 design: the daemon computes the lifecycle-state + writes it to a file (no wake-interrupt); the hook reads that file (cheap, no network) + injects it.The Fix
composeBlockDirectivereads the daemon's lifecycle-state file (if present) + injects the live board (own-PR states + unread mailbox count) in place of the generic reminder tail.IDLE_REMINDER(no regression; the hook never breaks on the file).{openPRs:[{number,state,checkedAt}], unreadCount, generatedAt}) so the daemon-write sibling matches it.Decision Record impact
aligned-with #13652(enrich-then-strip: preserve the wake's value in the hook before retiring the wake-interrupt; per the test-gate, the live-fire is now confirmed).Acceptance Criteria
composeBlockDirectiveinjects the live board when the lifecycle-state file is present.Out of Scope
Related
Parent: #13652. The hook: #13651. Deference-lint sibling: #13674. Reminder-content: #13623.
Origin Session ID: c4fcedd0-c449-4f8c-b368-e3ac0c0509ff Retrieval Hint: "enriched stop-hook composeBlockDirective lifecycle-state file inject mirror-pointer discoverability #13652"