From e4d737809631f9e6e28acd37f2d98b57ccf818c7 Mon Sep 17 00:00:00 2001 From: jedarden Date: Tue, 26 May 2026 22:18:56 -0400 Subject: [PATCH] feat(types): add granular NEEDLE worker states + directory tailer startup re-read - 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 --- .beads/issues.jsonl | 28 ++++++++----- .needle-predispatch-sha | 2 +- src/directoryTailer.ts | 11 ++++-- src/store.ts | 2 +- src/tui/utils/colors.ts | 36 +++++++++++------ src/types.ts | 39 ++++++++++++++----- src/web/frontend/src/App.tsx | 4 +- .../src/components/FleetSummaryBar.tsx | 2 +- .../frontend/src/components/WorkerGrid.tsx | 15 +++++++ src/web/frontend/src/types.ts | 5 +++ 10 files changed, 106 insertions(+), 38 deletions(-) diff --git a/.beads/issues.jsonl b/.beads/issues.jsonl index bda4821..7ae2dd5 100644 --- a/.beads/issues.jsonl +++ b/.beads/issues.jsonl @@ -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 1–8 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 1–8: 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 1–8 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 1–8: 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=&worker2= — 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 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} diff --git a/.needle-predispatch-sha b/.needle-predispatch-sha index cab0916..e9c5e1a 100644 --- a/.needle-predispatch-sha +++ b/.needle-predispatch-sha @@ -1 +1 @@ -2ea64081e5d2c7b47eeab7fee8a9948dfa750e41 +aec0137a11b3ee8414ca35c2ef63dfbdf546a165 diff --git a/src/directoryTailer.ts b/src/directoryTailer.ts index 1390937..429c5dc 100644 --- a/src/directoryTailer.ts +++ b/src/directoryTailer.ts @@ -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) { diff --git a/src/store.ts b/src/store.ts index 7cb6b5f..db334ae 100644 --- a/src/store.ts +++ b/src/store.ts @@ -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); } /** diff --git a/src/tui/utils/colors.ts b/src/tui/utils/colors.ts index 646957e..1f6a0b6 100644 --- a/src/tui/utils/colors.ts +++ b/src/tui/utils/colors.ts @@ -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 '○'; } } diff --git a/src/types.ts b/src/types.ts index 75d5ab0..4ef5d37 100644 --- a/src/types.ts +++ b/src/types.ts @@ -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 = { - BOOTING: ['SELECTING'], - SELECTING: ['CLAIMING', 'STOPPED'], - CLAIMING: ['WORKING', 'SELECTING'], - WORKING: ['CLOSING', 'SELECTING'], - CLOSING: ['SELECTING', 'STOPPED'], - STOPPED: ['BOOTING'], +export const VALID_TRANSITIONS: Partial> = { + 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'; } diff --git a/src/web/frontend/src/App.tsx b/src/web/frontend/src/App.tsx index 0176fde..36d1302 100644 --- a/src/web/frontend/src/App.tsx +++ b/src/web/frontend/src/App.tsx @@ -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, diff --git a/src/web/frontend/src/components/FleetSummaryBar.tsx b/src/web/frontend/src/components/FleetSummaryBar.tsx index e730156..7d2d318 100644 --- a/src/web/frontend/src/components/FleetSummaryBar.tsx +++ b/src/web/frontend/src/components/FleetSummaryBar.tsx @@ -22,7 +22,7 @@ const FleetSummaryBar: React.FC = ({ 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, diff --git a/src/web/frontend/src/components/WorkerGrid.tsx b/src/web/frontend/src/components/WorkerGrid.tsx index a8f11fc..198e5a1 100644 --- a/src/web/frontend/src/components/WorkerGrid.tsx +++ b/src/web/frontend/src/components/WorkerGrid.tsx @@ -5,6 +5,11 @@ const NEEDLE_STATE_LABELS: Record = { 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 = { 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 = { // Lower number = higher priority (shown first) const NEEDLE_STATE_PRIORITY: Partial> = { + EXECUTING: 0, WORKING: 0, + BUILDING: 0, + DISPATCHING: 0, + HANDLING: 0, + LOGGING: 0, CLAIMING: 1, SELECTING: 2, BOOTING: 3, diff --git a/src/web/frontend/src/types.ts b/src/web/frontend/src/types.ts index 301ba21..a8f740f 100644 --- a/src/web/frontend/src/types.ts +++ b/src/web/frontend/src/types.ts @@ -5,6 +5,11 @@ export type NeedleState = | 'SELECTING' | 'CLAIMING' | 'WORKING' + | 'BUILDING' + | 'DISPATCHING' + | 'EXECUTING' + | 'HANDLING' + | 'LOGGING' | 'CLOSING' | 'EXHAUSTED_IDLE' | 'STOPPED';