feat(types): add granular NEEDLE worker states + directory tailer startup re-read
Some checks are pending
CI / test (18.x) (push) Waiting to run
CI / test (20.x) (push) Waiting to run
CI / test (22.x) (push) Waiting to run

- Add BUILDING, DISPATCHING, EXECUTING, HANDLING, LOGGING, EXHAUSTED_IDLE states
- These represent the inner loop of bead execution, all map to WORKING display
- DirectoryTailer now re-reads files modified within 4 hours from start on startup
  This reconstructs worker state after FABRIC restart without replaying ancient history
- Update VALID_TRANSITIONS to include new state transitions
- Update color/icon mappings for new states
This commit is contained in:
jedarden 2026-05-26 22:18:56 -04:00
parent 7fa822d3ea
commit e4d7378096
10 changed files with 106 additions and 38 deletions

View file

@ -141,18 +141,28 @@
{"id":"bd-z87","title":"Test TUI [H] key switches to Heatmap view","description":"","design":"","acceptance_criteria":"","notes":"","status":"closed","priority":1,"issue_type":"task","owner":"","created_at":"2026-03-05T00:23:41.749224509Z","created_by":"coder","updated_at":"2026-03-05T00:31:41.660502130Z","closed_at":"2026-03-05T00:31:41.660227728Z","close_reason":"done","closed_by_session":"","source_system":"","source_repo":".","deleted_by":"","delete_reason":"","original_type":"","compaction_level":0,"original_size":0,"sender":"","comments":[{"id":9,"issue_id":"bd-z87","author":"Jed Arden","text":"Manual TUI testing task - requires human interaction to verify keyboard shortcuts. Closing as not suitable for autonomous worker implementation.","created_at":"2026-03-05T00:31:41Z"}]}
{"id":"bd-zci","title":"Map OTLP metrics → analytics DB instruments (tokens, cost, durations)","description":"## Gap\nplan.md §Architecture: 'Analytics Writer prefers OTLP metric instruments over log-derived values when both are present.' Current src/historicalStore.ts + src/workerAnalytics.ts derive these from log messages.\n\n## Work\n1. Accept OTLP metrics (Sum, Histogram, Gauge) through the receiver.\n2. Define the canonical instrument names in docs/schema.md: \\`needle.worker.tokens.in\\`, \\`needle.worker.tokens.out\\`, \\`needle.worker.cost.usd\\`, \\`needle.bead.duration\\`, etc. Align with NEEDLE's telemetry module.\n3. Persist aggregates to ~/.needle/fabric.db (see src/historicalStore.ts schema) and make analytics queries prefer metric-sourced rows when both exist.\n\n## Done when\n- fabric.db session_summary rows populated from OTLP metrics for a live NEEDLE worker.","design":"","acceptance_criteria":"","notes":"","status":"closed","priority":2,"issue_type":"task","assignee":"bravo","owner":"","created_at":"2026-04-21T12:46:21.386361740Z","created_by":"coding","updated_at":"2026-04-21T22:22:13.308294490Z","closed_at":"2026-04-21T22:22:13.308050161Z","close_reason":"Verified complete implementation: OTLP metric pipeline maps Sum/Histogram/Gauge data points through receivers -> normalizer -> MetricAccumulator -> fabric.db (metric_samples + session_worker_summaries). Canonical instruments defined in docs/schema.md. Alias resolution for NEEDLE naming conventions. Source-priority upserts ensure otlp-metric rows override log-derived estimates. All 177 tests pass.","closed_by_session":"","source_system":"","source_repo":".","deleted_by":"","delete_reason":"","original_type":"","compaction_level":0,"original_size":0,"sender":"","labels":["analytics","deferred","otlp","phase-1-5"]}
{"id":"bf-1373","title":"Fix: DiffView.parseDiff broken (added/removed line parsing fails)","description":"3 tests in src/tui/components/DiffView.test.ts fail in the parseDiff suite:\n- \"should parse added lines\"\n- \"should parse removed lines\"\n- \"should handle empty diff\"\n\nThe parseDiff function (exported from DiffView.ts) is not correctly identifying + and - lines in unified diff output, or the parsing of the old_string/new_string diff computation is incorrect.\n\nFix: audit parseDiff() in src/tui/components/DiffView.ts, ensure it correctly:\n- Splits diff on newlines\n- Classifies lines starting with '+' as added (excluding '+++')\n- Classifies lines starting with '-' as removed (excluding '---')\n- Returns empty array for empty/null input","design":"","acceptance_criteria":"","notes":"","status":"closed","priority":1,"issue_type":"task","created_at":"2026-05-02T18:18:17.589533196Z","updated_at":"2026-05-02T20:04:01.643730649Z","closed_at":"2026-05-02T20:04:01.643730649Z","close_reason":"Completed","source_repo":".","compaction_level":0}
{"id":"bf-2wf","title":"Phase 9: Productivity Analytics — remaining gaps","description":"Tracks unfinished Phase 9 items from docs/plan.md (Productivity Analytics). DONE already (verified in code 2026-05-22): beadsCompleted fires on bead.released/release_success (store.ts), worker sort by state, test-worker filter (isTestWorker + hideTestWorkers toggle), Productivity panel daily-throughput chart + worker leaderboard, GET /api/productivity. REMAINING (this epic): currentBead field, fleet summary bar, worker-card enrichment, bead workspace scanner + project breakdown. See docs/plan.md section Phase 9.","design":"","acceptance_criteria":"","notes":"","status":"open","priority":1,"issue_type":"epic","created_at":"2026-05-22T19:19:43.513243795Z","updated_at":"2026-05-22T19:19:43.513243795Z","source_repo":".","compaction_level":0,"labels":["phase9"],"dependencies":[{"issue_id":"bf-2wf","depends_on_id":"bf-60j","type":"blocks","created_at":"2026-05-22T19:21:15.280109842Z","created_by":"cli","thread_id":""},{"issue_id":"bf-2wf","depends_on_id":"bf-3xp","type":"blocks","created_at":"2026-05-22T19:21:15.283895541Z","created_by":"cli","thread_id":""},{"issue_id":"bf-2wf","depends_on_id":"bf-4f3","type":"blocks","created_at":"2026-05-22T19:21:15.287460586Z","created_by":"cli","thread_id":""},{"issue_id":"bf-2wf","depends_on_id":"bf-3t8","type":"blocks","created_at":"2026-05-22T19:21:15.291058758Z","created_by":"cli","thread_id":""}]}
{"id":"bf-30p4","title":"Fix: FileContextPanel 29 test failures (constructor, bindings, render, show/hide)","description":"29 of 57 tests in src/tui/components/FileContextPanel.test.ts fail, covering:\n- constructor: key handlers not bound on construction\n- setContextFromEvent: scroll offset not reset on new context\n- setContent: render not triggered when updating current file\n- syntax highlighting: TS/JS/Python/Rust/unknown file type detection\n- operation icons: read/edit/write/glob icon selection\n- recent files navigation: prev/next file navigation\n- show/hide/toggle: panel visibility methods\n- focus: focus() not delegating to box element\n- getElement: getElement() method missing or returning wrong element\n- clear: render not triggered after clear\n- key bindings: scroll up/down/page keys, open-in-editor key not bound\n- render output: no-file message, file path in header, directory path, operation history\n- regression: operation type detection\n\nThis is a broad failure suggesting FileContextPanel was written to a different API than what the tests expect, or the constructor wiring is broken.\n\nFix: audit FileContextPanel constructor and public API against the test expectations; ensure all key bindings, render, show/hide, focus methods are correctly implemented.","design":"","acceptance_criteria":"","notes":"","status":"open","priority":1,"issue_type":"task","created_at":"2026-05-02T18:18:36.164020387Z","updated_at":"2026-05-02T20:34:41.385937456Z","source_repo":".","compaction_level":0}
{"id":"bf-3oy5","title":"Fix: SemanticNarrativePanel refresh/update methods not working (3 failing tests)","description":"3 tests in src/tui/components/SemanticNarrativePanel.test.ts fail:\n- \"should refresh narrative from manager\" — refresh() method does not call semanticNarrative manager or update display\n- \"should generate narrative for worker and set it\" — updateFromWorker() method broken\n- \"should generate aggregated narrative and set it\" — updateAggregated() method broken\n\nFix: audit SemanticNarrativePanel and ensure refresh(), updateFromWorker(), and updateAggregated() correctly call the SemanticNarrative module (src/semanticNarrative.ts) and update the blessed box content.","design":"","acceptance_criteria":"","notes":"","status":"open","priority":1,"issue_type":"task","created_at":"2026-05-02T18:18:41.467283927Z","updated_at":"2026-05-02T18:18:41.467283927Z","source_repo":".","compaction_level":0}
{"id":"bf-3t8","title":"Worker card: show beadsCompleted + currentBead, remove eventCount","description":"docs/plan.md Phase 9 UI change 2 + checklist. In src/web/frontend/src/components/WorkerGrid.tsx the worker card currently renders {worker.eventCount} events (~line 156). Replace with beads-completed count and, when WORKING, the current bead id, e.g. bead: bf-5r22 / 31 completed . last 2m ago. Remove the eventCount display. Requires the currentBead field (depends on that bead). Update test/WorkerGrid.test.tsx.","design":"","acceptance_criteria":"","notes":"","status":"open","priority":2,"issue_type":"task","created_at":"2026-05-22T19:20:00.467884852Z","updated_at":"2026-05-22T19:20:00.467884852Z","source_repo":".","compaction_level":0,"labels":["phase9"],"dependencies":[{"issue_id":"bf-3t8","depends_on_id":"bf-60j","type":"blocks","created_at":"2026-05-22T19:21:15.294563645Z","created_by":"cli","thread_id":""}]}
{"id":"bf-1nah","title":"Fix bin/fabric entrypoint and install systemd service","description":"Two infrastructure gaps preventing fabric from running:\n\n1. bin/fabric is an empty file (0 bytes). The package.json declares bin: {'fabric': './dist/cli.js'}, so npm install -g should create a 'fabric' executable — but the bin/ directory entry is empty/unused (dist/cli.js IS the real entrypoint). Fix: either remove the empty bin/fabric file and rely solely on the package.json bin declaration, or make bin/fabric a shebang wrapper:\n #!/usr/bin/env node\n require('../dist/cli.js');\n Either way, verify that 'node dist/cli.js --help' works and that the bin entry actually resolves.\n\n2. The systemd user service has never been installed. The unit file exists at scripts/fabric-web.service. Install it:\n mkdir -p ~/.config/systemd/user\n cp scripts/fabric-web.service ~/.config/systemd/user/\n Also install the prune timer:\n cp scripts/fabric-prune.service ~/.config/systemd/user/\n cp scripts/fabric-prune.timer ~/.config/systemd/user/\n systemctl --user daemon-reload\n systemctl --user enable fabric-web.service\n systemctl --user start fabric-web.service\n Verify: systemctl --user status fabric-web.service shows 'active (running)' and curl -s http://localhost:3000/api/workers returns JSON.\n Also verify the secrets file exists at ~/.config/fabric/secrets.env with FABRIC_AUTH_TOKEN set (create with a random token if missing).\n\nAcceptance: systemctl --user status fabric-web.service is active, curl http://localhost:3000/api/workers returns valid JSON.","design":"","acceptance_criteria":"","notes":"","status":"closed","priority":1,"issue_type":"task","assignee":"marathon","created_at":"2026-05-26T21:00:11.965674425Z","updated_at":"2026-05-26T21:05:46.348779251Z","closed_at":"2026-05-26T21:05:46.348779251Z","close_reason":"Commit 67f991a: Fixed systemd service node paths for NixOS (/usr/bin/node -> /home/coding/.nix-profile/bin/node), removed empty bin/fabric file, created ~/.config/fabric/secrets.env, installed and enabled fabric-web.service and fabric-prune.timer. Acceptance verified: systemctl --user status fabric-web.service active (running), curl http://localhost:3000/api/workers returns valid JSON.","source_repo":".","compaction_level":0}
{"id":"bf-2wf","title":"Phase 9: Productivity Analytics — remaining gaps","description":"Tracks unfinished Phase 9 items from docs/plan.md (Productivity Analytics). DONE already (verified in code 2026-05-22): beadsCompleted fires on bead.released/release_success (store.ts), worker sort by state, test-worker filter (isTestWorker + hideTestWorkers toggle), Productivity panel daily-throughput chart + worker leaderboard, GET /api/productivity. REMAINING (this epic): currentBead field, fleet summary bar, worker-card enrichment, bead workspace scanner + project breakdown. See docs/plan.md section Phase 9.","design":"","acceptance_criteria":"","notes":"","status":"closed","priority":1,"issue_type":"epic","assignee":"claude-code-glm-4.7-echo","created_at":"2026-05-22T19:19:43.513243795Z","updated_at":"2026-05-22T22:02:57.997168418Z","closed_at":"2026-05-22T22:02:57.997168418Z","close_reason":"Phase 9 Productivity Analytics verification complete. All items were already implemented in prior sessions. See notes/bf-2wf.md for details.","source_repo":".","compaction_level":0,"labels":["phase9"],"dependencies":[{"issue_id":"bf-2wf","depends_on_id":"bf-60j","type":"blocks","created_at":"2026-05-22T19:21:15.280109842Z","created_by":"cli","thread_id":""},{"issue_id":"bf-2wf","depends_on_id":"bf-3xp","type":"blocks","created_at":"2026-05-22T19:21:15.283895541Z","created_by":"cli","thread_id":""},{"issue_id":"bf-2wf","depends_on_id":"bf-4f3","type":"blocks","created_at":"2026-05-22T19:21:15.287460586Z","created_by":"cli","thread_id":""},{"issue_id":"bf-2wf","depends_on_id":"bf-3t8","type":"blocks","created_at":"2026-05-22T19:21:15.291058758Z","created_by":"cli","thread_id":""}]}
{"id":"bf-30p4","title":"Fix: FileContextPanel 29 test failures (constructor, bindings, render, show/hide)","description":"29 of 57 tests in src/tui/components/FileContextPanel.test.ts fail, covering:\n- constructor: key handlers not bound on construction\n- setContextFromEvent: scroll offset not reset on new context\n- setContent: render not triggered when updating current file\n- syntax highlighting: TS/JS/Python/Rust/unknown file type detection\n- operation icons: read/edit/write/glob icon selection\n- recent files navigation: prev/next file navigation\n- show/hide/toggle: panel visibility methods\n- focus: focus() not delegating to box element\n- getElement: getElement() method missing or returning wrong element\n- clear: render not triggered after clear\n- key bindings: scroll up/down/page keys, open-in-editor key not bound\n- render output: no-file message, file path in header, directory path, operation history\n- regression: operation type detection\n\nThis is a broad failure suggesting FileContextPanel was written to a different API than what the tests expect, or the constructor wiring is broken.\n\nFix: audit FileContextPanel constructor and public API against the test expectations; ensure all key bindings, render, show/hide, focus methods are correctly implemented.","design":"","acceptance_criteria":"","notes":"","status":"closed","priority":1,"issue_type":"task","assignee":"claude-code-glm-4.7-juliet","created_at":"2026-05-02T18:18:36.164020387Z","updated_at":"2026-05-22T20:32:56.398383689Z","closed_at":"2026-05-22T20:32:56.398383689Z","close_reason":"Completed - all 57 tests passing","source_repo":".","compaction_level":0}
{"id":"bf-3cem","title":"plan-gap: Phase 2-7 TUI features — audit which items are genuinely unimplemented vs just unchecked","description":"plan.md Phases 2-7 contain 33 items marked [ ] (unchecked), but bead bf-ozsu notes that nearly all are already implemented. This bead is a verification audit — not implementation.\n\nWalk each unchecked item in plan.md Phases 2-7:\n- Phase 2 (TUI): Worker list panel, Live log stream panel, Worker detail panel, Keyboard controls, Command palette, File context panel, Focus mode with pinning\n- Phase 3 (Web): HTTP server + WebSocket, React dashboard, Worker cards + activity feed, Command palette, File context panel, Focus mode with pinning\n- Phase 4 (Intelligence): Cross-reference hyperlinking, Inline diff view, File activity heatmap, Cost & token tracking, Conversation transcript view\n- Phase 5 (Detection): Stuck detection, Loop detection, Worker collision detection, Smart error grouping, Semantic narrative\n- Phase 6 (Context): Git integration panel, AI session digest, Worker comparison analytics, Historical session index\n- Phase 7 (Advanced): Session replay, Task dependency DAG, Budget alerts, Anomaly detection, Recovery playbook\n\nFor each item: grep for the relevant symbol/file, check if a test exercises it, and classify as:\n - [x] IMPLEMENTED — code and test exist\n - [ ] STUBBED — file exists but the feature is hollow/non-functional\n - [ ] MISSING — not present at all\n\nFor anything STUBBED or MISSING that is not already an open bead, create a plan-gap: bead.\nThen close bf-ozsu (the plan.md checkbox update) by checking off all confirmed-implemented items.\n\nAcceptance: plan.md Phase 2-7 checkboxes reflect reality; any genuine gaps have new beads; bf-ozsu is closed.","design":"","acceptance_criteria":"","notes":"","status":"closed","priority":2,"issue_type":"task","assignee":"marathon","created_at":"2026-05-26T21:00:24.688151474Z","updated_at":"2026-05-26T21:22:13.312208071Z","closed_at":"2026-05-26T21:22:13.312208071Z","close_reason":"Audit complete: All 33 unchecked items in Phases 2-7 are IMPLEMENTED. Verified code + tests exist for all features. No new gaps found - only bf-4cqq (web worker comparison API) remains valid from prior analysis.","source_repo":".","compaction_level":0}
{"id":"bf-3oy5","title":"Fix: SemanticNarrativePanel refresh/update methods not working (3 failing tests)","description":"3 tests in src/tui/components/SemanticNarrativePanel.test.ts fail:\n- \"should refresh narrative from manager\" — refresh() method does not call semanticNarrative manager or update display\n- \"should generate narrative for worker and set it\" — updateFromWorker() method broken\n- \"should generate aggregated narrative and set it\" — updateAggregated() method broken\n\nFix: audit SemanticNarrativePanel and ensure refresh(), updateFromWorker(), and updateAggregated() correctly call the SemanticNarrative module (src/semanticNarrative.ts) and update the blessed box content.","design":"","acceptance_criteria":"","notes":"","status":"closed","priority":1,"issue_type":"task","assignee":"claude-code-glm-4.7-echo","created_at":"2026-05-02T18:18:41.467283927Z","updated_at":"2026-05-22T21:32:01.598916185Z","closed_at":"2026-05-22T21:32:01.598916185Z","close_reason":"Fixed SemanticNarrativePanel test mock setup issue - vi.mock() was capturing mockManagerInstance at module load time (undefined). Fixed by using vi.fn() without return value in mock declaration, then mockReturnValue(mockManagerInstance) in beforeEach.\n\nRetrospective:\n- What worked: Identifying the root cause as a vi.mock() hoisting issue\n- What didn't: Initially tried to understand test failures without running them (npm not available)\n- Surprise: vi.mock() callback captures variables at module load time, not at runtime\n- Reusable pattern: For mocks that need to return dynamically created instances, use vi.fn() without return value in mock declaration, then call mockReturnValue(instance) in beforeEach","source_repo":".","compaction_level":0}
{"id":"bf-3pck","title":"plan-gap: Update plan.md — check off completed Phases 2, 3, and 6","description":"Plan Gap: Phases 2, 3, and 6 are marked incomplete in docs/plan.md but all components are implemented and verified.\n\nEvidence:\n- Phase 2 (TUI): WorkerGrid.ts, ActivityStream.ts, WorkerDetail.ts, CommandPalette.ts, FileContextPanel.ts, focusPresets.ts all exist with tests\n- Phase 3 (Web): web/server.ts (HTTP+WebSocket), React App.tsx, WorkerGrid.tsx, ActivityStream.tsx, CommandPalette.tsx, FileContextPanel.tsx all exist with tests\n- Phase 6: Worker comparison analytics (bf-4cqq) — API endpoints /api/workers/compare, /api/analytics/workers, /api/analytics/sessions exist; WorkerAnalyticsPanel.tsx exists and is wired into App.tsx\n\nAcceptance: Update docs/plan.md to change Phase 2, 3 checkboxes from [ ] to [x]; update Phase 6 worker comparison analytics from [ ] to [x]; update status line at end of plan.","design":"","acceptance_criteria":"","notes":"","status":"closed","priority":2,"issue_type":"task","assignee":"marathon","created_at":"2026-05-26T21:31:20.469107030Z","updated_at":"2026-05-26T21:32:10.642303318Z","closed_at":"2026-05-26T21:32:10.642303318Z","close_reason":"Updated docs/plan.md to check off Phases 2 (TUI), 3 (Web), and 6 (Worker Comparison Analytics). All components verified: WorkerGrid, ActivityStream, WorkerDetail, CommandPalette, FileContextPanel, FocusPreset exist with tests. Web API endpoints /api/workers/compare, /api/analytics/workers, /api/analytics/sessions implemented. WorkerAnalyticsPanel.tsx wired into App.tsx. Tests pass (2484 passed, 4 skipped).","source_repo":".","compaction_level":0}
{"id":"bf-3t8","title":"Worker card: show beadsCompleted + currentBead, remove eventCount","description":"docs/plan.md Phase 9 UI change 2 + checklist. In src/web/frontend/src/components/WorkerGrid.tsx the worker card currently renders {worker.eventCount} events (~line 156). Replace with beads-completed count and, when WORKING, the current bead id, e.g. bead: bf-5r22 / 31 completed . last 2m ago. Remove the eventCount display. Requires the currentBead field (depends on that bead). Update test/WorkerGrid.test.tsx.","design":"","acceptance_criteria":"","notes":"","status":"closed","priority":2,"issue_type":"task","assignee":"claude-code-glm-4.7-FABRIC","created_at":"2026-05-22T19:20:00.467884852Z","updated_at":"2026-05-22T19:51:43.664614729Z","closed_at":"2026-05-22T19:51:43.664614729Z","close_reason":"Completed - worker card shows beadsCompleted + currentBead, removes eventCount","source_repo":".","compaction_level":0,"labels":["phase9"],"dependencies":[{"issue_id":"bf-3t8","depends_on_id":"bf-60j","type":"blocks","created_at":"2026-05-22T19:21:15.294563645Z","created_by":"cli","thread_id":""}]}
{"id":"bf-3vwc","title":"Implement: FileHeatmap web component treemap view (5 failing tests)","description":"5 tests in src/web/frontend/test/FileHeatmap.test.tsx fail for the Treemap view feature:\n- \"should have view mode toggle buttons\" — no list/treemap/timelapse toggle buttons rendered\n- \"should switch to treemap view when treemap button clicked\" — no treemap button\n- \"should render treemap nodes\" — treemap nodes not rendered\n- \"should hide sort button in treemap mode\" — sort button not hidden in treemap mode\n- \"should show tooltip when hovering treemap node\" — no hover tooltip\n\nThe FileHeatmap.tsx component (src/web/frontend/src/components/FileHeatmap.tsx) is missing the treemap view mode. The plan (feature 10) describes: \"Web version can use Treemap visualization (rectangles sized by activity).\"\n\nBead bd-mrh (Add treemap visualization option to File Heatmap) was closed but the tests show the feature is not in the component.\n\nFix: add treemap view mode to FileHeatmap.tsx — view mode toggle buttons (list/treemap), treemap node rendering with proportional sizing, hover tooltip, hide sort button in treemap mode.","design":"","acceptance_criteria":"","notes":"","status":"closed","priority":2,"issue_type":"task","created_at":"2026-05-02T18:18:57.205322769Z","updated_at":"2026-05-02T20:50:07.559084809Z","closed_at":"2026-05-02T20:50:07.559084809Z","close_reason":"All treemap view features already implemented and tested (31/31 tests passing in FileHeatmap.test.tsx).\n\n## Retrospective\n- **What worked:** Code review confirmed FileHeatmap.tsx has complete treemap implementation (lines 443-523) with calculateTreemapLayout, node rendering, hover tooltips, and sort button hiding in treemap mode\n- **What didn't:** Bead was created based on stale test failure information\n- **Surprise:** Tests were already passing when bead was created - the feature gap was already closed in earlier commits\n- **Reusable pattern:** Verify test status before creating gap closure beads","source_repo":".","compaction_level":0}
{"id":"bf-3xp","title":"Bead workspace scanner + project breakdown in /api/productivity","description":"docs/plan.md Phase 9 (By Project breakdown; checklist items: bead workspace scanner and /api/productivity project breakdown). Add a scanner that reads configured .beads/issues.jsonl files and counts CLOSED beads per project, deriving project from the bead id prefix (kt prefix = kalshi-tape, bf prefix = global) using close_reason/closed_at/assignee. Add a configurable workspace list in config.ts. Extend GET /api/productivity in src/web/server.ts near line 1100 to add a byProject array (today it returns only daily and workers). Add a By Project section to the ProductivityPanel React component. Add tests.","design":"","acceptance_criteria":"","notes":"","status":"open","priority":1,"issue_type":"task","assignee":"","created_at":"2026-05-22T19:21:03.942318600Z","updated_at":"2026-05-22T19:30:53.062766062Z","source_repo":".","compaction_level":0,"labels":["phase9"]}
{"id":"bf-48nk","title":"Genesis: FABRIC implementation gap closure","description":"Tied to plan: /home/coding/FABRIC/docs/plan.md\n\n## Overview\nFABRIC is a live display for NEEDLE worker activity (TUI + web). Phases 18 of the plan.md are marked complete, but test failures reveal concrete implementation gaps. This genesis bead tracks closure of all remaining gaps.\n\n## Progress\n- [x] Phase 18: Core infrastructure, TUI, web, intelligence features, directory tailer — all complete per plan.md\n- [ ] Bug fixes: failing unit tests across 10 test files (89 failed / 2206 total)\n- [ ] Missing module: src/memoryProfiler.ts (breaks server.ts and all web server tests)\n- [ ] Web frontend: treemap + timelapse in FileHeatmap not implemented (16 failing tests)\n- [ ] Web frontend: SpanDag zoom/pan interaction not implemented (13 failing tests)","design":"","acceptance_criteria":"","notes":"","status":"open","priority":1,"issue_type":"genesis","created_at":"2026-05-02T18:17:56.078683713Z","updated_at":"2026-05-11T11:34:30.986913667Z","source_repo":".","compaction_level":0}
{"id":"bf-4f3","title":"Fleet summary bar component (web dashboard)","description":"docs/plan.md Phase 9 UI change 1. Add a single always-visible summary line at the top of the web dashboard: N WORKING . N SELECTING . N EXHAUSTED . N beads today . N stuck. Aggregate counts from worker needleState, beadsCompleted-today, and stuck flags. New component under src/web/frontend/src/components/, wired into App.tsx above the worker grid. Add a frontend test.","design":"","acceptance_criteria":"","notes":"","status":"open","priority":2,"issue_type":"task","created_at":"2026-05-22T19:20:00.454860234Z","updated_at":"2026-05-22T19:20:00.454860234Z","source_repo":".","compaction_level":0,"labels":["phase9"]}
{"id":"bf-3xp","title":"Bead workspace scanner + project breakdown in /api/productivity","description":"docs/plan.md Phase 9 (By Project breakdown; checklist items: bead workspace scanner and /api/productivity project breakdown). Add a scanner that reads configured .beads/issues.jsonl files and counts CLOSED beads per project, deriving project from the bead id prefix (kt prefix = kalshi-tape, bf prefix = global) using close_reason/closed_at/assignee. Add a configurable workspace list in config.ts. Extend GET /api/productivity in src/web/server.ts near line 1100 to add a byProject array (today it returns only daily and workers). Add a By Project section to the ProductivityPanel React component. Add tests.","design":"","acceptance_criteria":"","notes":"","status":"closed","priority":1,"issue_type":"task","assignee":"claude-code-glm-4.7-FABRIC","created_at":"2026-05-22T19:21:03.942318600Z","updated_at":"2026-05-22T19:42:14.806571548Z","closed_at":"2026-05-22T19:42:14.806571548Z","close_reason":"Implemented Phase 9 bead workspace scanner and project breakdown: (1) src/beadWorkspaceScanner.ts scanner reads .beads/issues.jsonl files, counts CLOSED beads per project; (2) src/config.ts extended with WorkspaceConfig, loadWorkspaces(), detectBeadPrefix(); (3) /api/productivity endpoint returns byProject array; (4) ProductivityPanel By Project section with progress bars; (5) 16 test cases in beadWorkspaceScanner.test.ts. Total 612 lines added. Retrospective: worked - existing FABRIC patterns, vitest with fs mocking; surprise - already committed in 5e029c1; pattern - scanner + config + API + UI + tests.","source_repo":".","compaction_level":0,"labels":["phase9"]}
{"id":"bf-40cu","title":"Fix 6 failing unit tests","description":"6 tests fail across 5 files. Fix each:\n\n1. src/tui/components/CrossReferencePanel.test.ts — entire file fails with: 'Cannot call vi.hoisted() inside vi.mock()'. The test uses vi.hoisted() inside a vi.mock() factory, which Vitest forbids. Restructure the mock: hoist variables with a top-level vi.hoisted() call (before vi.mock()), then reference those variables inside the vi.mock() factory instead of nesting the calls.\n\n2. src/tui/components/WorkerAnalyticsPanel.test.ts — same vi.hoisted()-inside-vi.mock() error. Same fix.\n\n3. src/tui/components/WorkerGrid.e2e.test.ts > 'should handle realistic worker status scenario' — expects 'bd-abc123' in the rendered output (the worker's currentBead), but the grid does not render it. Read the WorkerGrid render method; ensure currentBead is displayed in the worker entry line when set.\n\n4. src/tui/components/WorkerGrid.test.ts > 'should include current task from lastEvent' — similar: test expects bead ID in rendered output.\n\n5. src/tui/components/WorkerGrid.test.ts > 'should handle very long task descriptions' — test asserts truncation of a long task string; the assertion no longer matches current render format.\n\n6. src/web/frontend/test/WorkerGrid.test.tsx (3 subtests: 'should display seconds ago', 'minutes ago', 'hours ago') — renders 'NaNh ago' instead of formatted time. The lastEvent timestamp (mock value) is likely being read as undefined or NaN. Check how the React WorkerGrid component formats lastEvent.ts.\n\nAcceptance: npm test passes with 0 failures.","design":"","acceptance_criteria":"","notes":"","status":"closed","priority":1,"issue_type":"task","assignee":"marathon","created_at":"2026-05-26T20:59:52.322631256Z","updated_at":"2026-05-26T21:16:51.205201866Z","closed_at":"2026-05-26T21:16:51.205201866Z","close_reason":"Fixed all 6 failing tests: (1) CrossReferencePanel.test.ts - moved vi.hoisted() outside vi.mock(), (2) WorkerAnalyticsPanel.test.ts - moved vi.hoisted() outside vi.mock(), (3) WorkerGrid.e2e.test.ts - now renders lastEvent.bead, (4) WorkerGrid.test.ts - now renders lastEvent.bead and msg with truncation, (5-6) React WorkerGrid.test.tsx - use lastActivity instead of lastSeen. All 2484 tests pass. Commit 5b350b9.","source_repo":".","compaction_level":0,"dependencies":[{"issue_id":"bf-40cu","depends_on_id":"bf-1nah","type":"blocks","created_at":"2026-05-26T21:00:43.542205369Z","created_by":"batch","thread_id":""}]}
{"id":"bf-48nk","title":"Genesis: FABRIC implementation gap closure","description":"Tied to plan: /home/coding/FABRIC/docs/plan.md\n\n## Overview\nFABRIC is a live display for NEEDLE worker activity (TUI + web). Phases 18 of the plan.md are marked complete, but test failures reveal concrete implementation gaps. This genesis bead tracks closure of all remaining gaps.\n\n## Progress\n- [x] Phase 18: Core infrastructure, TUI, web, intelligence features, directory tailer — all complete per plan.md\n- [ ] Bug fixes: failing unit tests across 10 test files (89 failed / 2206 total)\n- [ ] Missing module: src/memoryProfiler.ts (breaks server.ts and all web server tests)\n- [ ] Web frontend: treemap + timelapse in FileHeatmap not implemented (16 failing tests)\n- [ ] Web frontend: SpanDag zoom/pan interaction not implemented (13 failing tests)","design":"","acceptance_criteria":"","notes":"","status":"closed","priority":1,"issue_type":"genesis","assignee":"claude-code-glm-4.7-FABRIC","created_at":"2026-05-02T18:17:56.078683713Z","updated_at":"2026-05-22T20:06:17.484570333Z","closed_at":"2026-05-22T20:06:17.484570333Z","close_reason":"Verified all gaps closed. All 2484 tests pass. See notes/bf-48nk-verification.md","source_repo":".","compaction_level":0}
{"id":"bf-4cqq","title":"Web: Worker Comparison Analytics panel and API","description":"The TUI has a WorkerAnalyticsPanel with comparison mode (compareWorkers method in workerAnalytics.ts), but the web layer is missing both the backend API endpoints and the frontend component.\n\nMissing backend (src/web/server.ts):\n- GET /api/workers/compare?worker1=<id>&worker2=<id> — returns WorkerComparison via analyticsManager.compareWorkers()\n- GET /api/analytics/workers — returns per-worker WorkerMetrics for the leaderboard table\n- GET /api/analytics/sessions — exposes historicalStore.getSessions() for cross-session comparisons\n\nMissing frontend (src/web/frontend/src/):\n- components/WorkerAnalyticsPanel.tsx — comparison view mirroring TUI WorkerAnalyticsPanel behavior\n- Wire into App.tsx alongside the existing AnalyticsDashboard toggle\n\nReference: plan.md Phase 6 (Worker comparison analytics), historicalStore.ts getWorkerComparisonMetrics(), workerAnalytics.ts compareWorkers()","design":"","acceptance_criteria":"","notes":"","status":"closed","priority":2,"issue_type":"task","assignee":"marathon","created_at":"2026-05-26T04:03:52.021815911Z","updated_at":"2026-05-26T21:29:40.957322874Z","closed_at":"2026-05-26T21:29:40.957322874Z","close_reason":"Implemented web Worker Comparison Analytics panel and API endpoints:\n\nBackend API endpoints in src/web/server.ts:\n- GET /api/workers/compare?worker1=&worker2= — returns WorkerComparison via analyticsManager.compareWorkers()\n- GET /api/analytics/workers — returns per-worker WorkerMetrics for leaderboard table\n- GET /api/analytics/sessions — exposes historicalStore.getSessions() for cross-session comparisons\n\nFrontend component at src/web/frontend/src/components/WorkerAnalyticsPanel.tsx:\n- Comparison view mirroring TUI WorkerAnalyticsPanel behavior\n- Leaderboard table with sortable columns (beads, beads/hour, avg time, error rate, cost/bead, efficiency)\n- Historical sessions list\n- Worker selection for comparison with diff/percent/winner indicators\n\nWired into App.tsx with new Workers button (⚔️ icon) and command palette action (show:worker-analytics)\n\nCommits: 600b114\nTests: All 2484 tests pass, type-check and build pass","source_repo":".","compaction_level":0}
{"id":"bf-4f3","title":"Fleet summary bar component (web dashboard)","description":"docs/plan.md Phase 9 UI change 1. Add a single always-visible summary line at the top of the web dashboard: N WORKING . N SELECTING . N EXHAUSTED . N beads today . N stuck. Aggregate counts from worker needleState, beadsCompleted-today, and stuck flags. New component under src/web/frontend/src/components/, wired into App.tsx above the worker grid. Add a frontend test.","design":"","acceptance_criteria":"","notes":"","status":"closed","priority":2,"issue_type":"task","assignee":"claude-code-glm-4.7-FABRIC","created_at":"2026-05-22T19:20:00.454860234Z","updated_at":"2026-05-22T19:47:44.711552009Z","closed_at":"2026-05-22T19:47:44.711552009Z","close_reason":"Completed","source_repo":".","compaction_level":0,"labels":["phase9"]}
{"id":"bf-4xm8","title":"Implement: FileHeatmap web component timelapse animation (11 failing tests)","description":"11 tests in src/web/frontend/test/FileHeatmap.test.tsx fail for the Timelapse animation feature:\n- \"should have timelapse view mode button\"\n- \"should switch to timelapse view when timelapse button clicked\"\n- \"should fetch timelapse data when entering timelapse mode\"\n- \"should display timelapse playback controls when data loaded\"\n- \"should have play/pause button in timelapse mode\"\n- \"should have speed controls in timelapse mode\"\n- \"should have timeline slider in timelapse mode\"\n- \"should have loop checkbox in timelapse mode\"\n- \"should show timeline labels with time and progress\"\n- \"should display loading state while fetching timelapse data\"\n- \"should display error message on timelapse fetch failure\"\n\nThe plan (feature 10) describes: \"Time-lapse animation showing activity over time.\" Bead bd-tge (Add time-lapse animation to heatmap) was closed but tests show the feature is not in the component.\n\nFix: add timelapse mode to FileHeatmap.tsx — timelapse button, data fetch from API endpoint, playback controls (play/pause, speed, slider, loop), loading/error states, timeline labels.","design":"","acceptance_criteria":"","notes":"","status":"closed","priority":2,"issue_type":"task","created_at":"2026-05-02T18:19:04.021773205Z","updated_at":"2026-05-02T20:50:07.575786176Z","closed_at":"2026-05-02T20:50:07.575786176Z","close_reason":"All timelapse animation features already implemented and tested (31/31 tests passing in FileHeatmap.test.tsx).\n\n## Retrospective\n- **What worked:** Code review confirmed FileHeatmap.tsx has complete timelapse implementation (lines 526-641) with playback controls, speed adjustment, looping, timeline slider, and loading/error states\n- **What didn't:** Bead was created based on stale test failure information\n- **Surprise:** Tests were already passing when bead was created - the feature gap was already closed in earlier commits\n- **Reusable pattern:** Verify test status before creating gap closure beads","source_repo":".","compaction_level":0}
{"id":"bf-50gc","title":"Fix: store.ts maxEvents/event-expiration not enforced","description":"Tests in src/store.test.ts fail with 17 errors across three categories:\n\n1. maxEvents limit not enforced: adding 150 events to a store with maxEvents=100 results in 150 events (expected 100). Tests: \"should trim old events when over limit\", \"should keep most recent events\", \"should use default maxEvents of 10000\".\n\n2. Cross-Reference Integration: store does not track cross-references when events are added; getCrossReferenceLinks(), getLinkedEntities() etc. return empty or wrong results.\n\n3. Bead collision detection: getBeadCollisions() and collisionTypes on WorkerInfo are not implemented or not wired.\n\nRoot cause: InMemoryEventStore.addEvent() is likely missing the trim/eviction logic and the cross-reference/collision update hooks.\n\nFix: implement event eviction on maxEvents overflow (keep most recent), call CrossReferenceManager.update() on each addEvent(), and implement bead-collision tracking.","design":"","acceptance_criteria":"","notes":"","status":"closed","priority":0,"issue_type":"task","assignee":"claude-code-glm-4.7-alpha","created_at":"2026-05-02T18:18:11.996925149Z","updated_at":"2026-05-02T18:34:09.178146746Z","closed_at":"2026-05-02T18:34:09.178146746Z","close_reason":"Completed","source_repo":".","compaction_level":0,"comments":[{"id":14,"issue_id":"bf-50gc","author":"cli","text":"## Retrospective\n- **What worked:** Incremental test-driven debugging — running tests after each fix to verify progress without getting overwhelmed by 17 failures at once.\n- **What didn't:** Initial maxEvents fix was too aggressive (trimming at >= instead of >), causing events to be trimmed one event too early. Had to adjust the condition twice.\n- **Surprise:** CrossReferenceManager intentionally skipped event-type entities to avoid unbounded growth, but tests expected worker->event links. Fixed by creating immediate event links in processEvent() without storing event entities.\n- **Reusable pattern:** For event store trimming, use `> limit` not `>= limit` — trim only when exceeding, not when reaching capacity. For collision detection, always check time windows before marking collisions, not just during cleanup.","created_at":"2026-05-02T18:34:37.485472281Z"}]}
{"id":"bf-5klc","title":"Fix: CrossReferencePanel and WorkerAnalyticsPanel vi.mock hoisting errors","description":"Two test files fail to load entirely due to vitest mock hoisting issues:\n\nsrc/tui/components/CrossReferencePanel.test.ts:\n Error: Cannot access 'MockCrossReferenceManager' before initialization (line 79)\n Cause: vi.mock() factory references a const declared after it — vitest hoists vi.mock() calls to top of file, but the factory captures the const by reference before it is initialized.\n\nsrc/tui/components/WorkerAnalyticsPanel.test.ts:\n Error: Cannot access 'MockWorkerAnalytics' before initialization (line 72)\n Same cause.\n\nFix: In both test files, convert the vi.mock() factory to use inline mock definitions (no top-level const references), or use vi.hoisted() to declare the mock variables so they are available when the factory runs.\n\nReference: https://vitest.dev/api/vi.html#vi-mock (hoisting rules)","design":"","acceptance_criteria":"","notes":"","status":"open","priority":1,"issue_type":"task","created_at":"2026-05-02T18:18:48.874294693Z","updated_at":"2026-05-02T18:18:48.874294693Z","source_repo":".","compaction_level":0}
{"id":"bf-5klc","title":"Fix: CrossReferencePanel and WorkerAnalyticsPanel vi.mock hoisting errors","description":"Two test files fail to load entirely due to vitest mock hoisting issues:\n\nsrc/tui/components/CrossReferencePanel.test.ts:\n Error: Cannot access 'MockCrossReferenceManager' before initialization (line 79)\n Cause: vi.mock() factory references a const declared after it — vitest hoists vi.mock() calls to top of file, but the factory captures the const by reference before it is initialized.\n\nsrc/tui/components/WorkerAnalyticsPanel.test.ts:\n Error: Cannot access 'MockWorkerAnalytics' before initialization (line 72)\n Same cause.\n\nFix: In both test files, convert the vi.mock() factory to use inline mock definitions (no top-level const references), or use vi.hoisted() to declare the mock variables so they are available when the factory runs.\n\nReference: https://vitest.dev/api/vi.html#vi-mock (hoisting rules)","design":"","acceptance_criteria":"","notes":"","status":"closed","priority":1,"issue_type":"task","assignee":"claude-code-glm-4.7-echo","created_at":"2026-05-02T18:18:48.874294693Z","updated_at":"2026-05-22T21:49:56.075000556Z","closed_at":"2026-05-22T21:49:56.075000556Z","close_reason":"Completed","source_repo":".","compaction_level":0}
{"id":"bf-5r8a","title":"Fix: missing src/memoryProfiler.ts breaks server.ts and all web server tests","description":"src/web/server.ts imports { getMemoryProfiler } from '../memoryProfiler.js' at line 24, but the file src/memoryProfiler.ts does not exist. This causes src/web/server.test.ts to fail at import with: \"Cannot find module '../memoryProfiler.js'\". All web server tests are blocked.\n\nThe memoryProfiler module was planned in bd-ch6.7 (memory profiling / leak hunt) but the source file was never created. A stub or full implementation is needed.\n\nRelated: src/heapDiff.ts exists and is imported by server.ts at line 25 — the memoryProfiler likely wraps or supplements heapDiff functionality.\n\nFix: create src/memoryProfiler.ts exporting getMemoryProfiler() that returns a profiler object compatible with how server.ts uses it.","design":"","acceptance_criteria":"","notes":"","status":"closed","priority":0,"issue_type":"task","assignee":"claude-code-glm-4.7-charlie","created_at":"2026-05-02T18:18:03.468692907Z","updated_at":"2026-05-02T19:29:03.954818488Z","closed_at":"2026-05-02T19:29:03.954818488Z","close_reason":"Completed","source_repo":".","compaction_level":0,"comments":[{"id":13,"issue_id":"bf-5r8a","author":"cli","text":"MemoryProfiler module added to fix missing import in server.ts.\n\n## Retrospective\n- **What worked:** The file src/memoryProfiler.ts was already created (likely by another agent before I picked up the bead). All 90 web server tests pass, confirming the implementation is complete and correct.\n- **What didn't:** N/A — the file existed and was functional when I picked up the bead.\n- **Surprise:** The bead was opened for a missing file, but the file was already present (untracked) with a complete implementation. The file timestamp (May 2 14:27) suggests it was created after the bead was opened but before I picked it up.\n- **Reusable pattern:** For missing module imports, verify the current state first — files may be created by other agents while a bead is in the queue.","created_at":"2026-05-02T18:30:23.269027772Z"}]}
{"id":"bf-5u6j","title":"plan-gap: store.test.ts — add afterEach hook to reset global singletons","description":"Plan: Phase 1-9 implementation. Gap evidence: src/store.test.ts test 'should use default maxEvents of 10000' times out when run with full test suite, passes in isolation. Root cause: global singletons (WorkerAnalytics, CrossReferenceManager, HistoricalStore, SemanticNarrative, ErrorGroupManager, RecoveryManager, CostTracker) retain state across tests in the main describe block. Acceptance: add afterEach hook in main 'InMemoryEventStore' describe block that calls all available reset* functions; test suite passes with 0 timeouts.","design":"","acceptance_criteria":"","notes":"","status":"closed","priority":1,"issue_type":"task","assignee":"marathon","created_at":"2026-05-26T21:38:27.737513760Z","updated_at":"2026-05-26T21:40:03.272386257Z","closed_at":"2026-05-26T21:40:03.272386257Z","close_reason":"Added afterEach hook to main InMemoryEventStore describe block that calls all available reset* functions (resetStore, resetWorkerAnalytics, resetCrossReferenceManager, resetHistoricalStore, resetErrorGroupManager, resetRecoveryManager, resetCostTracker). Test suite now passes with 2484 tests passed, 0 failures. Type-check and build also pass. Commit 8d75e48 pushed.","source_repo":".","compaction_level":0}
{"id":"bf-5xch","title":"Web: Historical session index API and browser UI","description":"The HistoricalStore (src/historicalStore.ts) has getSessions(), getSession(), getSessionWorkerSummaries(), and getWorkerComparisonMetrics() — but the web server exposes none of these as API endpoints, and there is no web frontend component for browsing historical sessions.\n\nMissing backend (src/web/server.ts):\n- GET /api/sessions — list sessions (paginated, with start/end filter), calls historicalStore.getSessions()\n- GET /api/sessions/:id — get single session detail including per-worker summaries\n- GET /api/sessions/:id/workers — get worker summaries for a session via getSessionWorkerSummaries({sessionId})\n\nMissing frontend:\n- components/HistoricalSessionsPanel.tsx — list of past sessions with throughput/cost/worker-count summary, clicking a session shows per-worker stats\n- Wire into App.tsx (e.g., alongside Analytics toggle)\n\nThis fulfills plan.md Phase 6: Historical session index for comparisons.\n\nReference: historicalStore.ts getSessions(), getSession(), getSessionWorkerSummaries()","design":"","acceptance_criteria":"","notes":"","status":"closed","priority":2,"issue_type":"task","assignee":"marathon","created_at":"2026-05-26T04:04:00.284670337Z","updated_at":"2026-05-26T21:20:11.080721160Z","closed_at":"2026-05-26T21:20:11.080721160Z","close_reason":"Implemented Historical session index API and browser UI. Backend: GET /api/sessions, /api/sessions/:id, /api/sessions/:id/workers using HistoricalStore. Frontend: HistoricalSessionsPanel.tsx with session list table and click-to-expand worker performance breakdown. Integrated into App.tsx with Sessions toggle button and command palette action (show:sessions). All tests pass, builds succeed. Closes bf-5xch.","source_repo":".","compaction_level":0}
{"id":"bf-60j","title":"Add currentBead field to WorkerInfo (populate/clear from bead events)","description":"docs/plan.md Phase 9 checklist item 2. Add a currentBead field to WorkerInfo in src/types.ts and maintain it in src/store.ts: set it from bead.claim.succeeded (event.bead) and clear it (null) on bead.released. Verified absent 2026-05-22 (no currentBead in store.ts/types.ts). Foundation for the worker-card current-bead display. Update src/store.test.ts to cover claim->set and release->clear.","design":"","acceptance_criteria":"","notes":"","status":"closed","priority":1,"issue_type":"task","created_at":"2026-05-22T19:20:00.411233922Z","updated_at":"2026-05-22T19:25:34.098027497Z","closed_at":"2026-05-22T19:25:34.098027497Z","close_reason":"Completed","source_repo":".","compaction_level":0,"labels":["phase9"]}
{"id":"bf-izw3","title":"Fix: CommandPalette fuzzy search and public API broken (5 failing tests)","description":"5 tests in src/tui/components/CommandPalette.test.ts fail:\n\nFuzzy Search:\n- \"should show all suggestions when query is empty\" — getSuggestions('') returns empty instead of all defaults\n\nNavigation:\n- \"should wrap around when navigating past end\" — navigateDown() past last item does not wrap to index 0\n\nPublic API:\n- \"should add custom suggestions\" — addSuggestions() method missing or not working\n- \"should clear custom suggestions\" — clearSuggestions() method missing or not working\n- \"should set suggestions\" — setSuggestions() method missing or not working\n\nFix: implement/repair CommandPalette public API (addSuggestions, clearSuggestions, setSuggestions), fix empty-query to return all suggestions, fix navigation wrap-around.","design":"","acceptance_criteria":"","notes":"","status":"open","priority":1,"issue_type":"task","created_at":"2026-05-02T18:18:26.701247479Z","updated_at":"2026-05-02T20:55:25.639480990Z","source_repo":".","compaction_level":0}
{"id":"bf-7x4z","title":"Fix 13 TypeScript type errors in test files","description":"npx tsc --noEmit emits 13 errors, all in test files. Fix each:\n\n1. Multiple test files (tui/app.test.ts, WorkerDetail.e2e.test.ts, WorkerDetail.test.ts, WorkerGrid.e2e.test.ts, WorkerGrid.test.ts, regression.enhanced.test.ts, regression.test.ts, stuckDetection.test.ts) — all have:\n 'Types of property currentBead are incompatible: Type string | null | undefined is not assignable to type string | null'\n Test fixtures construct WorkerInfo with currentBead: undefined or omit it. The WorkerInfo type was updated to require string | null (not | undefined). Fix: either add currentBead: null to every fixture object that omits it, or update the WorkerInfo type to mark currentBead as optional (string | null | undefined) if undefined is semantically valid (check how the field is used in production code first).\n\n2. src/tui/components/SemanticNarrativePanel.test.ts — three errors:\n - 'string[] | undefined is not assignable to string[]' for accomplishments field\n - 'Mock is not assignable to (segmentId: string) => void' for a vi.fn() mock\n - missing required fields accomplishments, challenges, sentiment, stats, isLive, and missing workerId/events on NarrativeSegment\n Fix: add the missing required fields to the fixture objects, and cast the vi.fn() mock appropriately.\n\n3. src/beadWorkspaceScanner.test.ts — 'Element implicitly has any type because string can't index the mock object'. Fix: add an index signature to the mock or use type assertion.\n\nAcceptance: npx tsc --noEmit emits 0 errors.","design":"","acceptance_criteria":"","notes":"","status":"closed","priority":1,"issue_type":"task","assignee":"marathon","created_at":"2026-05-26T21:00:02.644660483Z","updated_at":"2026-05-26T21:11:58.279555620Z","closed_at":"2026-05-26T21:11:58.279555620Z","close_reason":"Fixed all 13 TypeScript type errors in test files. Added currentBead: null to 8 WorkerInfo fixtures, completed SemanticNarrative fixtures with all required fields (accomplishments, challenges, sentiment, stats, generatedAt, isLive), fixed NarrativeSegment fixtures with workerId and events, fixed onSelectCallback mock type assertion, added Record<string, string> index signature to mockBeadsData. npx tsc --noEmit now emits 0 errors. Committed 9b5e740 and pushed.","source_repo":".","compaction_level":0,"dependencies":[{"issue_id":"bf-7x4z","depends_on_id":"bf-1nah","type":"blocks","created_at":"2026-05-26T21:00:46.341875352Z","created_by":"batch","thread_id":""}]}
{"id":"bf-82u8","title":"plan-gap: Missing /api/spans/dag endpoint — SpanDag component exists but API is unimplemented","description":"Gap evidence: SpanDag.tsx fetches from /api/spans/dag (line 118) but this endpoint does not exist in src/web/server.ts. The component is tested (src/web/frontend/test/SpanDag.test.tsx) and has zoom/pan interactions implemented, but cannot function without the backend API.\n\nAcceptance: Implement /api/spans/dag endpoint in src/web/server.ts that returns span hierarchy data in the SpanDagResponse format. The endpoint should query OTLP span data from the store and return it as a tree structure. Add corresponding tests to verify the endpoint works correctly.","design":"","acceptance_criteria":"","notes":"","status":"closed","priority":1,"issue_type":"task","assignee":"marathon","created_at":"2026-05-26T23:36:03.223865423Z","updated_at":"2026-05-26T23:41:24.102251456Z","closed_at":"2026-05-26T23:41:24.102251456Z","close_reason":"Implemented /api/spans/dag endpoint in src/web/server.ts that returns span hierarchy data in SpanDagResponse format. Added SpanDagResponse interface to src/types.ts for JSON serialization. Updated SpanNode interface to use nullable fields (null instead of undefined) for consistency with web frontend types. Fixed src/dagUtils.ts to use nullable SpanNode fields. All 2484 tests pass. Type check passes.","source_repo":".","compaction_level":0}
{"id":"bf-izw3","title":"Fix: CommandPalette fuzzy search and public API broken (5 failing tests)","description":"5 tests in src/tui/components/CommandPalette.test.ts fail:\n\nFuzzy Search:\n- \"should show all suggestions when query is empty\" — getSuggestions('') returns empty instead of all defaults\n\nNavigation:\n- \"should wrap around when navigating past end\" — navigateDown() past last item does not wrap to index 0\n\nPublic API:\n- \"should add custom suggestions\" — addSuggestions() method missing or not working\n- \"should clear custom suggestions\" — clearSuggestions() method missing or not working\n- \"should set suggestions\" — setSuggestions() method missing or not working\n\nFix: implement/repair CommandPalette public API (addSuggestions, clearSuggestions, setSuggestions), fix empty-query to return all suggestions, fix navigation wrap-around.","design":"","acceptance_criteria":"","notes":"","status":"closed","priority":1,"issue_type":"task","created_at":"2026-05-02T18:18:26.701247479Z","updated_at":"2026-05-22T20:09:40.160119677Z","closed_at":"2026-05-22T20:09:40.160119677Z","close_reason":"Fixed 5 failing CommandPalette tests by correcting mock instance capture in test setup","source_repo":".","compaction_level":0}
{"id":"bf-m27d","title":"Implement: SpanDag web component zoom/pan interactions (13 failing tests)","description":"13 tests in src/web/frontend/test/SpanDag.test.tsx fail for zoom and pan:\n- \"should have zoom controls available\"\n- \"should display current zoom level\"\n- \"should zoom in when zoom in button clicked\"\n- \"should zoom out when zoom out button clicked\"\n- \"should zoom on mouse wheel\"\n- \"should respect minimum zoom limit (25%)\"\n- \"should respect maximum zoom limit (400%)\"\n- \"should reset zoom and pan when reset button clicked\"\n- \"should show reset button when zoomed or panned\"\n- \"should pan on mouse drag\"\n- \"should start dragging on mouse down\"\n- \"should stop dragging on mouse up\"\n- \"should stop dragging on mouse leave\"\n- \"should show grab cursor when zoomed or panned\"\n- \"should handle middle mouse button for panning\"\n- \"should display trace count\" (Stats Bar)\n\nThe plan (feature 9) describes: \"Web version uses interactive SVG/Canvas with drag to pan, scroll to zoom, click to select, hover for details.\" Bead bd-i35 (Enhance web DAG with zoom and pan) was closed but tests show the feature is missing.\n\nFix: add zoom/pan state to SpanDag.tsx — zoom in/out buttons, mouse wheel zoom, drag-to-pan with mousedown/mousemove/mouseup/mouseleave handlers, middle-mouse pan, zoom limits (25%400%), reset button (shown when zoomed/panned), grab cursor styling, trace count stat display.","design":"","acceptance_criteria":"","notes":"","status":"closed","priority":2,"issue_type":"task","created_at":"2026-05-02T18:19:12.741828454Z","updated_at":"2026-05-02T20:50:07.586761150Z","closed_at":"2026-05-02T20:50:07.586761150Z","close_reason":"All zoom/pan interaction features already implemented and tested (35/35 tests passing in SpanDag.test.tsx).\n\n## Retrospective\n- **What worked:** Code review confirmed SpanDag.tsx has complete zoom/pan implementation (lines 103-334) with zoom controls, mouse wheel zoom, drag-to-pan, zoom limits, reset button, and cursor styling\n- **What didn't:** Bead was created based on stale test failure information\n- **Surprise:** Tests were already passing when bead was created - the feature gap was already closed in earlier commits\n- **Reusable pattern:** Verify test status before creating gap closure beads","source_repo":".","compaction_level":0}
{"id":"bf-ozsu","title":"Update plan.md: check off completed Phase 4-7 items","description":"plan.md still shows 33 unchecked items for phases 4-7, but nearly all of these features are fully implemented. The checklist has not been updated since the phases were built out.\n\nFeatures in plan.md marked [ ] that are confirmed implemented in code:\n- Cross-reference hyperlinking (CrossReferencePanel, /api/xref/*)\n- Inline diff view for Edit tool calls (DiffView.ts, DiffView.tsx)\n- File activity heatmap (FileHeatmap.ts, FileHeatmap.tsx, /api/heatmap)\n- Cost & token tracking dashboard (CostDashboard.tsx, /api/cost/*)\n- Conversation transcript view (ConversationTranscriptPanel.tsx, /api/conversations/*)\n- Stuck detection (stuckDetection.ts)\n- Loop detection (fileAnomalyDetection.ts)\n- Worker collision detection (CollisionAlert, /api/collisions)\n- Smart error grouping (errorGrouping.ts, ErrorGroupPanel, /api/errors/*)\n- Semantic activity narrative (semanticNarrative.ts, SemanticNarrativePanel, /api/narrative)\n- Git integration panel (gitParser.ts, GitIntegrationPanel, /api/git/status)\n- AI session digest (sessionDigest.ts, SessionDigestPanel, /api/digest)\n- Session replay with timeline scrubbing (SessionReplay.ts, SessionReplay.tsx)\n- Task dependency DAG (dagUtils.ts, DependencyDag, /api/dag)\n- Budget alerts and projections (BudgetAlertPanel, /api/cost/alerts)\n- Anomaly detection (fileAnomalyDetection.ts)\n- Recovery playbook (recoveryPlaybook.ts, RecoveryPanel, /api/recovery/*)\n- Focus mode with pinning (focusPresets.ts, FocusPresetManager in App.tsx and app.ts)\n\nRemaining true gap (separate bead): Worker comparison analytics and Historical session index.\n\nAction: mark all completed items with [x] in plan.md Implementation Phases section.","design":"","acceptance_criteria":"","notes":"","status":"closed","priority":3,"issue_type":"task","assignee":"marathon","created_at":"2026-05-26T04:04:14.538259815Z","updated_at":"2026-05-26T21:24:52.406014978Z","closed_at":"2026-05-26T21:24:52.406014978Z","close_reason":"Updated plan.md to check off 18 completed items across Phases 4-7: Phase 4 (5/5 complete), Phase 5 (5/5 complete), Phase 6 (3/4 complete, worker comparison analytics web layer is bf-4cqq), Phase 7 (5/5 complete). Verified implementations in code: CrossReferencePanel, DiffView, FileHeatmap, CostDashboard, ConversationTranscriptPanel, stuckDetection, fileAnomalyDetection, CollisionAlert, errorGrouping, semanticNarrative, GitIntegration, sessionDigest, historicalStore, SessionReplay, DependencyDag, BudgetAlertPanel, RecoveryPanel. Committed 08b1b5a, pushed.","source_repo":".","compaction_level":0}

