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 <noreply@anthropic.com>
This commit is contained in:
parent
ac5e98ea85
commit
469ad2cebe
2 changed files with 548 additions and 3 deletions
|
|
@ -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}
|
||||
|
|
|
|||
545
src/web/frontend/test/CollisionAlert.test.tsx
Normal file
545
src/web/frontend/test/CollisionAlert.test.tsx
Normal file
|
|
@ -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> = {}): 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(
|
||||
<CollisionAlert
|
||||
alerts={[]}
|
||||
visible={false}
|
||||
/>
|
||||
);
|
||||
|
||||
expect(container.querySelector('.collision-alert-panel')).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should render empty state when no alerts', () => {
|
||||
const { container } = render(
|
||||
<CollisionAlert
|
||||
alerts={[]}
|
||||
visible={true}
|
||||
/>
|
||||
);
|
||||
|
||||
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(
|
||||
<CollisionAlert
|
||||
alerts={alerts}
|
||||
visible={true}
|
||||
/>
|
||||
);
|
||||
|
||||
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(
|
||||
<CollisionAlert
|
||||
alerts={alerts}
|
||||
visible={true}
|
||||
/>
|
||||
);
|
||||
|
||||
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(
|
||||
<CollisionAlert
|
||||
alerts={alerts}
|
||||
visible={true}
|
||||
/>
|
||||
);
|
||||
|
||||
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(
|
||||
<CollisionAlert
|
||||
alerts={alerts}
|
||||
visible={true}
|
||||
/>
|
||||
);
|
||||
|
||||
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(
|
||||
<CollisionAlert
|
||||
alerts={alerts}
|
||||
visible={true}
|
||||
/>
|
||||
);
|
||||
|
||||
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(
|
||||
<CollisionAlert
|
||||
alerts={alerts}
|
||||
visible={true}
|
||||
/>
|
||||
);
|
||||
|
||||
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(
|
||||
<CollisionAlert
|
||||
alerts={alerts}
|
||||
visible={true}
|
||||
/>
|
||||
);
|
||||
|
||||
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(
|
||||
<CollisionAlert
|
||||
alerts={alerts}
|
||||
visible={true}
|
||||
/>
|
||||
);
|
||||
|
||||
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(
|
||||
<CollisionAlert
|
||||
alerts={alerts}
|
||||
visible={true}
|
||||
/>
|
||||
);
|
||||
|
||||
expect(container.querySelector('.collision-item-icon')?.textContent).toBe('!!!');
|
||||
});
|
||||
|
||||
it('should show !! for error', () => {
|
||||
const alerts = [
|
||||
createMockAlert({ id: 'alert-1', severity: 'error' }),
|
||||
];
|
||||
|
||||
const { container } = render(
|
||||
<CollisionAlert
|
||||
alerts={alerts}
|
||||
visible={true}
|
||||
/>
|
||||
);
|
||||
|
||||
expect(container.querySelector('.collision-item-icon')?.textContent).toBe('!!');
|
||||
});
|
||||
|
||||
it('should show ! for warning', () => {
|
||||
const alerts = [
|
||||
createMockAlert({ id: 'alert-1', severity: 'warning' }),
|
||||
];
|
||||
|
||||
const { container } = render(
|
||||
<CollisionAlert
|
||||
alerts={alerts}
|
||||
visible={true}
|
||||
/>
|
||||
);
|
||||
|
||||
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(
|
||||
<CollisionAlert
|
||||
alerts={alerts}
|
||||
visible={true}
|
||||
/>
|
||||
);
|
||||
|
||||
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(
|
||||
<CollisionAlert
|
||||
alerts={alerts}
|
||||
visible={true}
|
||||
onAcknowledge={mockOnAcknowledge}
|
||||
/>
|
||||
);
|
||||
|
||||
// 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(
|
||||
<CollisionAlert
|
||||
alerts={alerts}
|
||||
visible={true}
|
||||
onAcknowledgeAll={mockOnAcknowledgeAll}
|
||||
/>
|
||||
);
|
||||
|
||||
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(
|
||||
<CollisionAlert
|
||||
alerts={alerts}
|
||||
visible={true}
|
||||
/>
|
||||
);
|
||||
|
||||
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(
|
||||
<CollisionAlert
|
||||
alerts={alerts}
|
||||
visible={true}
|
||||
onAcknowledge={mockOnAcknowledge}
|
||||
/>
|
||||
);
|
||||
|
||||
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(
|
||||
<CollisionAlert
|
||||
alerts={alerts}
|
||||
visible={true}
|
||||
/>
|
||||
);
|
||||
|
||||
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(
|
||||
<CollisionAlert
|
||||
alerts={alerts}
|
||||
visible={true}
|
||||
/>
|
||||
);
|
||||
|
||||
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(
|
||||
<CollisionAlert
|
||||
alerts={alerts}
|
||||
visible={true}
|
||||
/>
|
||||
);
|
||||
|
||||
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(
|
||||
<CollisionAlert
|
||||
alerts={alerts}
|
||||
visible={true}
|
||||
onClose={mockOnClose}
|
||||
/>
|
||||
);
|
||||
|
||||
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(
|
||||
<CollisionAlert
|
||||
alerts={alerts}
|
||||
visible={true}
|
||||
/>
|
||||
);
|
||||
|
||||
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(
|
||||
<CollisionAlert
|
||||
alerts={alerts}
|
||||
visible={true}
|
||||
/>
|
||||
);
|
||||
|
||||
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(
|
||||
<CollisionAlert
|
||||
alerts={alerts}
|
||||
visible={true}
|
||||
/>
|
||||
);
|
||||
|
||||
// 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(
|
||||
<CollisionAlert alerts={[]} visible={true} />
|
||||
);
|
||||
|
||||
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(
|
||||
<CollisionAlert alerts={alerts} visible={true} />
|
||||
);
|
||||
|
||||
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(
|
||||
<CollisionAlert alerts={alerts} visible={true} />
|
||||
);
|
||||
|
||||
// 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(
|
||||
<CollisionAlert alerts={alerts} visible={true} />
|
||||
);
|
||||
|
||||
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(
|
||||
<CollisionAlert alerts={alerts} visible={true} />
|
||||
);
|
||||
|
||||
// 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');
|
||||
});
|
||||
});
|
||||
});
|
||||
Loading…
Add table
Reference in a new issue