- Create BlockedTriageModal component at src/components/shared/blocked-triage-modal.tsx
- Implements modal with blocked task triage functionality
- Uses deriveBlockedIds and buildBlockedByTree from kanban lib
- Each row shows blocker chain and has inline archetype picker
- Modal is scrollable and closes via Escape/close button
- Add corresponding tests at tests/components/blocked-triage-modal.test.tsx
- Register test in package.json test script
- Removed broken LaunchSwarmDialog (formula-based) from TopBar/LeftPanel
- All Rocket buttons (TopBar, LeftPanel, DAG nodes, social cards) now open
AssignmentPanel (archetype-based) which actually works
- Every Rocket clears taskId first so assignMode && !taskId condition passes
- Conversation button priority: taskId always shows conversation, not assign panel
- Added TelemetryStrip: minimized right sidebar with status dots when non-telemetry
panel (conversation/assignment) is active
- Live feed has minimize button → restores last taskId or assignMode
- DAG nodes: Signal icon → restores telemetry feed
- Social button on DAG nodes: single router.push to avoid race (setView + setTaskId)
- Fixed social card message button: opens right panel with drawer:closed (no popup)
Co-Authored-By: Oz <oz-agent@warp.dev>
- Add MessageSquare icon to GraphNodeCard; prop-thread onConversationOpen
and selectedTaskId through WorkflowGraph node data (no useUrlState
inside ReactFlow nodes — avoids context/timing issues)
- Fix ContextualRightPanel: check taskId before epicId so clicking the
conversation icon always opens ThreadDrawer even when an epic filter
is active
- setEpicId now clears task from URL so selecting an epic resets any
open conversation thread
- handleGraphSelect toggles: second click on same node calls setTaskId(null)
closing the right panel
- Add onSelect to WorkflowGraph flowModel deps to prevent stale callbacks
- Fix ContextualRightPanel onClose no-ops: wired to setTaskId(null) /
setSwarmId(null) so back button works
- Right panel always visible (removed panel==='open' gate in UnifiedShell)
- SmartDag task grid: horizontal scroll, fixed-width cards, hideClosed=true
- Add <Suspense> in page.tsx for useSearchParams compatibility
- Enable dolt auto-start in .beads/config.yaml
- Add 14 static analysis tests (graph-node-conversation.test.tsx)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
## Design Decision
Per bd (bead) system design, a task should have only ONE agent archetype
assigned at a time. This provides clear ownership and simpler mental model.
## What Changed
When assigning a new archetype:
1. Remove any existing agent: labels first (DELETE API)
2. Then add the new agent: label (POST API)
3. Optimistic UI updates to match
## Why This Makes Sense
- Clear ownership: 'Who's working on this?'
- Simpler coordination between tasks
- Matches how bd/agent orchestration is intended to work
- Reassigning is still possible (just click a different archetype)
## UI Behavior
- If task has 'coder' assigned, clicking 'architect' will:
1. Remove 'coder' label
2. Add 'architect' label
- Dropdown shows 'Assigned' badge on current archetype
- X button still available to unassign completely
## Test Coverage
Added graph-node-single-archetype.test.tsx with 5 tests:
- Removes existing labels before adding new
- Calls DELETE before POST
- Only allows one archetype per task
- Preserves non-agent labels
- Returns early if same archetype clicked
## The Bug
User reported: 'An archetype can only exist on one task at a time - when
I try to make the next task have the same arch, it deleted the one I
added prior.'
## Root Cause
The SSE subscription (useBeadsSubscription) refreshes data from the server
whenever ANY change happens. When user assigns an archetype:
1. User clicks assign -> optimistic update adds label locally
2. SSE fires (from previous operation or heartbeat) -> fetches fresh data
3. useEffect syncs localLabels with data.labels (which doesn't have the
new label yet)
4. Label disappears from UI
5. Eventually API completes and another SSE refresh brings it back
This race condition causes labels to flicker or disappear entirely.
## The Fix
Track pending optimistic labels in a useRef Set, and merge them with
incoming server data during sync:
1. pendingOptimisticLabels = useRef<Set<string>>(new Set())
2. When optimistically adding: add to pending set
3. useEffect merge: combine server labels + pending labels
4. After API completes: remove from pending set
This ensures optimistic labels survive SSE refreshes.
## Test Coverage
Added graph-node-labels-optimistic.test.tsx with 10 tests:
- Uses useRef for tracking
- Tracks in a Set
- Preserves labels during sync
- Adds/removes from pending set
- Handles multiple concurrent operations
- Per-node state isolation
## Verification
- typecheck: pass
- lint: pass (0 errors)
- test: all pass
Various supporting changes made during the assign archetypes feature development:
- Added contextual-right-panel.tsx and swarm-command-feed.tsx
- Updated activity-panel.tsx with new features
- UI improvements to left-panel, mobile-nav
- Test updates for url-state-integration, mobile-nav, top-bar
- Package.json updates for dependencies
- Global CSS refinements
These changes support the main assign archetypes feature but are
not directly part of its core functionality.
## Context
This commit adds the supporting infrastructure that makes the assign
feature work end-to-end.
## Components Added/Modified
### SmartDag
- Main view component for graph-based task management
- Integrates TaskCardGrid and WorkflowGraph
- Has 'Assign' mode toggle button
- Passes archetypes and assignMode to WorkflowGraph
- Manages filter state (hideClosed, sortReadyFirst, etc.)
### useGraphAnalysis Hook
- Extracted graph analysis logic for reuse
- Returns: actionableNodeIds, cycleNodeIdSet, blockerTooltipMap, etc.
- Used by both SmartDag and AssignmentPanel
- Ensures consistent 'actionable' definition across components
### UnifiedShell
- Added assignMode state
- Added selectedAssignIssue state
- Renders AssignmentPanel when in graph view + assign mode
- Wires up onAssignModeChange and onSelectedIssueChange callbacks
## Design Philosophy
- Shared hook means single source of truth for 'actionable'
- Clean separation between view (SmartDag) and sidebar (AssignmentPanel)
- URL state preserved for navigation
## Test Coverage
- SmartDag tests: 12 tests covering buttons, callbacks, imports
- useGraphAnalysis tests: 6 tests covering cycle detection, blockers
- UnifiedShell tests: 9 tests covering state and rendering
## The Feature Request
User wanted an enhanced sidebar panel showing:
- Tasks needing agents (ready but unassigned)
- Pre-assigned tasks waiting to start
- Active workers on current epic
## Design Collaboration
We discussed what each section should show:
1. **Needs Agent**: Actionable tasks (no blockers) without agent: label
2. **Pre-assigned**: Tasks with agent: label, not yet in_progress
3. **Squad Roster**: in_progress tasks with assignee
## Technical Implementation
- Uses useGraphAnalysis hook for actionableNodeIds
- Helper functions: hasAgentLabel(), getAgentLabels(), extractArchetypeIdFromLabel()
- Quick assign dropdown on each 'Needs Agent' item
- Archetype badges shown on 'Pre-assigned' items
## UI/UX Decisions
- Each section has count badge in header
- Max-height with scroll for each section
- Consistent styling with existing panel patterns
- Uses CSS variables for theming
## Test Coverage
- Added assignment-panel-sections.test.tsx with 5 TDD tests
- Tests verify: useGraphAnalysis import, section headers, filtering logic
## Beads: beadboard-b7t (closed)
## The Collaboration Story
User requested ability to assign agent archetypes to tasks directly from
graph nodes. This was the core UI feature request.
## Design Decisions Made Together
1. **Placement**: We decided to put the assign UI at the bottom of the card
to not interfere with existing status/badges display
2. **Pattern**: Used Radix dropdown-menu (already in project) for consistency
3. **Visual feedback**: Added loading spinner during API calls, success/error
messages that auto-dismiss
4. **Closed tasks**: Excluded closed tasks from assignment (logical constraint)
## Issues We Encountered
- Initially only added POST handler for assignment
- User pointed out we needed DELETE for unassign - added that
- The unassign button (X) needed to call DELETE not POST
## API Changes
- Added DELETE handler to /api/swarm/prep for removing agent assignments
- Uses 'bd label remove' under the hood
## What the UI Shows
- Dropdown with available archetypes
- Badge showing assigned archetype name with color
- X button to unassign (on each badge)
- 'Assigned' label on already-assigned archetypes in dropdown
## Test Coverage
- Added graph-node-assign.test.tsx with 6 TDD tests
## Beads: beadboard-brq (closed)
## Context
This is the foundation commit for the 'Assign Archetypes to Tasks' feature.
We needed a way to display which agents are assigned to tasks directly on
the graph nodes.
## Decision Process
- User wanted to see agent assignments on DAG nodes
- We discovered that labels (including 'agent:archetype-id' format) weren't
being passed through the WorkflowGraph component
- Added 'labels' and 'archetypes' to GraphNodeData interface
## What Changed
- WorkflowGraph now passes issue.labels to each node's data
- GraphNodeData interface updated to include labels: string[]
- Added archetypes prop for dropdown population
## Test Coverage
- Added graph-node-labels.test.tsx with 4 passing tests
## Beads: beadboard-yo5 (closed)
This commit includes the new SwarmWorkspace with its 3 sub-tabs, the LeftPanel mission picker, and the comprehensive Operations Command Dashboard featuring the live interactive DAG telemetry and task assignment prep flow.
STORY:
With the shell layout complete, we needed the actual content for each view.
Three agents worked in parallel on the card components that would populate
the Social and Swarm views, plus integrating the existing graph into the shell.
COLLABORATION:
Agent bb-98c (social-card-builder) created SocialCard:
- Task ID with teal styling
- UNLOCKS section (green) showing what this task unblocks
- BLOCKS section (amber) showing what's blocking this task
- Agent avatars with liveness glow
- View-jump icons for quick navigation
Agent bb-nuy (swarm-card-builder) created SwarmCard:
- Agent roster with liveness indicators
- Progress bar (ASCII block format: ████████░░░░)
- Attention items with warning styling
- View-jump icons
Agent bb-54x (graph-integrator) integrated WorkflowGraph:
- Created GraphView wrapper with Flow/Overview tabs
- Wired into UnifiedShell when view=graph
- Connected taskId to selectedId for URL sync
- Connected graphTab to URL state
DELIVERABLES:
- src/components/social/social-card.tsx: Task card for activity feed
- src/components/swarm/swarm-card.tsx: Swarm health card
- src/components/graph/graph-view.tsx: Graph wrapper with tabs
- src/components/shared/mobile-nav.tsx: Bottom tab bar
- Tests for all components
VERIFICATION:
- npm run typecheck: PASS
- npm run lint: PASS
- npm run test: PASS
CLOSES: bb-ui2.11, bb-ui2.16, bb-ui2.20
STORY:
Phase 1 of the Unified UX epic required a complete 3-panel shell layout
with responsive behavior across mobile, tablet, and desktop breakpoints.
The existing page structure was fragmented - we needed a cohesive shell.
COLLABORATION:
Three agents (bb-5am, bb-dwz, bb-3dv) worked in parallel on:
- TopBar: View tabs (Social/Graph/Swarm) with active states, filter input
- LeftPanel: Channel tree navigation with epic filtering, responsive collapse
- RightPanel: Detail strip with sidebar (desktop) / drawer (tablet/mobile) modes
We encountered a hydration mismatch error on mobile/tablet because
useResponsive was returning different values on server vs client.
Fixed by defaulting to desktop on server and only updating after mount.
Mobile navigation (bb-ui2.27) added:
- Hamburger menu for left panel access on mobile/tablet
- Bottom tab bar for thumb-friendly view switching
DELIVERABLES:
- src/components/shared/top-bar.tsx: TopBar with view tabs + hamburger
- src/components/shared/left-panel.tsx: Epic tree with expand/collapse
- src/components/shared/right-panel.tsx: Responsive sidebar/drawer
- src/components/shared/unified-shell.tsx: Main 3-panel grid layout
- src/components/shared/mobile-nav.tsx: Bottom tab bar for mobile
- src/hooks/use-responsive.ts: Breakpoint detection (mobile/tablet/desktop)
- Tests for all components
VERIFICATION:
- npm run typecheck: PASS
- npm run lint: PASS
- npm run test: PASS
CLOSES: bb-ui2.6, bb-ui2.7, bb-ui2.8, bb-ui2.9, bb-ui2.27
STORY:
Development requires supporting scripts and comprehensive test coverage.
This commit adds utility scripts and extends test suites.
COLLABORATION:
- scripts/capture-sessions.mjs: Screenshot capture for sessions hub
- scripts/capture-timeline.mjs: Timeline view capture utility
- sessions-header-logic.ts: Session header business logic
- Additional test files for sessions, hooks, and snapshot diffing
STORY:
Agent identities were stored in a local JSON registry, but they should
be first-class beads visible in the BeadBoard system. This consolidates
agent identity to bd CLI as the source of truth.
COLLABORATION:
Replaced local JSON registry with bd CLI wrapper in agent-registry.ts:
- All agent operations now delegate to bd CLI
- Agents appear as team-visible beads with gt:agent label
- Identity isolation prevents agent beads from polluting mission lists
The consolidation makes agents visible to the entire team and ensures
consistent identity management across all tools.
DELIVERABLES:
- src/lib/agent-registry.ts refactored to bd CLI wrapper
- tests/lib/agent-registry-bd.test.ts for bd integration
- tools/bb.ts updated for consolidated identity ops
VERIFICATION:
- All registry tests PASS
- Agents appear on agent page but NOT in task lists
- Quality gates (typecheck, lint) GREEN
CLOSES: bb-1y7
EPIC: bb-u6f
STORY:
The Swarm view needs to show epics as "swarms" - groups of agents
working together on a mission. This requires aggregating bead data
by swarm/epic and computing health statistics.
COLLABORATION:
Created buildSwarmCards function that transforms epic + agent data:
SwarmCard interface:
- id, title, status
- stats: { completed, active, ready, blocked, total }
- agents: AgentRoster[] with liveness
- attention: blocked tasks + stuck agents needing attention
- lastActivity
We also created swarm-molecules.ts for molecular composition patterns
used by the swarm orchestration layer.
DELIVERABLES:
- src/lib/swarm-cards.ts with SwarmCard, AgentRoster types
- src/lib/swarm-molecules.ts for molecular composition
- tests/lib/swarm-cards.test.ts
- tests/lib/swarm-molecules.test.ts
VERIFICATION:
- npm run typecheck: PASS
- npm run lint: PASS
- npm run test: PASS
CLOSES: bb-ui2.15
BLOCKS: bb-ui2.16, bb-ui2.17
STORY:
The Social view needs to transform raw BeadIssue data into renderable
SocialCard objects. This includes computing blocked/blocking relationships
from dependencies and extracting agent assignments.
COLLABORATION:
Created buildSocialCards function that transforms BeadIssue → SocialCard:
SocialCard interface:
- id, title, status
- blockedBy: tasks this task depends on
- blocking: tasks that depend on this task
- agents: assigned agents with liveness
- lastActivity: most recent event
The function derives blockedBy from depends_on dependencies and blocking
from blocked_by reverse dependencies, creating a complete picture of
task relationships for the activity feed.
DELIVERABLES:
- src/lib/social-cards.ts with SocialCard interface and builder
- tests/lib/social-cards.test.ts
VERIFICATION:
- npm run typecheck: PASS
- npm run lint: PASS
- npm run test: PASS
CLOSES: bb-ui2.10
BLOCKS: bb-ui2.11, bb-ui2.12
STORY:
The Unified UX needs URL as the single source of truth for view state.
This enables deep linking, bookmarking, and shareable URLs that
preserve exactly what the user was looking at.
COLLABORATION:
Created useUrlState hook using Next.js useSearchParams and useRouter:
Interface:
- view: 'social' | 'graph' | 'swarm' (default: social)
- taskId: selected task ID (for detail panel)
- swarmId: selected swarm ID
- panel: 'open' | 'closed' (detail panel state)
- graphTab: 'flow' | 'overview' (graph view mode)
URL Patterns:
- /?view=social
- /?view=social&task=bb-buff.1&panel=open
- /?view=swarm&swarm=bb-buff
- /?view=graph&task=bb-buff.1&graphTab=flow
The hook uses router.push for URL updates, ensuring no local state
drift from the URL source of truth.
DELIVERABLES:
- src/hooks/use-url-state.ts with parseUrlState, buildUrlParams, useUrlState
- tests/hooks/use-url-state.test.ts with 18+ tests
VERIFICATION:
- npm run typecheck: PASS
- npm run lint: PASS
- npm run test: PASS (all tests including new ones)
CLOSES: bb-ui2.4
BLOCKS: bb-ui2.5