Skip to content

fix: claude signal drain act-then-delete parity - multi-instance safety + signal-independent first-bind - #578

Merged
danshapiro merged 10 commits into
mainfrom
fix/claude-signal-drain-parity
Jul 29, 2026
Merged

fix: claude signal drain act-then-delete parity - multi-instance safety + signal-independent first-bind#578
danshapiro merged 10 commits into
mainfrom
fix/claude-signal-drain-parity

Conversation

@danshapiro

Copy link
Copy Markdown
Owner

Root Cause

The claude signal-file drain was destructive: it deleted signal files on read without checking whether they belonged to the current instance. In multi-instance scenarios (dev servers on different ports, test/drill runs, or agents testing in worktrees), a second freshell instance sharing $HOME silently destroyed claude rebind signals from a first instance.

Proven in live drill: production 3002 server swept the shared signal dir every second and consumed all 7 of the drill's claude signals, leaving those panes with sessionRef: None.

The Fix

Port the proven opencode signal-drain pattern to claude:

  • Act-then-Delete: read disposition (Acted/Retain/Discard) before deleting
  • Multi-instance retention: signals for unknown terminals retained (~10min TTL)
  • Explicit discard policy: only claude signals deleted; opencode/other signals retained
  • Foreign-provider cleanup: stale .tmp files cleaned up with warn-once logging
  • Signal-independent first-bind guarantee: fresh claude panes bind from preallocated --session-id even if signal dir is destroyed

Tests

  • Multi-instance retention: second instance preserves signals from first
  • Signal-independent first-bind: fresh claude pane binds from prealloc even with all signals destroyed
  • Foreign-provider discard: opencode signals retained, stale .tmp cleaned
  • Integration: parity with opencode signal lifecycle

Reviews

Plan review: passed round 1, zero blockers
Delta review: passed round 1, zero blockers

danshapiro and others added 10 commits July 29, 2026 09:36
…drain-parity plan

All 23 validated assumptions confirmed (0 falsified). Precision fixes from
validator evidence: correct module-header anchor (:1-14), Phase 4 wording
lives in a comment not an assert message, make the npm-ci precondition for
freshell-ws integration tests explicit with its failure signature (MCP
inject resolves node_modules/tsx at pane create), note the deliberate new
warn from splitting the retired/foreign-provider guard, and allow the
harness's pid-scoped temp roots alongside tempfile::tempdir().
…minals survive drains, TTL-reaped)

Phase 5 appended to claude_session_rebind: a SessionStart signal naming
a terminal id unknown to this instance (another freshell server sharing
$HOME owns that pane) must be retained across two drain cycles, never
emit a terminal.session.associated frame, and be reaped only after the
staleness TTL (mtime backdated past STALE_SIGNAL_MAX_AGE = 600s).
Mirrors opencode's Phase 9 plus a TTL-reap tail it lacks.

Red-proofed against the pre-parity drain (1d7449a~1): Phase 5 fails at
the first retention assertion because the destructive drain ate the
foreign signal. Green at HEAD.

🤖 Generated with [Amplifier](https://github.com/microsoft/amplifier)

Co-Authored-By: Amplifier <[email protected]>
…d (review finding)

The Discard arms of apply_claude_signal had zero direct test coverage:
if a Discard regressed to Retain, a foreign-provider signal would
warn-log every 1s sweep for 10 minutes and no test would catch it.

Port the opencode lane's foreign-provider phase as Phase 7: a claude
signal naming a shell-mode pane (valid unbound UUID session id) must be
CONSUMED, not retained, and must never emit a terminal.session.associated
frame. Red-direction verified by temporarily inverting the file-consumed
assertion and observing the failure.

🤖 Generated with [Amplifier](https://github.com/microsoft/amplifier)

Co-Authored-By: Amplifier <[email protected]>
@danshapiro
danshapiro merged commit ab9c338 into main Jul 29, 2026
3 checks passed
@danshapiro
danshapiro deleted the fix/claude-signal-drain-parity branch July 29, 2026 20:43
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant