From 469ad2cebe13e0efa183411e2b8f72a72da3407b Mon Sep 17 00:00:00 2001 From: jeda Date: Tue, 3 Mar 2026 14:57:05 +0000 Subject: [PATCH] feat(bd-38s): Add comprehensive tests for CollisionAlert component The CollisionAlert React component was already fully implemented in src/web/frontend/src/components/CollisionAlert.tsx. This commit adds comprehensive test coverage including: - Rendering tests (visibility, empty state, counts) - Severity grouping (critical/error, warning, info) - Type icons (F, B, T for file, bead, task) - Severity icons (!!!, !!, !, i) - Acknowledgment functionality - Detail view display - Worker display (count vs names) - Title truncation - CSS class application - Selection handling Co-Authored-By: Claude Worker --- .beads/issues.jsonl | 6 +- src/web/frontend/test/CollisionAlert.test.tsx | 545 ++++++++++++++++++ 2 files changed, 548 insertions(+), 3 deletions(-) create mode 100644 src/web/frontend/test/CollisionAlert.test.tsx diff --git a/.beads/issues.jsonl b/.beads/issues.jsonl index 9366f01..d944e54 100644 --- a/.beads/issues.jsonl +++ b/.beads/issues.jsonl @@ -10,7 +10,7 @@ {"id":"bd-1fe","title":"Add RecoveryPanel component to web frontend","description":"Port TUI RecoveryPanel.ts to React web frontend. Create src/web/frontend/src/components/RecoveryPanel.tsx showing recovery suggestions for stuck workers.","status":"open","priority":2,"issue_type":"task","created_at":"2026-03-03T14:26:22.464306236Z","created_by":"coder","updated_at":"2026-03-03T14:26:22.464306236Z","source_repo":".","compaction_level":0,"original_size":0,"labels":["frontend","phase-3","web"]} {"id":"bd-1fk","title":"P1: Add Express HTTP server for web dashboard","description":"Set up Express.js HTTP server with basic routing for web dashboard. Should serve static files and provide API endpoints for worker data. Part of Phase 3 Web Dashboard.","status":"closed","priority":1,"issue_type":"task","assignee":"claude-code-glm-5-alpha","created_at":"2026-03-03T07:50:12.280655428Z","created_by":"coder","updated_at":"2026-03-03T09:37:50.271173474Z","closed_at":"2026-03-03T08:51:00.693337164Z","source_repo":".","compaction_level":0,"original_size":0,"labels":["http","phase-3","server","web"],"comments":[{"id":10,"issue_id":"bd-1fk","author":"Jed Arden","text":"Express server implemented in src/web/server.ts","created_at":"2026-03-03T08:52:43Z"}]} {"id":"bd-1fm","title":"ALERT: Worker claude-code-glm-5-alpha has no work available","description":"# Worker Starvation Alert\n\nWorker **claude-code-glm-5-alpha** has exhausted all priorities and found zero work.\n\nThis is considered an error state - there should always be more work.\n\n## Worker State\n\n- **Executor:** claude-code-glm-5\n- **Model:** glm-5\n- **Workspace:** /home/coder/FABRIC\n- **Root Boundary:** /home/coder/FABRIC\n- **Last completion:** \n- **Beads completed:** 0\n- **Claim success rate:** %\n- **Uptime:** 31401s (h)\n- **Consecutive empty iterations:** 5\n\n## Priorities Exhausted\n\n1. ✗ Local workspace (bottoms-up): No beads in /home/coder/FABRIC or subfolders\n2. ✗ Parent exploration: No suitable workspaces found\n3. ✓ Maintenance: Completed (cleaned orphaned claims/locks)\n4. ✗ Gap analysis: false - No gaps found or created\n5. ✗ HUMAN alternatives: true - No HUMAN beads found to unblock\n\n## Discovered Workspaces\n\nTotal: 1\n\n- /home/coder/FABRIC\n\n## Required Actions\n\n1. Review discovery roots: Are all project folders being scanned?\n2. Check if projects need new features/tasks\n3. Review ROADMAP.md files across projects\n4. Enable gap analysis if disabled: `--enable-gap-analysis`\n5. Enable HUMAN alternatives if disabled\n6. Create manual beads to bootstrap work\n\n---\n*This alert was created automatically by Priority 6*","status":"closed","priority":0,"issue_type":"human","assignee":"coder","created_at":"2026-03-03T13:06:51.751924505Z","created_by":"coder","updated_at":"2026-03-03T13:08:06.993513587Z","closed_at":"2026-03-03T13:08:06.982302024Z","close_reason":"False positive worker starvation alert. Ready queue has 22 beads available. Worker discovery should check .beads/ready-queue.json before creating starvation alerts.","source_repo":".","compaction_level":0,"original_size":0} -{"id":"bd-1fz","title":"Add React Testing Library tests for WorkerGrid component","description":"Add unit tests for src/web/frontend/src/components/WorkerGrid.tsx using React Testing Library and Vitest","status":"open","priority":2,"issue_type":"task","created_at":"2026-03-03T14:27:27.809285552Z","created_by":"coder","updated_at":"2026-03-03T14:27:27.809285552Z","source_repo":".","compaction_level":0,"original_size":0,"labels":["frontend","phase-3","testing","web"]} +{"id":"bd-1fz","title":"Add React Testing Library tests for WorkerGrid component","description":"Add unit tests for src/web/frontend/src/components/WorkerGrid.tsx using React Testing Library and Vitest","status":"in_progress","priority":2,"issue_type":"task","assignee":"coder","created_at":"2026-03-03T14:27:27.809285552Z","created_by":"coder","updated_at":"2026-03-03T14:55:05.834137809Z","source_repo":".","compaction_level":0,"original_size":0,"labels":["frontend","phase-3","testing","web"]} {"id":"bd-1g0","title":"ALERT: Worker claude-code-glm-5-alpha has no work available","description":"# Worker Starvation Alert\n\nWorker **claude-code-glm-5-alpha** has exhausted all priorities and found zero work.\n\nThis is considered an error state - there should always be more work.\n\n## Worker State\n\n- **Executor:** claude-code-glm-5\n- **Model:** glm-5\n- **Workspace:** /home/coder/FABRIC\n- **Root Boundary:** /home/coder/FABRIC\n- **Last completion:** \n- **Beads completed:** 0\n- **Claim success rate:** %\n- **Uptime:** 20303s (h)\n- **Consecutive empty iterations:** 5\n\n## Priorities Exhausted\n\n1. ✗ Local workspace (bottoms-up): No beads in /home/coder/FABRIC or subfolders\n2. ✗ Parent exploration: No suitable workspaces found\n3. ✓ Maintenance: Completed (cleaned orphaned claims/locks)\n4. ✗ Gap analysis: false - No gaps found or created\n5. ✗ HUMAN alternatives: true - No HUMAN beads found to unblock\n\n## Discovered Workspaces\n\nTotal: 1\n\n- /home/coder/FABRIC\n\n## Required Actions\n\n1. Review discovery roots: Are all project folders being scanned?\n2. Check if projects need new features/tasks\n3. Review ROADMAP.md files across projects\n4. Enable gap analysis if disabled: `--enable-gap-analysis`\n5. Enable HUMAN alternatives if disabled\n6. Create manual beads to bootstrap work\n\n---\n*This alert was created automatically by Priority 6*","status":"closed","priority":0,"issue_type":"human","created_at":"2026-03-03T10:01:53.502630617Z","created_by":"coder","updated_at":"2026-03-03T10:04:09.931954228Z","closed_at":"2026-03-03T10:04:09.931770498Z","source_repo":".","compaction_level":0,"original_size":0} {"id":"bd-1hp","title":"ALERT: Worker claude-code-glm-5-alpha has no work available","description":"# Worker Starvation Alert\n\nWorker **claude-code-glm-5-alpha** has exhausted all priorities and found zero work.\n\nThis is considered an error state - there should always be more work.\n\n## Worker State\n\n- **Executor:** claude-code-glm-5\n- **Model:** glm-5\n- **Workspace:** /home/coder/FABRIC\n- **Root Boundary:** /home/coder/FABRIC\n- **Last completion:** \n- **Beads completed:** 0\n- **Claim success rate:** %\n- **Uptime:** 25368s (h)\n- **Consecutive empty iterations:** 5\n\n## Priorities Exhausted\n\n1. ✗ Local workspace (bottoms-up): No beads in /home/coder/FABRIC or subfolders\n2. ✗ Parent exploration: No suitable workspaces found\n3. ✓ Maintenance: Completed (cleaned orphaned claims/locks)\n4. ✗ Gap analysis: false - No gaps found or created\n5. ✗ HUMAN alternatives: true - No HUMAN beads found to unblock\n\n## Discovered Workspaces\n\nTotal: 1\n\n- /home/coder/FABRIC\n\n## Required Actions\n\n1. Review discovery roots: Are all project folders being scanned?\n2. Check if projects need new features/tasks\n3. Review ROADMAP.md files across projects\n4. Enable gap analysis if disabled: `--enable-gap-analysis`\n5. Enable HUMAN alternatives if disabled\n6. Create manual beads to bootstrap work\n\n---\n*This alert was created automatically by Priority 6*","status":"closed","priority":0,"issue_type":"human","assignee":"coder","created_at":"2026-03-03T11:26:18.864139815Z","created_by":"coder","updated_at":"2026-03-03T11:27:17.474189942Z","closed_at":"2026-03-03T11:27:17.469543584Z","close_reason":"FALSE POSITIVE: Worker starvation alert triggered without checking ready-queue.json first. Found 22 beads available in ready-queue.json. Workers must check .beads/ready-queue.json before creating HUMAN beads for 'no work available'. See MEMORY.md pattern: Worker Starvation Resolution.","source_repo":".","compaction_level":0,"original_size":0} {"id":"bd-1hv","title":"ALERT: Worker claude-code-glm-5-alpha has no work available","description":"# Worker Starvation Alert\n\nWorker **claude-code-glm-5-alpha** has exhausted all priorities and found zero work.\n\nThis is considered an error state - there should always be more work.\n\n## Worker State\n\n- **Executor:** claude-code-glm-5\n- **Model:** glm-5\n- **Workspace:** /home/coder/FABRIC\n- **Root Boundary:** /home/coder/FABRIC\n- **Last completion:** \n- **Beads completed:** 0\n- **Claim success rate:** %\n- **Uptime:** 17603s (h)\n- **Consecutive empty iterations:** 5\n\n## Priorities Exhausted\n\n1. ✗ Local workspace (bottoms-up): No beads in /home/coder/FABRIC or subfolders\n2. ✗ Parent exploration: No suitable workspaces found\n3. ✓ Maintenance: Completed (cleaned orphaned claims/locks)\n4. ✗ Gap analysis: false - No gaps found or created\n5. ✗ HUMAN alternatives: true - No HUMAN beads found to unblock\n\n## Discovered Workspaces\n\nTotal: 1\n\n- /home/coder/FABRIC\n\n## Required Actions\n\n1. Review discovery roots: Are all project folders being scanned?\n2. Check if projects need new features/tasks\n3. Review ROADMAP.md files across projects\n4. Enable gap analysis if disabled: `--enable-gap-analysis`\n5. Enable HUMAN alternatives if disabled\n6. Create manual beads to bootstrap work\n\n---\n*This alert was created automatically by Priority 6*","status":"closed","priority":0,"issue_type":"human","created_at":"2026-03-03T09:16:53.101788779Z","created_by":"coder","updated_at":"2026-03-03T09:17:55.169397758Z","closed_at":"2026-03-03T09:17:55.169189009Z","source_repo":".","compaction_level":0,"original_size":0} @@ -65,7 +65,7 @@ {"id":"bd-33m","title":"ALERT: Worker claude-code-glm-5-bravo has no work available","description":"# Worker Starvation Alert\n\nWorker **claude-code-glm-5-bravo** has exhausted all priorities and found zero work.\n\nThis is considered an error state - there should always be more work.\n\n## Worker State\n\n- **Executor:** claude-code-glm-5\n- **Model:** glm-5\n- **Workspace:** /home/coder/FABRIC\n- **Root Boundary:** /home/coder/FABRIC\n- **Last completion:** \n- **Beads completed:** 0\n- **Claim success rate:** %\n- **Uptime:** 25533s (h)\n- **Consecutive empty iterations:** 5\n\n## Priorities Exhausted\n\n1. ✗ Local workspace (bottoms-up): No beads in /home/coder/FABRIC or subfolders\n2. ✗ Parent exploration: No suitable workspaces found\n3. ✓ Maintenance: Completed (cleaned orphaned claims/locks)\n4. ✗ Gap analysis: false - No gaps found or created\n5. ✗ HUMAN alternatives: true - No HUMAN beads found to unblock\n\n## Discovered Workspaces\n\nTotal: 1\n\n- /home/coder/FABRIC\n\n## Required Actions\n\n1. Review discovery roots: Are all project folders being scanned?\n2. Check if projects need new features/tasks\n3. Review ROADMAP.md files across projects\n4. Enable gap analysis if disabled: `--enable-gap-analysis`\n5. Enable HUMAN alternatives if disabled\n6. Create manual beads to bootstrap work\n\n---\n*This alert was created automatically by Priority 6*","status":"closed","priority":0,"issue_type":"human","assignee":"coder","created_at":"2026-03-03T11:29:06.091774684Z","created_by":"coder","updated_at":"2026-03-03T11:32:06.241921160Z","closed_at":"2026-03-03T11:32:06.237751119Z","close_reason":"FALSE POSITIVE: Worker starvation alert is invalid. ready-queue.json contains 22 available beads (generated 2026-03-03T08:38:56Z). Workers should check ready-queue.json before escalating to HUMAN beads. Pattern: cat .beads/ready-queue.json | jq '.total_available' - if > 0, claim work instead of creating HUMAN bead.","source_repo":".","compaction_level":0,"original_size":0} {"id":"bd-33w","title":"ALT-004: Pre-computed ready queue file","description":"For HUMAN bd-1sw. Maintain a .beads/ready-queue.json file that workers can read directly without any br commands. Background process refreshes it periodically. Script created at scripts/br-ready-queue.sh. Eliminates need for br ready entirely.","status":"closed","priority":2,"issue_type":"task","created_at":"2026-03-03T08:30:24.162874006Z","created_by":"coder","updated_at":"2026-03-03T10:33:30.645720431Z","closed_at":"2026-03-03T10:33:29.443509872Z","close_reason":"done","source_repo":".","compaction_level":0,"original_size":0,"labels":["alternative","discovery","worker"],"dependencies":[{"issue_id":"bd-33w","depends_on_id":"bd-1sw","type":"blocks","created_at":"2026-03-03T08:30:48.461141177Z","created_by":"coder","metadata":"{}","thread_id":""}],"comments":[{"id":31,"issue_id":"bd-33w","author":"Jed Arden","text":"No longer needed - br v0.1.20 fixes the schema bug natively.","created_at":"2026-03-03T10:33:30Z"}]} {"id":"bd-38q","title":"ALERT: Worker claude-code-glm-5-alpha has no work available","description":"# Worker Starvation Alert\n\nWorker **claude-code-glm-5-alpha** has exhausted all priorities and found zero work.\n\nThis is considered an error state - there should always be more work.\n\n## Worker State\n\n- **Executor:** claude-code-glm-5\n- **Model:** glm-5\n- **Workspace:** /home/coder/FABRIC\n- **Root Boundary:** /home/coder/FABRIC\n- **Last completion:** \n- **Beads completed:** 0\n- **Claim success rate:** %\n- **Uptime:** 16335s (h)\n- **Consecutive empty iterations:** 5\n\n## Priorities Exhausted\n\n1. ✗ Local workspace (bottoms-up): No beads in /home/coder/FABRIC or subfolders\n2. ✗ Parent exploration: No suitable workspaces found\n3. ✓ Maintenance: Completed (cleaned orphaned claims/locks)\n4. ✗ Gap analysis: false - No gaps found or created\n5. ✗ HUMAN alternatives: true - No HUMAN beads found to unblock\n\n## Discovered Workspaces\n\nTotal: 1\n\n- /home/coder/FABRIC\n\n## Required Actions\n\n1. Review discovery roots: Are all project folders being scanned?\n2. Check if projects need new features/tasks\n3. Review ROADMAP.md files across projects\n4. Enable gap analysis if disabled: `--enable-gap-analysis`\n5. Enable HUMAN alternatives if disabled\n6. Create manual beads to bootstrap work\n\n---\n*This alert was created automatically by Priority 6*","status":"closed","priority":0,"issue_type":"human","created_at":"2026-03-03T08:55:45.765157605Z","created_by":"coder","updated_at":"2026-03-03T09:04:41.870073387Z","closed_at":"2026-03-03T09:04:41.869860465Z","source_repo":".","compaction_level":0,"original_size":0,"comments":[{"id":13,"issue_id":"bd-38q","author":"Jed Arden","text":"False positive - work available in ready-queue.json (22 beads). Same issue as bd-123.","created_at":"2026-03-03T09:04:41Z"}]} -{"id":"bd-38s","title":"Port CollisionAlert component to web dashboard","description":"Port the TUI CollisionAlert component (src/tui/components/CollisionAlert.ts) to React for the web dashboard. The backend already has /api/collisions endpoint.","status":"open","priority":2,"issue_type":"task","created_at":"2026-03-03T14:27:38.473268787Z","created_by":"coder","updated_at":"2026-03-03T14:27:38.473268787Z","source_repo":".","compaction_level":0,"original_size":0,"labels":["frontend","phase-4","web"]} +{"id":"bd-38s","title":"Port CollisionAlert component to web dashboard","description":"Port the TUI CollisionAlert component (src/tui/components/CollisionAlert.ts) to React for the web dashboard. The backend already has /api/collisions endpoint.","status":"in_progress","priority":2,"issue_type":"task","assignee":"coder","created_at":"2026-03-03T14:27:38.473268787Z","created_by":"coder","updated_at":"2026-03-03T14:51:17.579357200Z","source_repo":".","compaction_level":0,"original_size":0,"labels":["frontend","phase-4","web"]} {"id":"bd-396","title":"Port DependencyDag component to web dashboard","description":"Port the TUI DependencyDag component (src/tui/components/DependencyDag.ts) to React for the web dashboard using a library like react-flow or dagre.","status":"open","priority":3,"issue_type":"task","created_at":"2026-03-03T14:27:55.835238720Z","created_by":"coder","updated_at":"2026-03-03T14:27:55.835238720Z","source_repo":".","compaction_level":0,"original_size":0,"labels":["frontend","phase-4","web"]} {"id":"bd-3a0","title":"P4-003: Add Session Replay Feature","description":"Phase 4 Intelligence: Record and replay worker sessions. Allow stepping through events chronologically. Store session metadata in SQLite.","status":"closed","priority":4,"issue_type":"task","created_at":"2026-03-03T07:53:39.995567065Z","created_by":"coder","updated_at":"2026-03-03T07:53:39.995567065Z","closed_at":"2026-03-03T07:53:39.995567065Z","source_repo":".","compaction_level":0,"original_size":0,"labels":["intelligence","phase-4","replay"]} {"id":"bd-3av","title":"P4-006: File Heatmap","description":"Implement file heatmap feature - show which files are being accessed/modified most frequently by workers. Helps identify hot paths and potential bottlenecks.","status":"closed","priority":3,"issue_type":"task","assignee":"coder","created_at":"2026-03-03T13:30:47.180967166Z","created_by":"coder","updated_at":"2026-03-03T14:07:39.203982357Z","closed_at":"2026-03-03T14:07:39.186412732Z","close_reason":"done","source_repo":".","compaction_level":0,"original_size":0,"labels":["heatmap","intelligence","phase-4"]} @@ -92,7 +92,7 @@ {"id":"bd-6xy","title":"ALERT: Worker claude-code-glm-5-bravo has no work available","description":"# Worker Starvation Alert\n\nWorker **claude-code-glm-5-bravo** has exhausted all priorities and found zero work.\n\nThis is considered an error state - there should always be more work.\n\n## Worker State\n\n- **Executor:** claude-code-glm-5\n- **Model:** glm-5\n- **Workspace:** /home/coder/FABRIC\n- **Root Boundary:** /home/coder/FABRIC\n- **Last completion:** \n- **Beads completed:** 0\n- **Claim success rate:** %\n- **Uptime:** 18748s (h)\n- **Consecutive empty iterations:** 5\n\n## Priorities Exhausted\n\n1. ✗ Local workspace (bottoms-up): No beads in /home/coder/FABRIC or subfolders\n2. ✗ Parent exploration: No suitable workspaces found\n3. ✓ Maintenance: Completed (cleaned orphaned claims/locks)\n4. ✗ Gap analysis: false - No gaps found or created\n5. ✗ HUMAN alternatives: true - No HUMAN beads found to unblock\n\n## Discovered Workspaces\n\nTotal: 1\n\n- /home/coder/FABRIC\n\n## Required Actions\n\n1. Review discovery roots: Are all project folders being scanned?\n2. Check if projects need new features/tasks\n3. Review ROADMAP.md files across projects\n4. Enable gap analysis if disabled: `--enable-gap-analysis`\n5. Enable HUMAN alternatives if disabled\n6. Create manual beads to bootstrap work\n\n---\n*This alert was created automatically by Priority 6*","status":"closed","priority":0,"issue_type":"human","created_at":"2026-03-03T09:36:00.727780827Z","created_by":"coder","updated_at":"2026-03-03T09:37:27.774940828Z","closed_at":"2026-03-03T09:37:27.774712168Z","source_repo":".","compaction_level":0,"original_size":0} {"id":"bd-9r6","title":"ALERT: Worker claude-code-glm-5-alpha has no work available","description":"# Worker Starvation Alert\n\nWorker **claude-code-glm-5-alpha** has exhausted all priorities and found zero work.\n\nThis is considered an error state - there should always be more work.\n\n## Worker State\n\n- **Executor:** claude-code-glm-5\n- **Model:** glm-5\n- **Workspace:** /home/coder/FABRIC\n- **Root Boundary:** /home/coder/FABRIC\n- **Last completion:** \n- **Beads completed:** 0\n- **Claim success rate:** %\n- **Uptime:** 21201s (h)\n- **Consecutive empty iterations:** 5\n\n## Priorities Exhausted\n\n1. ✗ Local workspace (bottoms-up): No beads in /home/coder/FABRIC or subfolders\n2. ✗ Parent exploration: No suitable workspaces found\n3. ✓ Maintenance: Completed (cleaned orphaned claims/locks)\n4. ✗ Gap analysis: false - No gaps found or created\n5. ✗ HUMAN alternatives: true - No HUMAN beads found to unblock\n\n## Discovered Workspaces\n\nTotal: 1\n\n- /home/coder/FABRIC\n\n## Required Actions\n\n1. Review discovery roots: Are all project folders being scanned?\n2. Check if projects need new features/tasks\n3. Review ROADMAP.md files across projects\n4. Enable gap analysis if disabled: `--enable-gap-analysis`\n5. Enable HUMAN alternatives if disabled\n6. Create manual beads to bootstrap work\n\n---\n*This alert was created automatically by Priority 6*","status":"closed","priority":0,"issue_type":"human","created_at":"2026-03-03T10:16:51.892714230Z","created_by":"coder","updated_at":"2026-03-03T10:18:08.139454773Z","closed_at":"2026-03-03T10:18:08.139241701Z","source_repo":".","compaction_level":0,"original_size":0} {"id":"bd-9rs","title":"ALT-006: Direct ready-queue.json reader for workers","description":"For HUMAN bead bd-3sh. Workers read .beads/ready-queue.json directly instead of using br ready. Implemented scripts/br-get-next-bead.sh and scripts/br-regenerate-queue.sh. Bypasses br CLI bugs entirely.","status":"closed","priority":1,"issue_type":"task","created_at":"2026-03-03T08:39:14.763941870Z","created_by":"coder","updated_at":"2026-03-03T08:49:23.005617063Z","closed_at":"2026-03-03T08:49:23.005418015Z","source_repo":".","compaction_level":0,"original_size":0,"labels":["alternative","br","resilience","worker"]} -{"id":"bd-ak8","title":"Add web server unit tests","description":"Create src/web/server.test.ts with tests for Express HTTP endpoints, WebSocket connections, and broadcast functionality.","status":"in_progress","priority":2,"issue_type":"task","assignee":"coder","created_at":"2026-03-03T14:26:45.138473558Z","created_by":"coder","updated_at":"2026-03-03T14:48:39.874056462Z","source_repo":".","compaction_level":0,"original_size":0,"labels":["phase-3","testing","web"]} +{"id":"bd-ak8","title":"Add web server unit tests","description":"Create src/web/server.test.ts with tests for Express HTTP endpoints, WebSocket connections, and broadcast functionality.","status":"closed","priority":2,"issue_type":"task","assignee":"coder","created_at":"2026-03-03T14:26:45.138473558Z","created_by":"coder","updated_at":"2026-03-03T14:54:44.224826330Z","closed_at":"2026-03-03T14:54:44.200679342Z","close_reason":"completed","source_repo":".","compaction_level":0,"original_size":0,"labels":["phase-3","testing","web"]} {"id":"bd-b02","title":"FIX: Worker discovery should check ready-queue.json before creating HUMAN beads","description":"Workers create HUMAN beads for starvation when work exists in ready-queue.json. Discovery should: 1) Read ready-queue.json first, 2) Only create HUMAN if truly no work, 3) Check timestamp for staleness. Related to bd-123 (closed as false positive).","status":"closed","priority":1,"issue_type":"task","created_at":"2026-03-03T09:04:28.545784106Z","created_by":"coder","updated_at":"2026-03-03T09:20:14.122855469Z","closed_at":"2026-03-03T09:20:14.122791066Z","source_repo":".","compaction_level":0,"original_size":0,"labels":["br","bug","discovery","worker-starvation"]} {"id":"bd-b0c","title":"Add WorkerDetail component to web frontend","description":"Port TUI WorkerDetail.ts to React web frontend. Create src/web/frontend/src/components/WorkerDetail.tsx as a dedicated worker detail panel component.","status":"closed","priority":2,"issue_type":"task","assignee":"coder","created_at":"2026-03-03T14:26:27.140264786Z","created_by":"coder","updated_at":"2026-03-03T14:41:02.648208451Z","closed_at":"2026-03-03T14:41:02.622587994Z","close_reason":"completed","source_repo":".","compaction_level":0,"original_size":0,"labels":["frontend","phase-3","web"]} {"id":"bd-dlz","title":"ALERT: Worker claude-code-glm-5-alpha has no work available","description":"# Worker Starvation Alert\n\nWorker **claude-code-glm-5-alpha** has exhausted all priorities and found zero work.\n\nThis is considered an error state - there should always be more work.\n\n## Worker State\n\n- **Executor:** claude-code-glm-5\n- **Model:** glm-5\n- **Workspace:** /home/coder/FABRIC\n- **Root Boundary:** /home/coder/FABRIC\n- **Last completion:** \n- **Beads completed:** 0\n- **Claim success rate:** %\n- **Uptime:** 29227s (h)\n- **Consecutive empty iterations:** 5\n\n## Priorities Exhausted\n\n1. ✗ Local workspace (bottoms-up): No beads in /home/coder/FABRIC or subfolders\n2. ✗ Parent exploration: No suitable workspaces found\n3. ✓ Maintenance: Completed (cleaned orphaned claims/locks)\n4. ✗ Gap analysis: false - No gaps found or created\n5. ✗ HUMAN alternatives: true - No HUMAN beads found to unblock\n\n## Discovered Workspaces\n\nTotal: 1\n\n- /home/coder/FABRIC\n\n## Required Actions\n\n1. Review discovery roots: Are all project folders being scanned?\n2. Check if projects need new features/tasks\n3. Review ROADMAP.md files across projects\n4. Enable gap analysis if disabled: `--enable-gap-analysis`\n5. Enable HUMAN alternatives if disabled\n6. Create manual beads to bootstrap work\n\n---\n*This alert was created automatically by Priority 6*","status":"closed","priority":0,"issue_type":"human","assignee":"coder","created_at":"2026-03-03T12:30:37.722452022Z","created_by":"coder","updated_at":"2026-03-03T12:36:21.514407968Z","closed_at":"2026-03-03T12:36:21.506434412Z","close_reason":"FALSE POSITIVE: Ready queue has 22 beads available. Worker discovery failed to find them. Workers should check .beads/ready-queue.json directly when 'no work available' is reported.","source_repo":".","compaction_level":0,"original_size":0} diff --git a/src/web/frontend/test/CollisionAlert.test.tsx b/src/web/frontend/test/CollisionAlert.test.tsx new file mode 100644 index 0000000..23567e0 --- /dev/null +++ b/src/web/frontend/test/CollisionAlert.test.tsx @@ -0,0 +1,545 @@ +/** + * Tests for CollisionAlert component + * @vitest-environment jsdom + */ + +import { describe, it, expect, vi, beforeEach } from 'vitest'; +import { render, screen, fireEvent, cleanup } from '@testing-library/react'; +import CollisionAlert from '../src/components/CollisionAlert'; +import { CollisionAlert as CollisionAlertData } from '../src/types'; + +describe('CollisionAlert', () => { + const createMockAlert = (overrides: Partial = {}): CollisionAlertData => ({ + id: 'alert-1', + type: 'file', + severity: 'warning', + title: 'Test Alert', + description: 'Test description', + workers: ['worker-alpha', 'worker-beta'], + timestamp: Date.now(), + acknowledged: false, + collision: { + path: '/test/file.ts', + workers: ['worker-alpha', 'worker-beta'], + detectedAt: Date.now(), + isActive: true, + }, + ...overrides, + }); + + const mockOnAcknowledge = vi.fn(); + const mockOnAcknowledgeAll = vi.fn(); + const mockOnClose = vi.fn(); + + beforeEach(() => { + mockOnAcknowledge.mockClear(); + mockOnAcknowledgeAll.mockClear(); + mockOnClose.mockClear(); + }); + + describe('rendering', () => { + it('should not render when visible is false', () => { + const { container } = render( + + ); + + expect(container.querySelector('.collision-alert-panel')).not.toBeInTheDocument(); + }); + + it('should render empty state when no alerts', () => { + const { container } = render( + + ); + + expect(container.querySelector('.collision-alert-panel')).toBeInTheDocument(); + expect(container.querySelector('.collision-empty')).toBeInTheDocument(); + }); + + it('should render alerts count', () => { + const alerts = [ + createMockAlert({ id: 'alert-1', title: 'Alert 1' }), + createMockAlert({ id: 'alert-2', title: 'Alert 2' }), + ]; + + const { container } = render( + + ); + + expect(container.querySelector('.collision-summary')?.textContent).toContain('Alerts: 2'); + }); + + it('should render unacknowledged count', () => { + const alerts = [ + createMockAlert({ id: 'alert-1', acknowledged: false }), + createMockAlert({ id: 'alert-2', acknowledged: true }), + ]; + + const { container } = render( + + ); + + expect(container.querySelector('.collision-summary')?.textContent).toContain('1 unacknowledged'); + }); + }); + + describe('severity grouping', () => { + it('should group critical alerts together', () => { + const alerts = [ + createMockAlert({ id: 'alert-1', severity: 'critical', title: 'Critical Alert' }), + createMockAlert({ id: 'alert-2', severity: 'error', title: 'Error Alert' }), + ]; + + const { container } = render( + + ); + + const criticalGroup = container.querySelector('.collision-group-critical'); + expect(criticalGroup).toBeInTheDocument(); + expect(criticalGroup?.textContent).toContain('CRITICAL/ERROR'); + expect(criticalGroup?.textContent).toContain('Critical Alert'); + expect(criticalGroup?.textContent).toContain('Error Alert'); + }); + + it('should group warning alerts together', () => { + const alerts = [ + createMockAlert({ id: 'alert-1', severity: 'warning', title: 'Warning Alert' }), + ]; + + const { container } = render( + + ); + + const warningGroup = container.querySelector('.collision-group-warning'); + expect(warningGroup).toBeInTheDocument(); + expect(warningGroup?.textContent).toContain('WARNINGS'); + expect(warningGroup?.textContent).toContain('Warning Alert'); + }); + + it('should group info alerts together', () => { + const alerts = [ + createMockAlert({ id: 'alert-1', severity: 'info', title: 'Info Alert' }), + ]; + + const { container } = render( + + ); + + const infoGroup = container.querySelector('.collision-group-info'); + expect(infoGroup).toBeInTheDocument(); + expect(infoGroup?.textContent).toContain('INFO'); + expect(infoGroup?.textContent).toContain('Info Alert'); + }); + }); + + describe('type icons', () => { + it('should show [F] for file type', () => { + const alerts = [ + createMockAlert({ id: 'alert-1', type: 'file' }), + ]; + + const { container } = render( + + ); + + expect(container.querySelector('.collision-item-type')?.textContent).toBe('[F]'); + }); + + it('should show [B] for bead type', () => { + const alerts = [ + createMockAlert({ id: 'alert-1', type: 'bead' }), + ]; + + const { container } = render( + + ); + + expect(container.querySelector('.collision-item-type')?.textContent).toBe('[B]'); + }); + + it('should show [T] for task type', () => { + const alerts = [ + createMockAlert({ id: 'alert-1', type: 'task' }), + ]; + + const { container } = render( + + ); + + expect(container.querySelector('.collision-item-type')?.textContent).toBe('[T]'); + }); + }); + + describe('severity icons', () => { + it('should show !!! for critical', () => { + const alerts = [ + createMockAlert({ id: 'alert-1', severity: 'critical' }), + ]; + + const { container } = render( + + ); + + expect(container.querySelector('.collision-item-icon')?.textContent).toBe('!!!'); + }); + + it('should show !! for error', () => { + const alerts = [ + createMockAlert({ id: 'alert-1', severity: 'error' }), + ]; + + const { container } = render( + + ); + + expect(container.querySelector('.collision-item-icon')?.textContent).toBe('!!'); + }); + + it('should show ! for warning', () => { + const alerts = [ + createMockAlert({ id: 'alert-1', severity: 'warning' }), + ]; + + const { container } = render( + + ); + + expect(container.querySelector('.collision-item-icon')?.textContent).toBe('!'); + }); + + it('should show i for info', () => { + const alerts = [ + createMockAlert({ id: 'alert-1', severity: 'info' }), + ]; + + const { container } = render( + + ); + + expect(container.querySelector('.collision-item-icon')?.textContent).toBe('i'); + }); + }); + + describe('acknowledgment', () => { + it('should call onAcknowledge when acknowledge button clicked', () => { + const alerts = [ + createMockAlert({ id: 'alert-1', title: 'Test Alert' }), + ]; + + const { container } = render( + + ); + + // Click acknowledge button (alert is already selected by default) + const ackBtn = container.querySelector('.collision-action-btn'); + expect(ackBtn).toBeInTheDocument(); + fireEvent.click(ackBtn!); + + expect(mockOnAcknowledge).toHaveBeenCalledWith('alert-1'); + }); + + it('should call onAcknowledgeAll when acknowledge all clicked', () => { + const alerts = [ + createMockAlert({ id: 'alert-1' }), + createMockAlert({ id: 'alert-2' }), + ]; + + const { container } = render( + + ); + + const ackAllBtn = container.querySelectorAll('.collision-action-btn')[1]; + fireEvent.click(ackAllBtn); + + expect(mockOnAcknowledgeAll).toHaveBeenCalled(); + }); + + it('should show [ACK] for acknowledged alerts', () => { + const alerts = [ + createMockAlert({ id: 'alert-1', acknowledged: true, title: 'Acknowledged Alert' }), + ]; + + const { container } = render( + + ); + + expect(container.querySelector('.collision-item-ack')?.textContent).toBe('[ACK]'); + }); + + it('should disable acknowledge button for acknowledged alerts', () => { + const alerts = [ + createMockAlert({ id: 'alert-1', acknowledged: true, title: 'Acknowledged Alert' }), + ]; + + const { container } = render( + + ); + + const acknowledgeBtn = container.querySelector('.collision-action-btn'); + expect(acknowledgeBtn).toBeDisabled(); + }); + }); + + describe('detail view', () => { + it('should show detail section when alert selected', () => { + const alerts = [ + createMockAlert({ id: 'alert-1', title: 'Test Alert', description: 'Detailed description' }), + ]; + + const { container } = render( + + ); + + const detailSection = container.querySelector('.collision-detail'); + expect(detailSection).toBeInTheDocument(); + expect(detailSection?.textContent).toContain('Selected Alert Details:'); + expect(detailSection?.textContent).toContain('Detailed description'); + }); + + it('should show workers in detail view', () => { + const alerts = [ + createMockAlert({ + id: 'alert-1', + workers: ['worker-alpha', 'worker-beta'] + }), + ]; + + const { container } = render( + + ); + + const detailSection = container.querySelector('.collision-detail'); + expect(detailSection?.textContent).toContain('worker-alpha'); + expect(detailSection?.textContent).toContain('worker-beta'); + }); + + it('should show suggestion when present', () => { + const alerts = [ + createMockAlert({ + id: 'alert-1', + suggestion: 'Consider coordinating with other worker' + }), + ]; + + const { container } = render( + + ); + + expect(container.querySelector('.collision-detail-suggestion')?.textContent).toContain('Consider coordinating'); + }); + }); + + describe('close button', () => { + it('should call onClose when close button clicked', () => { + const alerts = [ + createMockAlert({ id: 'alert-1' }), + ]; + + const { container } = render( + + ); + + const closeBtn = container.querySelector('.collision-alert-close'); + fireEvent.click(closeBtn!); + + expect(mockOnClose).toHaveBeenCalled(); + }); + }); + + describe('worker display', () => { + it('should show worker count when more than 2 workers', () => { + const alerts = [ + createMockAlert({ + id: 'alert-1', + workers: ['w1', 'w2', 'w3', 'w4'] + }), + ]; + + const { container } = render( + + ); + + expect(container.querySelector('.collision-item-workers')?.textContent).toBe('4 workers'); + }); + + it('should show worker names when 2 or fewer workers', () => { + const alerts = [ + createMockAlert({ + id: 'alert-1', + workers: ['alpha', 'beta'] + }), + ]; + + const { container } = render( + + ); + + expect(container.querySelector('.collision-item-workers')?.textContent).toBe('alpha, beta'); + }); + }); + + describe('title truncation', () => { + it('should truncate long titles', () => { + const longTitle = 'A'.repeat(50); + const alerts = [ + createMockAlert({ id: 'alert-1', title: longTitle }), + ]; + + const { container } = render( + + ); + + // Title should be truncated to 40 chars + '...' + const titleEl = container.querySelector('.collision-item-title'); + expect(titleEl?.textContent).toBe('A'.repeat(40) + '...'); + }); + }); + + describe('CSS classes', () => { + it('should apply collision-alert-panel class', () => { + const { container } = render( + + ); + + expect(container.querySelector('.collision-alert-panel')).toBeInTheDocument(); + }); + + it('should apply severity class to items', () => { + const alerts = [ + createMockAlert({ id: 'alert-1', severity: 'critical' }), + ]; + + const { container } = render( + + ); + + expect(container.querySelector('.collision-severity-critical')).toBeInTheDocument(); + }); + + it('should apply selected class to selected item', () => { + const alerts = [ + createMockAlert({ id: 'alert-1', title: 'First' }), + createMockAlert({ id: 'alert-2', title: 'Second' }), + ]; + + const { container } = render( + + ); + + // First item should be selected by default + const selectedItems = container.querySelectorAll('.collision-item.selected'); + expect(selectedItems.length).toBe(1); + }); + + it('should apply acknowledged class', () => { + const alerts = [ + createMockAlert({ id: 'alert-1', acknowledged: true }), + ]; + + const { container } = render( + + ); + + expect(container.querySelector('.collision-item.acknowledged')).toBeInTheDocument(); + }); + }); + + describe('selection', () => { + it('should change selection when clicking an alert', () => { + const alerts = [ + createMockAlert({ id: 'alert-1', title: 'First Alert' }), + createMockAlert({ id: 'alert-2', title: 'Second Alert' }), + ]; + + const { container } = render( + + ); + + // Click second item + const items = container.querySelectorAll('.collision-item'); + fireEvent.click(items[1]); + + // Second item should now be selected + expect(items[1]).toHaveClass('selected'); + expect(items[0]).not.toHaveClass('selected'); + }); + }); +});