Skip to content

feat: board × radar fusion + observer dogfooding (PRD #8)#9

Merged
Catafal merged 1 commit into
mainfrom
feat/board-radar-fusion
Jun 11, 2026
Merged

feat: board × radar fusion + observer dogfooding (PRD #8)#9
Catafal merged 1 commit into
mainfrom
feat/board-radar-fusion

Conversation

@Catafal

@Catafal Catafal commented Jun 11, 2026

Copy link
Copy Markdown
Owner

Implements PRD #8 — and, per its dogfooding clause, the observer loop was run end-to-end on a real session before this PR, with every wiring defect that surfaced fixed in scope.

Features

  • Liveness join (board_liveness, the deep module): task ↔ session join via artifact runtime_session_id + scoped discovery, latest judge verdict extraction, ~5s TTL cache (bounded, stale-on-failure), never raises to renderers
  • Board/dashboard: ●/◐/✓ dots per card and row, judge summary line on executing/qa cards, ⚑ needs-human marker, /show Observer section, board legend
  • Enablement: bach config set (8 allowlisted observer knobs, typed + cross-field validation), bach hooks install/status --project <name>, watcher spawn confirmation + ~/.bach/logs/watcher-*.log

Dogfooding found what unit tests couldn't

  1. Hooks never fired: the Phase 16 installer wrote {"command", "run"} entries — a shape Claude Code silently ignores. Rewritten to the documented {matcher, hooks:[{type,command}]} schema with absolute venv paths (hooks don't have the venv on PATH) and migration of old entries.
  2. Radar task join scanned the wrong directory (~/.bach instead of project-local .bach/runs per ADR-002) — the Task column could never link. Registry-wide scan now.
  3. Zombie watchers: follow_events only yielded on new transcript lines, so a closed session starved the spool forever. Fixed with heartbeat ticks + four follow-on bugs the heartbeat exposed (spool matcher checked type but real events carry hook_event_name; force_judge re-fired every tick; the interval clock fired on the first event; cold-start judge windows had 1 unknown event — now backfilled from the transcript tail).
  4. Observer same→same is a benign no-op; authority-rejected verdicts still log a visible suggestion; codex stderr now logged on judge failure.
  5. Pre-existing NotImplementedError in the TCC skip banner crashed the REPL — implemented per its documented design.

Acceptance evidence (real session, this repo)

observer_start    transcript=~/.claude/projects/-Users-...-bach/42980b2f....jsonl
observer_spool_stop   (SessionEnd via installed hooks)
judge_session_start   events=50 (tail backfill)
llm_call_done         codex gpt-5.4-mini, 12s
judge_session_done    suggested='executing' needs_human=False confidence=0.90
observer_status_written  from='executing' to='executing'  (allowed no-op)
observer_done

Plus the live join: t17: liveness=idle resolved through real artifacts + discovery. bach sessions close --pid exercised twice on real sessions (correctly refused to guess between two claude processes in one project — one of which was the implementing session itself).

Tests: 1003 → 1028 passing. Review: 3 STOP + 3 DRIFT + 5 WATCH findings all fixed.

Closes #8

🤖 Generated with Claude Code

Board becomes live mission control:
- New deep module board_liveness: liveness_for_tasks joins task artifacts'
  runtime_session_id with scoped session discovery, extracts the latest
  judge verdict, ~5s TTL cache (bounded), never raises to renderers
- Liveness dots on board cards + dashboard rows; judge summary line on
  executing/qa cards; needs-human marker; /show Observer section
- bach config set (allowlisted observer knobs, typed + cross-field
  validation); bach hooks install/status --project <name>
- Watcher spawn confirmation with log path; watcher stdout/stderr now
  land in ~/.bach/logs/

Dogfooding fixes (loop proven end-to-end on a real session):
- hooks installer wrote a schema Claude Code silently ignored — rewritten
  to the documented {matcher, hooks:[{type,command}]} shape with absolute
  venv paths + migration of old entries
- sessions radar artifact join scanned ~/.bach instead of project-local
  .bach/runs (ADR-002) — registry-wide scan
- zombie watcher: follow_events starved the spool when the transcript went
  quiet — heartbeat ticks; spool matcher checked "type" but real events
  carry hook_event_name; force_judge made one-shot; interval clock no
  longer fires on first event; cold-start judge window backfilled from
  transcript tail; observer same→same is a benign no-op; authority-
  rejected verdicts still logged for display; codex stderr logged on
  judge failure
- pre-existing NotImplementedError in the TCC skip banner (crashed the
  REPL) implemented per its documented design

Acceptance evidence: full chain verified live — hooks → spool → watcher →
judge (codex, 12s, confidence 0.90) → authority-checked write → board
liveness join resolving t17 through real artifacts.

Closes #8

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@Catafal Catafal merged commit 17c0fd1 into main Jun 11, 2026
2 checks passed
@Catafal Catafal deleted the feat/board-radar-fusion branch June 11, 2026 09:28
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.

PRD: Board × Radar fusion + observer dogfooding enablement

1 participant