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>
Document the POST /event/normalized endpoint contract with:
- Complete endpoint specification (URL, method, request/response)
- All 4 event types with field descriptions and examples
- Derivation guide for sessionId, paneId, cwd, transcriptPath from harness context
- Implementation example for adapter authors
- Error handling and versioning guidance
This enables future adapter authors to integrate any coding harness
without reading the daemon source code.
- 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>
- plan.md: mark Phase 8 as complete, remove (planned) marker
- plan.md: update PermissionRequest status from unverified to confirmed
(probe 2026-05-25 confirmed firing + payload with tool_name/tool_input)
- plan.md: mark Open question 2 (PermissionRequest specifics) as resolved
- plan.md: correct Phase 8 keyboard map (l/Tab focuses detail pane, not queue head)
- plan.md: mark Open question 5 (popup vs persistent window) as resolved
(both retained: popup for quick triage, TUI for sustained work)
- README: add SessionStart and SessionEnd to hook wiring snippet
(daemon also consumes these for registry and retirement)
- README: add trailboss-bootstrap and trailboss-watch to repository layout
(trailboss-watch noted as deprecated/superseded by TUI)
- CLAUDE.md: update Go version from 1.22+ to 1.24+ to match tui/go.mod
Co-Authored-By: Claude <noreply@anthropic.com>
Bead-Id: tb-1jl
- plan.md: mark Phase 8 as complete, remove (planned) marker
- plan.md: update PermissionRequest status from unverified to confirmed
(probe 2026-05-25 confirmed firing + payload with tool_name/tool_input)
- plan.md: mark Open question 2 (PermissionRequest specifics) as resolved
- plan.md: correct Phase 8 keyboard map (l/Tab focuses detail pane, not queue head)
- plan.md: mark Open question 5 (popup vs persistent window) as resolved
(both retained: popup for quick triage, TUI for sustained work)
- README: add SessionStart and SessionEnd to hook wiring snippet
(daemon also consumes these for registry and retirement)
- README: add trailboss-bootstrap and trailboss-watch to repository layout
(trailboss-watch noted as deprecated/superseded by TUI)
- CLAUDE.md: update Go version from 1.22+ to 1.24+ to match tui/go.mod
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>
- Change 'pre-built binary' language to 'build from tui/ with Go'
- Add explicit build step in installation instructions
- Note that trailboss-start auto-builds if binary is missing
- Update predispatch SHA
Bead-Id: tb-25j
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
- Define Theme struct with all Dracula palette colors
- Auto-detect terminal color support (truecolor, 256-color, no-color)
- Implement graceful fallback for dumb terminals
- Fix model theme field type to use pointer
- Colors: stopped badge (#F1FA8C), permission badge (#FF5555), selected row (#BD93F9),
panel border (#6272A4), header bar (#282A36), status bar (#44475A),
metadata text (#AAAAAA), accent cyan (#8BE9FD), accent green (#50FA7B)
Closes tb-6cj
Rewrite README to reflect actual implementation (phases 1–9 complete,
all 7 acceptance scenarios passing). Previous README said "No
implementation yet" — replaced with accurate architecture overview,
setup instructions, TUI key reference, component table, and design
principles.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Ran test-walking-skeleton.sh successfully
- All AS-1 through AS-7 pass end-to-end
- No code changes required - implementation was already complete
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The "no implementation code exists" line was stale and would have misdirected
a fresh marathon session to re-implement from phase 1.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
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>
Implemented trailboss CLI with jump-next and skip commands that
navigate to panes returned by the daemon's /next endpoint.
- bin/trailboss: main CLI with jump-next, skip, popup stubs
- jump-next: queries /next, navigates via switch-client/select-window/select-pane
- skip: POSTs /skip, then jumps to new head
- test-navigation.sh: synthetic event test verifying exit criterion
Exit criterion passed: jump-next lands operator on pane returned by /next.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Phase 1: Probed PermissionRequest hook, confirmed firing + payload shape.
Documented in docs/research/claude-code-mechanics.md.
Phase 2: Built trailboss-emit.sh that forwards hook payloads to collector
and injects $TMUX_PANE. Wired all hooks in .claude/settings.json.
Verified: stub collector logs POSTs with session_id, cwd, and pane_id.
Demonstrated bare curl drops $TMUX_PANE, proving wrapper required.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
The launcher sourced env into its own process, but the loop runs in a separate
tmux shell that didn't inherit ANTHROPIC_BASE_URL/AUTH_TOKEN — so claude ran
unauthenticated ("Not logged in"). Source the env file inside the loop command
so the proxy/model config is present in the shell that launches claude.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The plan defined deliverables but no definition of done. Add a "Testing &
validation" section: a per-phase exit-criteria table, seven acceptance
scenarios (AS-1..AS-7, incl. reconcile self-correction, dropped-event
recovery, skip/cooldown, no-forced-focus-steal, pane reuse), a test harness
(synthetic event injection, throwaway-tmux isolation, transcript fixtures,
mock hook emitter, navigation assertion, invariant checks), and a quality
gate. Point the marathon instruction at these as the definition of done.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Model the setup on existing marathon instances (FORGE/AIVC/NEEDLE):
- .marathon/instruction.md — per-iteration autonomous prompt, plan-driven off
docs/plan/plan.md with the 7-phase order, settled invariants, and commit/push
loop protocol
- .marathon/launch-marathon.sh — self-contained `while true` loop piping the
instruction to `claude --print`, sourcing model/proxy config from an
untracked .marathon/env (keeps internal endpoints out of this public repo)
- .marathon/env.example — config template
- .gitignore — ignore .marathon/env, logs/, *-config/, and .beads/*.db
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Add a "Switching & keybindings" section: global prefix bindings that call the
daemon via run-shell — Next (jump to head-of-queue pane), Popup (display-popup
picker), Skip (tail+cooldown then advance) — plus a status-line segment.
Documents the prefix-not-bare-key constraint and why the jump is the keypress,
never automatic. Resolves open questions 3 and 4 down to polish.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- MEDIUM: reason enum stated as exactly {permission, stopped} (drop stray
"stopped/needs-next" third label)
- MEDIUM: specify oldest-READY-stuck-first — a cooldowned skipped item is not
eligible to be the head; advance to next ready item, else present empty
- MEDIUM: SubagentStop downgraded from "Confirmed" to "exists; not probed"
(probe only saw SessionStart/Stop)
- LOW: agent-inbox reuse reworded (Trail Boss actions are navigate-and-reply /
skip, not accept/edit/ignore); note permission_mode is display-only
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- HIGH: settings.json example wired PermissionRequest to a bare curl that drops
the env-only $TMUX_PANE; route all hooks through trailboss-emit.sh (which
injects $TMUX_PANE) and note SessionStart is not special
- MEDIUM: remove the stray trailboss-register.sh (contradicted "registry
self-heals on every emit"); single emitter script
- LOW: add struck-through SubagentStop row to plan detection table; clarify
disler's store is append-only (not "read-only")
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Resolve open questions from the design session:
- Stuck is stuck: no permission-vs-stopped priority; reason is display-only;
queue is a flat FIFO dead-letter queue (Stop AND PermissionRequest still
both required — permission blocks emit no Stop)
- Drop Notification entirely
- Auto-advance depletion loop: next stuck session loads on resolve/skip;
saturation is a non-issue by construction
- New primary open question: harness-coupled detection vs harness-agnostic
core, via a normalized stuck/unstuck adapter contract (switching is already
tmux-level/harness-agnostic)
- Reboot: operator re-invokes manually (no auto-resurrection in v1)
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Emphasize that agents run autonomously and the human is engaged only by
exception — the failure-mode handler / dead-letter-queue processor for a fleet
of agents, not a gate on every step.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Single-pane attention router for interactive AI coding agents: stuck sessions
surface in one prioritized queue (most-stuck first) via Claude Code hooks, and
your reply is delivered back into the exact session.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>