Implements the tmux detector poller module based on the design in
docs/notes/tmux-detector-design.md.
Features:
- Auto-discovers opted-in panes via @tb- prefix in window title
- Polls every 2s (configurable via TRAILBOSS_POLL_INTERVAL_MS)
- Detects stuck sessions: quiet for 30s with prompt-like last line
- Detects unstuck: any output change after being stuck
- Emits normalized events to /event/normalized endpoint
- Synthetic session IDs: tmux-%446-{timestamp}
- Graceful shutdown with SIGINT/SIGTERM handling
Configuration:
- TRAILBOSS_POLL_INTERVAL_MS (default: 2000)
- TRAILBOSS_QUIET_THRESHOLD_MS (default: 30000)
- TRAILBOSS_OPT_IN_PREFIX (default: @tb-)
- TRAILBOSS_DAEMON_URL (default: http://127.0.0.1:4000/event/normalized)
Usage: bun run daemon/tmux-detector.ts
Co-Authored-By: Claude <noreply@anthropic.com>
- Remove CORS headers and OPTIONS preflight from daemon
- CORS was enabling cross-origin browser POSTs to loopback endpoint
- No legitimate consumer is a browser (emitter uses curl, TUI uses Go http)
Add invariant test script test-invariants.sh:
- Daemon listens on 127.0.0.1 only (loopback-only binding)
- No synthesized input: verify no tmux send-keys in daemon sources
- OPTIONS preflight is no longer honored (returns 404)
Validates trust boundary specified in docs/plan/plan.md section
'Failure modes & invariants'.
Co-Authored-By: Claude <noreply@anthropic.com>
- Add reconcileStuckDirection() to scan transcripts and recover sessions that became stuck while daemon was down
- Add paneExists() to verify tmux pane still exists before enqueuing
- Add detectStuckFromTranscriptTail() to detect stuck state from transcript tail
- Add getSessionsNotInQueue() to DB layer for finding non-queued sessions
- Call reconcileStuckDirection() on daemon startup to recover dropped events
- Remove duplicate AS-4 test code
Acceptance criteria:
- Extended test-walking-skeleton.sh AS-4 to full plan scenario (daemon restart with transcript tail scan)
- All AS-1..AS-7 tests pass
- Session appears in /queue within one sweep after daemon restart
Co-Authored-By: Claude <noreply@anthropic.com>
- Parse ISO-8601 timestamps via Date.parse(), handle both strings and numbers
- Recognize real entry shape: type='user'|'assistant' with nested message.role
- Ignore non-message lines (attachments, queue-operations, etc.)
- Remove duplicate TranscriptEntry interface (import was not exported)
- Add AS-8 test validating real-format JSONL behavior
- Update fixtures to use real Claude Code format
Fixes tb-67q
daemon/ (index.ts, types.ts, claude-adapter.ts, db.ts, reconcile.ts, schema.sql),
package.json, and test scripts were implemented but never staged. Phase 3 exit
criteria verified per PROGRESS.md.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>