View file

@ -1 +1 @@
2ea64081e5d2c7b47eeab7fee8a9948dfa750e41
aec0137a11b3ee8414ca35c2ef63dfbdf546a165

View file

@ -99,16 +99,21 @@ export class DirectoryTailer extends EventEmitter {
const now = Date.now();
const candidates: Array<{ fullPath: string; mtime: number; size: number }> = [];
// Files modified within this window are read from the start on startup so
// FABRIC can reconstruct current worker state after a restart.
const STARTUP_REREAD_MS = 4 * 60 * 60 * 1000; // 4 hours
for (const entry of fs.readdirSync(this.directory)) {
if (!entry.endsWith('.jsonl')) continue;
const fullPath = path.join(this.directory, entry);
try {
const stat = fs.statSync(fullPath);
// Register all files; position = stat.size so initial activation
// starts from the current end (don't re-emit historical events).
const isRecent = now - stat.mtimeMs <= STARTUP_REREAD_MS;
// Recent files: read from the beginning so state is reconstructed.
// Old files: start at EOF — don't replay ancient history on restart.
this.fileInfo.set(fullPath, {
mtime: stat.mtimeMs,
position: stat.size,
position: isRecent ? 0 : stat.size,
lastActivity: 0,
});
if (now - stat.mtimeMs <= this.recentMtimeMs) {

View file

@ -741,7 +741,7 @@ export class InMemoryEventStore implements EventStore {
* Check if a string is a valid NeedleState value.
*/
private isValidNeedleState(value: string): value is NeedleState {
return ['BOOTING', 'SELECTING', 'CLAIMING', 'WORKING', 'CLOSING', 'STOPPED'].includes(value);
return ['BOOTING', 'SELECTING', 'CLAIMING', 'WORKING', 'BUILDING', 'DISPATCHING', 'EXECUTING', 'HANDLING', 'LOGGING', 'CLOSING', 'EXHAUSTED_IDLE', 'STOPPED'].includes(value);
}
/**

View file

@ -46,12 +46,18 @@ export function getStatusColor(status: 'active' | 'idle' | 'error'): string {
export function getNeedleStateColor(state: NeedleState): string {
const c = getColors();
switch (state) {
case 'BOOTING': return c.info;
case 'SELECTING': return c.warn;
case 'CLAIMING': return 'magenta';
case 'WORKING': return c.active;
case 'CLOSING': return c.warn;
case 'STOPPED': return c.idle;
case 'BOOTING': return c.info;
case 'SELECTING': return c.warn;
case 'CLAIMING': return 'magenta';
case 'BUILDING':
case 'DISPATCHING':
case 'EXECUTING':
case 'HANDLING':
case 'LOGGING':
case 'WORKING': return c.active;
case 'CLOSING': return c.warn;
case 'EXHAUSTED_IDLE': return c.idle;
case 'STOPPED': return c.idle;
}
}
@ -60,12 +66,18 @@ export function getNeedleStateColor(state: NeedleState): string {
*/
export function getNeedleStateIcon(state: NeedleState): string {
switch (state) {
case 'BOOTING': return '⏳';
case 'SELECTING': return '🔍';
case 'CLAIMING': return '🎯';
case 'WORKING': return '●';
case 'CLOSING': return '⏹';
case 'STOPPED': return '○';
case 'BOOTING': return '⏳';
case 'SELECTING': return '🔍';
case 'CLAIMING': return '🎯';
case 'BUILDING':
case 'DISPATCHING':
case 'EXECUTING':
case 'HANDLING':
case 'LOGGING':
case 'WORKING': return '●';
case 'CLOSING': return '⏹';
case 'EXHAUSTED_IDLE': return '○';
case 'STOPPED': return '○';
}
}

View file

@ -20,27 +20,42 @@ export type WorkerStatus = 'active' | 'idle' | 'error';
* worker.state_transition events. These are the canonical states
* that replace the coarse WorkerStatus in the UI.
*
* BOOTING SELECTING CLAIMING WORKING CLOSING STOPPED
* Abstract: BOOTING SELECTING CLAIMING WORKING CLOSING STOPPED
* Granular: SELECTING BUILDING DISPATCHING EXECUTING HANDLING LOGGING SELECTING
*
* BUILDING/DISPATCHING/EXECUTING/HANDLING/LOGGING are the inner loop of a
* single bead execution. They all map to the WORKING display state.
*/
export type NeedleState =
| 'BOOTING'
| 'SELECTING'
| 'CLAIMING'
| 'WORKING'
| 'BUILDING'
| 'DISPATCHING'
| 'EXECUTING'
| 'HANDLING'
| 'LOGGING'
| 'CLOSING'
| 'EXHAUSTED_IDLE'
| 'STOPPED';
/**
* All valid state transitions in the NEEDLE worker state machine.
* A worker in BOOTING can only go to SELECTING, etc.
*/
export const VALID_TRANSITIONS: Record<NeedleState, NeedleState[]> = {
BOOTING: ['SELECTING'],
SELECTING: ['CLAIMING', 'STOPPED'],
CLAIMING: ['WORKING', 'SELECTING'],
WORKING: ['CLOSING', 'SELECTING'],
CLOSING: ['SELECTING', 'STOPPED'],
STOPPED: ['BOOTING'],
export const VALID_TRANSITIONS: Partial<Record<NeedleState, NeedleState[]>> = {
BOOTING: ['SELECTING'],
SELECTING: ['BUILDING', 'CLAIMING', 'EXHAUSTED_IDLE', 'STOPPED'],
CLAIMING: ['WORKING', 'SELECTING'],
BUILDING: ['DISPATCHING', 'SELECTING'],
DISPATCHING: ['EXECUTING', 'SELECTING'],
EXECUTING: ['HANDLING', 'SELECTING'],
HANDLING: ['LOGGING', 'SELECTING'],
LOGGING: ['SELECTING'],
WORKING: ['CLOSING', 'SELECTING'],
CLOSING: ['SELECTING', 'STOPPED'],
EXHAUSTED_IDLE: ['SELECTING', 'STOPPED'],
STOPPED: ['BOOTING'],
};
/**
@ -51,10 +66,16 @@ export function needleStateToStatus(state: NeedleState): WorkerStatus {
case 'BOOTING':
case 'SELECTING':
case 'CLAIMING':
case 'BUILDING':
case 'DISPATCHING':
case 'EXECUTING':
case 'HANDLING':
case 'LOGGING':
case 'WORKING':
return 'active';
case 'CLOSING':
return 'active';
case 'EXHAUSTED_IDLE':
case 'STOPPED':
return 'idle';
}

View file

@ -398,7 +398,7 @@ const App: React.FC = () => {
if (existing) {
return prev.map(w => w.id === event.worker ? {
...w,
lastSeen: event.timestamp,
lastSeen: event.timestamp ?? (event.ts ? new Date(event.ts).toISOString() : undefined),
eventCount: w.eventCount + 1,
status: 'active' as const,
currentTool: event.tool,
@ -407,7 +407,7 @@ const App: React.FC = () => {
} else {
return [...prev, {
id: event.worker,
lastSeen: event.timestamp,
lastSeen: event.timestamp ?? (event.ts ? new Date(event.ts).toISOString() : undefined),
eventCount: 1,
status: 'active' as const,
currentTool: event.tool,

View file

@ -22,7 +22,7 @@ const FleetSummaryBar: React.FC<FleetSummaryBarProps> = ({ workers }) => {
}
return {
working: stateCounts.WORKING || 0,
working: (stateCounts.WORKING || 0) + (stateCounts.BUILDING || 0) + (stateCounts.DISPATCHING || 0) + (stateCounts.EXECUTING || 0) + (stateCounts.HANDLING || 0) + (stateCounts.LOGGING || 0),
selecting: stateCounts.SELECTING || 0,
exhausted: (stateCounts.EXHAUSTED_IDLE || 0) + (stateCounts.STOPPED || 0),
beadsToday: totalBeadsCompleted,

View file

@ -5,6 +5,11 @@ const NEEDLE_STATE_LABELS: Record<NeedleState, string> = {
BOOTING: 'BOOTING',
SELECTING: 'SELECTING',
CLAIMING: 'CLAIMING',
BUILDING: 'WORKING',
DISPATCHING: 'WORKING',
EXECUTING: 'WORKING',
HANDLING: 'WORKING',
LOGGING: 'WORKING',
WORKING: 'WORKING',
CLOSING: 'CLOSING',
EXHAUSTED_IDLE: 'EXHAUSTED',
@ -15,6 +20,11 @@ const NEEDLE_STATE_COLORS: Record<NeedleState, string> = {
BOOTING: '#5bc0de',
SELECTING: '#f0ad4e',
CLAIMING: '#9b59b6',
BUILDING: '#5cb85c',
DISPATCHING: '#5cb85c',
EXECUTING: '#5cb85c',
HANDLING: '#5cb85c',
LOGGING: '#5cb85c',
WORKING: '#5cb85c',
CLOSING: '#f0ad4e',
EXHAUSTED_IDLE: '#95a5a6',
@ -23,7 +33,12 @@ const NEEDLE_STATE_COLORS: Record<NeedleState, string> = {
// Lower number = higher priority (shown first)
const NEEDLE_STATE_PRIORITY: Partial<Record<string, number>> = {
EXECUTING: 0,
WORKING: 0,
BUILDING: 0,
DISPATCHING: 0,
HANDLING: 0,
LOGGING: 0,
CLAIMING: 1,
SELECTING: 2,
BOOTING: 3,

View file

@ -5,6 +5,11 @@ export type NeedleState =
| 'SELECTING'
| 'CLAIMING'
| 'WORKING'
| 'BUILDING'
| 'DISPATCHING'
| 'EXECUTING'
| 'HANDLING'
| 'LOGGING'
| 'CLOSING'
| 'EXHAUSTED_IDLE'
| 'STOPPED';