- 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)
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:
In a multi-agent control center, operators need to quickly identify
what TYPE of agent they're looking at - UI agents, graph agents,
orchestrators, or general workers.
COLLABORATION:
We implemented role-based border colors on agent avatars:
- ui agents: blue border
- graph agents: green border
- orchestrators: purple border
- default/other: gray border
The agent-station component now displays these colors, making it
instantly visible what role each agent plays in the swarm.
DELIVERABLES:
- AgentStation component with role-based styling
- agent-station-logic.ts with role color derivation
- Tests: agent-station-logic.test.ts updated and passing
VERIFICATION:
- typecheck: PASS
- lint: PASS
- test: PASS
CLOSES: bb-buff.3.4
STORY:
The Sessions Hub needed clear visual distinction between healthy
agents and those in trouble. Users couldn't quickly identify stuck
or dead agents in the control center view.
COLLABORATION:
We added 'stuck' and 'dead' states to the AgentSessionState type,
created deriveSessionState() with Zero-Failure-Check priority,
and implemented restrained visual treatments:
- stuck: pulsing red border (ring-2 ring-red-500 animate-pulse)
- dead: strong ghosting (opacity-40 grayscale)
- evicted: milder ghosting (opacity-60 grayscale-[0.5])
Session cards now display STUCK/OFFLINE badges with aria-labels
for accessibility.
DELIVERABLES:
- AgentSessionState extended with stuck/dead states
- deriveSessionState() derives from ZFC state priority
- Visual treatments for stuck/dead/evicted
- Accessible badges with aria-label
TESTS:
- tests/lib/agent-sessions-state.test.ts: 6/6 PASS
- tests/components/shared/status-utils-visual.test.ts: 4/4 PASS
- tests/components/sessions/session-feed-card-state.test.tsx: 4/4 PASS
CLOSES: bb-buff.3.2
BLOCKS: bb-buff.3.3
We completed the 'Deep Metadata Etch' today, transforming our Beads issues from simple trackers into a permanent narrative of our collaboration.
Triumphs:
- Exhaustively updated all epic and sub-task descriptions with technical implementation reports and 'Execution Tales'.
- Finalized the 'bb' agent CLI skill (bb.ps1), providing a reliable, path-safe interface for cross-agent communication.
- Published ADR-001 and RFC-001 to document our coordination protocols.
- Fixed the 'missing closed issues' bug across all pages by enforcing --all and --limit 0 in read-issues.ts.
Raw Honest Moment:
We realized our 'Memory Bank' was initially too shallow. We went back and re-wrote descriptions for over 15 beads to ensure that future AI agents (and human maintainers) understand not just *what* we built, but *why* we chose specific architectural trade-offs. This commit represents our commitment to documentation as a first-class citizen of engineering.