actor: ZenchantLive

notes: Root cause: state divergence and backend drift. .beads/issues.jsonl + beads.db had 381 issues, while active Dolt DB exposed only 4 due stale/incorrect Dolt dataset, stale lock files, and server-mode requirements after upgrading bd.\n\nRepair sequence that worked:\n1) Upgraded bd to 0.56.1.\n2) Installed local dolt binary (~/.local/bin/dolt).\n3) Started local dolt sql-server on 127.0.0.1:3307 rooted at .beads/dolt.\n4) Verified source-of-truth candidate counts: issues.jsonl=381 and beads.db=381 matched.\n5) Recovered known-good 381-issue Dolt dataset from backup path and restored into active .beads/dolt/beadboard.\n6) Set active dolt database via 'bd dolt set database beadboard'.\n7) Re-ran status/ready/list verification.\n\nFinal verification:\n- bd status => Total 381, Open 66, Blocked 23, Closed 219, Ready 43\n- bd ready -n 20 => non-empty (42 ready total)\n- bd list --all --json => total 381; status counts {open:66, closed:219, tombstone:94, deferred:2}\n\nOperational note:\n- bd 0.56.1 is server-mode for Dolt; ensure dolt sql-server is running on configured host/port before bd commands.\n- If unavailable, restart from repo with: cd .beads/dolt && dolt sql-server --host 127.0.0.1 --port 3307

title: Dolt/JSONL recovery: restore beadboard issue inventory

description: Capture successful Dolt reconciliation after bd 0.56.1 migration and lock/server failures.

acceptance_criteria: Document root cause, exact repair commands, final counts, and restart steps for dolt server

status: open

issue_type: task

created_by: ZenchantLive


event_type: closed

new_value: Recovered active Dolt dataset to 381 issues, restored non-empty ready queue, and documented repeatable repair steps + server prerequisites.


new_value: Recreating as proper children of Phase 0 epic with correct naming


title: Wire blockedOnly prop to SocialPage

description:
blockedOnly is already in useUrlState and SocialPage already accepts it as a prop, but UnifiedShell never passes it through. One-line fix.

File: src/components/shared/unified-shell.tsx (~line 128)

Change: ensure blockedOnly is destructured from useUrlState() at line 38, then add prop to <SocialPage>:
  <SocialPage
    issues={filteredIssues}
    selectedId={taskId ?? undefined}
    onSelect={handleCardSelect}
    projectScopeOptions={projectScopeOptions}
    blockedOnly={blockedOnly}
  />

Acceptance: Clicking 'Blocked Items' toggle in TopBar immediately filters social feed to blocked cards only. Toggling off restores full list.


title: Wire blockedOnly prop to SocialPage


title: Add activity render case to renderMiddleContent()

description:
view=activity is a valid ViewType but renderMiddleContent() returns null for it. SocialCard 'Jump to Activity' sends users here — they land on a blank page.

File: src/components/shared/unified-shell.tsx (renderMiddleContent, lines 103-140)

Change: Add before final return null:
  if (view === 'activity') {
    return (
      <ActivityPanel
        issues={filteredIssues}
        projectRoot={projectRoot}
      />
    );
  }

Import: import { ActivityPanel } from '../activity/activity-panel'
First verify ActivityPanel's prop signature in activity-panel.tsx to confirm it accepts issues + projectRoot.

Acceptance: /?view=activity renders the activity feed. SocialCard 'Jump to Activity' lands on content, not blank page.


event_type: updated

description:
ContextualRightPanel currently only branches on epicId. When no task/epic/swarm is selected, the right panel is empty. Wire it to show ActivityPanel (global live feed) as the default state.

File: src/components/activity/contextual-right-panel.tsx
- Read current branching logic
- Add fallback: when no selection active (no epicId, no taskId, no swarmId), render <ActivityPanel issues={issues} projectRoot={projectRoot} />
- ActivityPanel props: issues: BeadIssue[], projectRoot: string, collapsed?: boolean

Also: SocialCard 'Jump to Activity' currently navigates to ?view=activity (dead end). Remove or redirect to clear selection state so right panel falls back to global ActivityPanel.

Acceptance: With nothing selected, right panel shows live activity feed. Selecting a task switches right panel to task context. Deselecting returns to global feed.

title: Wire ActivityPanel as right panel default when nothing selected


new_value: Activity is ambient (right panel default), not a separate nav tab. Nav stays at Social + Graph only. Jump to Activity wiring handled in .2.


new_value: blockedOnly destructured from useUrlState and passed to SocialPage in unified-shell.tsx


new_value: ContextualRightPanel already shows ActivityPanel as default when no selection. Fixed Jump to Activity in social-page.tsx to not navigate to dead view=activity — now opens right panel in place. PRD §3.4 and Phase 0 P0.2/P0.3 updated to reflect activity-as-ambient-panel design decision.


new_value: thread-drawer.tsx line 418: replaced hardcoded 'In Progress' with issue?.status formatted via replace(/_/g,' ')


new_value: TopBar now receives totalTasks, criticalAlerts, busyCount from live issues array in unified-shell.tsx


new_value: All Phase 0 wiring fixes complete: blockedOnly, activity ambient panel, thread drawer status, TopBar metrics.


description:
TASK CONTEXT
- Bead ID: beadboard-r1i.1
- Title: Extend ContextualRightPanel props and thread taskId/swarmId from UnifiedShell
- Parent/Epic: beadboard-r1i
- Dependencies: none (first in phase)

TASK CONTRACT
- Goal: Add taskId and swarmId to ContextualRightPanelProps and pass them from UnifiedShell, so child tasks can implement per-branch rendering. No visible change yet — stubs only.
- Success Criteria:
  - ContextualRightPanelProps includes taskId?: string | null and swarmId?: string | null
  - UnifiedShell passes both params to <ContextualRightPanel>
  - TypeScript compiles clean (npm run typecheck passes)
  - Existing epicId branch and ActivityPanel fallback continue working
- Scope:
  - src/components/activity/contextual-right-panel.tsx — props interface only
  - src/components/shared/unified-shell.tsx — pass taskId, swarmId to ContextualRightPanel call (~line 162)
- Out of Scope:
  - Implementing the actual task or swarm branch content (that is r1i.2, r1i.3)
  - Adding agentId (no agent detail view planned for this phase)

IMPLEMENTATION CONSTRAINTS
- Preserve existing backend/API contracts unless explicitly stated otherwise.
- Reuse shared components and logic; avoid one-off forks.
- Keep changes targeted and minimal for this bead.

VERIFICATION REQUIREMENTS
- Required commands:
  - npm run typecheck
  - npm run lint
  - npm run test
- Report any remaining risks and follow-up beads explicitly.

title: Extend ContextualRightPanel props and thread taskId/swarmId from shell

description:
ContextualRightPanel currently only accepts epicId. Extend it to accept taskId and swarmId, then pass those from UnifiedShell.

File 1: src/components/activity/contextual-right-panel.tsx
- Add taskId?: string | null and swarmId?: string | null to ContextualRightPanelProps
- Add branching stubs (return placeholder divs for now — wired in child tasks)

File 2: src/components/shared/unified-shell.tsx (~line 162)
- Pass taskId and swarmId to <ContextualRightPanel>:
    <ContextualRightPanel epicId={epicId} taskId={taskId} swarmId={swarmId} issues={issues} projectRoot={projectRoot} />

Check current UnifiedShell call to ContextualRightPanel before editing.

Acceptance: TypeScript compiles clean. Props flow through. No visual change yet — stubs in place for .2 and .3.


new_value: Added taskId and swarmId to ContextualRightPanelProps. UnifiedShell now passes both. TypeScript clean. Stubs ready for r1i.2 and r1i.3.


description:
TASK CONTEXT
- Bead ID: beadboard-0fi
- Title: Phase 2: Operator Identity
- Parent/Epic: none (phase epic)
- Dependencies (must be done first): beadboard-r1i (Phase 1 right panel complete)

TASK CONTRACT
- Goal: Replace fragmented actor identity with a single operator profile stored in UnifiedShell state, visible in TopBar, and threaded to both ConversationDrawer and ThreadDrawer. Currently ConversationDrawer reads/writes actor from localStorage independently; ThreadDrawer sends comments with no actor at all.
- Success Criteria:
  - Operator can set their name once in TopBar and it persists across both drawer types
  - Comments posted via ThreadDrawer include the actor field
  - ConversationDrawer no longer reads localStorage directly — receives actor from shell
  - Switching identity in TopBar immediately affects subsequent comments
  - npm run typecheck && npm run lint && npm run test all pass
- Scope:
  - UnifiedShell actor state (single source of truth)
  - TopBar identity selector UI
  - ConversationDrawer actor prop (remove localStorage.getItem)
  - ThreadDrawer postComment actor wiring
- Out of Scope:
  - Multi-user auth or session management
  - Persisting identity server-side
  - Changing comment API contract

IMPLEMENTATION CONSTRAINTS
- Preserve existing backend/API contracts unless explicitly stated otherwise.
- Reuse shared components and logic; avoid one-off forks.
- Keep changes targeted and minimal for this bead.

VERIFICATION REQUIREMENTS
- Required commands:
  - npm run typecheck
  - npm run lint
  - npm run test
- Report any remaining risks and follow-up beads explicitly.

title: Phase 2: Operator Identity

description:
Replace fragmented actor identity with a unified operator profile visible in the shell. Currently ConversationDrawer reads actor from localStorage independently; ThreadDrawer sends comments with no actor at all. This phase establishes a single identity source in the shell and threads it to both drawers.

Ref: docs/plans/2026-02-28-ux-redesign-synthesis-prd.md §8 Phase 2

Acceptance: Comments from both ThreadDrawer and ConversationDrawer show consistent actor. Switching operator identity in TopBar immediately affects subsequent comments.

issue_type: epic


description:
TASK CONTEXT
- Bead ID: beadboard-r1i.2
- Title: Implement task branch in ContextualRightPanel (?task=X)
- Parent/Epic: beadboard-r1i
- Dependencies: beadboard-r1i.1 (taskId prop must exist on ContextualRightPanel)

TASK CONTRACT
- Goal: When a task is selected (taskId is set), the right panel shows the task thread and issue metadata using ThreadDrawer in embedded mode.
- Success Criteria:
  - Selecting any task card opens right panel showing that task's thread (comments, events)
  - ThreadDrawer renders in embedded mode (no modal/overlay behaviour)
  - Issue title, status badge, and thread items are visible in the right panel
  - Deselecting the task (clearing taskId) returns right panel to ActivityPanel global feed
  - npm run typecheck passes
- Scope:
  - src/components/activity/contextual-right-panel.tsx — add taskId branch before the ActivityPanel fallback
  - ThreadDrawer already exists at src/components/shared/thread-drawer.tsx with embedded prop
  - Use ThreadDrawer with: embedded=true, isOpen=true, issue from issues array, projectRoot
- Out of Scope:
  - Agent assignment controls in the right panel (Phase 3)
  - Creating a new component — reuse ThreadDrawer embedded mode
  - Modifying ThreadDrawer itself

IMPLEMENTATION CONSTRAINTS
- Preserve existing backend/API contracts unless explicitly stated otherwise.
- Reuse shared components and logic; avoid one-off forks.
- Keep changes targeted and minimal for this bead.

VERIFICATION REQUIREMENTS
- Required commands:
  - npm run typecheck
  - npm run lint
  - npm run test
- Report any remaining risks and follow-up beads explicitly.


description:
TASK CONTEXT
- Bead ID: beadboard-r1i.3
- Title: Implement swarm branch in ContextualRightPanel (?swarm=X)
- Parent/Epic: beadboard-r1i
- Dependencies: beadboard-r1i.1 (swarmId prop must exist on ContextualRightPanel)

TASK CONTRACT
- Goal: When a swarm is selected (swarmId is set), the right panel shows MissionInspector with that swarm's topology, agent roster, and mission status.
- Success Criteria:
  - Navigating to ?swarm=X renders MissionInspector in the right panel
  - MissionInspector receives correct missionId, missionTitle, projectRoot, assignedAgents
  - Existing epicId and task branches are unaffected
  - npm run typecheck passes
- Scope:
  - src/components/activity/contextual-right-panel.tsx — add swarmId branch
  - MissionInspector at src/components/mission/mission-inspector.tsx (props: missionId, missionTitle, projectRoot, assignedAgents, onClose, onAssign)
  - Resolve assignedAgents: check useSwarmList hook or /api/agents — use whatever UnifiedShell already has available
  - onClose: clear swarmId from URL state; onAssign: call /api/mission/assign
- Out of Scope:
  - Building a new swarm inspector UI — MissionInspector already exists
  - Graph swarm overlay highlighting (Phase 3)

IMPLEMENTATION CONSTRAINTS
- Preserve existing backend/API contracts unless explicitly stated otherwise.
- Reuse shared components and logic; avoid one-off forks.
- Keep changes targeted and minimal for this bead.

VERIFICATION REQUIREMENTS
- Required commands:
  - npm run typecheck
  - npm run lint
  - npm run test
- Report any remaining risks and follow-up beads explicitly.


new_value: TaskId branch added to ContextualRightPanel: ThreadDrawer in embedded mode renders task thread and issue metadata when taskId is set. No new typecheck errors.


new_value: SwarmId branch added via SwarmIdBranch inner component (hooks-safe): MissionInspector renders with useSwarmList for title lookup. assignedAgents wired to swarm.agents (TODO: populate from agent-registry). No new typecheck errors.


new_value: All Phase 1 children complete: ContextualRightPanel now branches on epicId, taskId, swarmId, with ActivityPanel fallback. URL context contract fully wired.


title: Install mysql2 and create Dolt connection client

description:
TASK CONTEXT
- Bead ID: beadboard-550.1
- Parent: beadboard-550 (Replace issues.jsonl reads with direct Dolt SQL)
- Dependencies: none

TASK CONTRACT
- Goal: Install mysql2, create src/lib/dolt-client.ts that reads connection config from .beads/metadata.json and exports a pooled MySQL2 connection usable by other modules.
- Success Criteria:
  - mysql2 in package.json dependencies (not devDependencies)
  - src/lib/dolt-client.ts exports: getDoltConnection(projectRoot: string): Promise<mysql2.Pool>
  - Reads host/port/database from .beads/metadata.json (keys: dolt_server_port, dolt_database; host defaults to 127.0.0.1)
  - Gracefully throws a typed DoltConnectionError if metadata.json missing or server unreachable
  - npm run typecheck passes

IMPLEMENTATION CONSTRAINTS
- Use mysql2/promise (async/await API, not callback)
- Pool size: connectionLimit 5 (sufficient for Next.js server-side)
- Do not hardcode 3307 or 'beadboard' — always read from metadata.json
- No new API routes — this is a library module only

VERIFICATION REQUIREMENTS
- npm run typecheck
- npm run lint
- npm run test (existing tests must still pass)


notes: mysql2 installed (package.json dep). Created src/lib/dolt-client.ts: exports DoltConnectionError class and getDoltConnection(projectRoot) -> Promise<mysql.Pool>. Reads host/port/database from .beads/metadata.json, pools with connectionLimit 5, tests connectivity on first call, caches pool per resolved projectRoot. typecheck: only pre-existing status-badge.tsx TS2307. lint: pass. tests: 117/117 pass.


new_value: mysql2 installed; src/lib/dolt-client.ts written with pool caching, metadata.json config reading, DoltConnectionError, and connectivity test. All gates pass.


title: Implement readIssuesViaDolt() with JOIN across issues/deps/labels

description:
TASK CONTEXT
- Bead ID: beadboard-550.2
- Parent: beadboard-550
- Dependencies: beadboard-550.1 (dolt-client.ts must exist)

TASK CONTRACT
- Goal: Create src/lib/read-issues-dolt.ts with a readIssuesViaDolt() function that queries the Dolt SQL server and returns BeadIssue[] normalized identically to the existing JSONL parser output.
- Success Criteria:
  - readIssuesViaDolt(projectRoot, options) returns BeadIssue[] matching the shape from parseIssuesJsonl
  - Fetches issues + LEFT JOINs labels and dependencies in a single round-trip (or two queries max)
  - Applies same filters as readIssuesViaBd: excludes tombstones unless includeTombstones, excludes gt:agent labels unless skipAgentFilter
  - Returns null (not throws) if Dolt is unreachable so caller can fall back
  - npm run typecheck passes

IMPLEMENTATION CONSTRAINTS
- SQL query: SELECT issues.*, GROUP_CONCAT for labels, separate query for dependencies (avoid N+1)
- Map Dolt datetime columns to ISO string format matching BeadIssue.created_at etc.
- Dependencies table: issue_id, depends_on_id, type — map to BeadDependency[]
- Labels table: issue_id, label — map to string[]
- Reuse normalizeBdIssue logic from read-issues.ts where possible, or write equivalent

VERIFICATION REQUIREMENTS
- npm run typecheck
- npm run lint
- npm run test


new_value: Created src/lib/read-issues-dolt.ts: readIssuesViaDolt(projectRoot, options) queries Dolt via 2 SQL queries (issues+labels GROUP_CONCAT, then dependencies), normalizes rows to BeadIssue[], returns null on unreachable. typecheck+lint+tests pass.


title: Wire Dolt as primary read path in readIssuesFromDisk()

description:
TASK CONTEXT
- Bead ID: beadboard-550.3
- Parent: beadboard-550
- Dependencies: beadboard-550.2

TASK CONTRACT
- Goal: Replace the preferBd (bd CLI) path in readIssuesFromDisk() with readIssuesViaDolt(). Dolt is tried first; issues.jsonl is the fallback.
- Success Criteria:
  - readIssuesFromDisk() calls readIssuesViaDolt() when Dolt metadata is present
  - Falls back to issues.jsonl read if Dolt unreachable (same fallback chain as today)
  - preferBd option can now be removed or ignored (Dolt is always preferred when available)
  - No changes to any API routes or component files — only read-issues.ts changes
  - npm run typecheck && npm run lint && npm run test pass
  - Manual: create a bead via bd, hard-refresh frontend, confirm it appears without issues.jsonl re-export

IMPLEMENTATION CONSTRAINTS
- Do not remove issues.jsonl fallback yet (beadboard-550 scope only deprecates it)
- Do not change the return type or signature of readIssuesFromDisk()
- Log a console.warn (not throw) when falling back to issues.jsonl

VERIFICATION REQUIREMENTS
- npm run typecheck
- npm run lint
- npm run test
- Manual verification: bd create a test bead, confirm frontend shows it immediately


new_value: readIssuesFromDisk() now tries readIssuesViaDolt() first (always), falls back to issues.jsonl with console.warn. preferBd path kept for legacy but Dolt is always attempted. typecheck+lint+tests pass.


title: Verify watcher still fires on bd writes and document Dolt read path

description:
TASK CONTEXT
- Bead ID: beadboard-550.4
- Parent: beadboard-550
- Dependencies: beadboard-550.3

TASK CONTRACT
- Goal: Confirm SSE real-time updates still work after switching to Dolt reads. The watcher watches .last_touched and issues.jsonl; verify bd writes still trigger .last_touched so SSE pushes new data to the browser.
- Success Criteria:
  - Run bd update on an issue, confirm SSE event fires in the browser within 2s
  - If .last_touched is NOT updated by bd writes: add a workaround (e.g. touch .beads/last_touched after Dolt read detects staleness, or poll Dolt on a 5s interval as SSE trigger)
  - Update AGENTS.md Data Backend section to document the new Dolt read path and remove the manual issues.jsonl re-export instructions
  - Remove the issues.jsonl re-export command from AGENTS.md (no longer needed)
  - npm run typecheck && npm run lint && npm run test pass

IMPLEMENTATION CONSTRAINTS
- Do not rewrite the watcher — minimal change to make triggers work
- If polling is needed, use a simple setInterval in the SSE route, max 5s interval
- Document the decision in a code comment

VERIFICATION REQUIREMENTS
- npm run typecheck
- npm run lint
- npm run test
- Manual: bd update a bead status, watch browser Network tab for SSE event, confirm UI updates


notes: testing SSE watcher trigger


new_value: SSE watcher confirmed working: bd writes update last-touched, chokidar fires, syncActivity calls readIssuesFromDisk (now Dolt-first). AGENTS.md updated: replaced manual export instructions with explanation of new Dolt read path. All gates pass.


new_value: All 4 child tasks complete. BeadBoard now reads issues directly from Dolt SQL via mysql2. issues.jsonl kept as deprecated fallback. SSE realtime works. 146/146 tests pass.


new_value: All fixes implemented and tested. 14/14 tests passing. Conversation panel now opens on icon click, closes on second click, prioritizes task over epic, and back button navigates correctly.


notes: Identity UI pattern: editable chip. Shows actor name as pill in TopBar. Click → becomes text input. Blur or Enter → snaps back to chip. Placeholder 'your name' when empty. Uses local useState for editing vs display mode.

description:
TASK CONTEXT
- Bead ID: beadboard-0fi.1
- Title: Add actor state to UnifiedShell and identity input to TopBar
- Parent/Epic: beadboard-0fi
- Dependencies (must be done first): none (first in phase)

TASK CONTRACT
- Goal: Add a single operator identity string to UnifiedShell state, initialize it from localStorage('bb.humanActor') for backwards compatibility, persist changes back to localStorage, and render an editable identity field in TopBar so the operator can set their name once and have it flow to both drawers.
- Success Criteria:
  - UnifiedShell exposes actor: string and setActor: (name: string) => void
  - Initial value read from localStorage('bb.humanActor') if present, otherwise empty string
  - On change, value written back to localStorage('bb.humanActor')
  - TopBar renders a compact text input or editable chip showing current actor name
  - TopBar receives actor and onActorChange props (check current TopBarProps in src/components/shared/top-bar.tsx before editing)
  - npm run typecheck passes
- Scope:
  - src/components/shared/unified-shell.tsx — add useState for actor, pass to TopBar
  - src/components/shared/top-bar.tsx — add actor input UI and props
- Out of Scope:
  - Passing actor to drawers (that is beadboard-0fi.2 and beadboard-0fi.3)
  - Server-side persistence
  - Validation or auth

IMPLEMENTATION CONSTRAINTS
- Preserve existing backend/API contracts unless explicitly stated otherwise.
- Reuse shared components and logic; avoid one-off forks.
- Keep changes targeted and minimal for this bead.

VERIFICATION REQUIREMENTS
- Required commands:
  - npm run typecheck
  - npm run lint
  - npm run test
- Report any remaining risks and follow-up beads explicitly.


title: Add actor state to UnifiedShell and identity input to TopBar


notes:
Identity UI pattern: editable chip. Shows actor name as pill in TopBar. Click → becomes text input. Blur or Enter → snaps back to chip. Placeholder 'your name' when empty. Uses local useState for editing vs display mode.

IMPLEMENTATION PLAN (ready to execute next session):

1. src/components/shared/unified-shell.tsx
   - Add import: useState (already imported)
   - Add actor state near top of component (after existing useState declarations):
     const [actor, setActor] = useState<string>(() =>
       typeof window !== 'undefined' ? (window.localStorage.getItem('bb.humanActor') ?? '') : ''
     );
   - Add useEffect to persist on change:
     useEffect(() => {
       if (typeof window !== 'undefined') window.localStorage.setItem('bb.humanActor', actor);
     }, [actor]);
   - Pass to TopBar: actor={actor} onActorChange={setActor}

2. src/components/shared/top-bar.tsx
   - Add to TopBarProps: actor?: string; onActorChange?: (name: string) => void;
   - Add editable chip component inline (no new file needed):
     - Local state: const [editing, setEditing] = useState(false);
     - When !editing: render <button onClick={() => setEditing(true)} className='...'>
         {actor || 'your name'}
       </button>
     - When editing: render <input autoFocus value={actor} onChange={e => onActorChange?.(e.target.value)}
         onBlur={() => setEditing(false)} onKeyDown={e => e.key === 'Enter' && setEditing(false)}
         placeholder='your name' className='...' />
   - Place the chip in TopBar right section, before ThemeToggle

3. Run: npm run typecheck && npm run lint && npm run test
4. Close beadboard-0fi.1 then start beadboard-0fi.2


new_value: actor state in UnifiedShell, editable chip in TopBar, all checks pass


description:
TASK CONTEXT
- Bead ID: beadboard-0fi.2
- Title: Wire actor from shell to ConversationDrawer (remove localStorage read)
- Parent/Epic: beadboard-0fi
- Dependencies (must be done first): beadboard-0fi.1

TASK CONTRACT
- Goal: ConversationDrawer currently reads and writes actor identity from localStorage('bb.humanActor') at lines 125 and 167. Replace these with an actor prop received from UnifiedShell so identity is controlled by a single source of truth.
- Success Criteria:
  - ConversationDrawer accepts actor: string prop
  - localStorage.getItem('bb.humanActor') call at line 125 removed — replaced with prop
  - localStorage.setItem call at line 167 removed — UnifiedShell owns persistence
  - Comments still include actor in the POST body
  - npm run typecheck passes
- Scope:
  - src/components/sessions/conversation-drawer.tsx — replace localStorage reads with prop
  - src/components/shared/unified-shell.tsx — pass actor to ConversationDrawer call site
  - Verify ConversationDrawer is called in unified-shell.tsx or sessions-page.tsx and update accordingly
- Out of Scope:
  - ConversationDrawer UI changes
  - ThreadDrawer (that is beadboard-0fi.3)

IMPLEMENTATION CONSTRAINTS
- Preserve existing backend/API contracts unless explicitly stated otherwise.
- Reuse shared components and logic; avoid one-off forks.
- Keep changes targeted and minimal for this bead.

VERIFICATION REQUIREMENTS
- Required commands:
  - npm run typecheck
  - npm run lint
  - npm run test
- Report any remaining risks and follow-up beads explicitly.


new_value: actor prop added, localStorage reads/writes removed, syncs from prop via useEffect


description:
TASK CONTEXT
- Bead ID: beadboard-0fi.3
- Title: Wire actor from shell to ThreadDrawer postComment
- Parent/Epic: beadboard-0fi
- Dependencies (must be done first): beadboard-0fi.1

TASK CONTRACT
- Goal: ThreadDrawer currently posts comments with no actor field. Add an actor prop and include it in the comment POST body so comments from ThreadDrawer are attributed the same as ConversationDrawer comments.
- Success Criteria:
  - ThreadDrawer accepts actor?: string prop
  - Comment POST body includes actor field when prop is provided
  - UnifiedShell passes actor to ThreadDrawer at its call site in unified-shell.tsx
  - Posted comments visible in the thread show the correct actor attribution
  - npm run typecheck passes
- Scope:
  - src/components/shared/thread-drawer.tsx — add actor prop, include in POST
  - src/components/shared/unified-shell.tsx — pass actor to ThreadDrawer call site
  - Check thread-drawer.tsx postComment fetch call to find where to inject actor
- Out of Scope:
  - Changing the comment API endpoint schema
  - ConversationDrawer (that is beadboard-0fi.2)

IMPLEMENTATION CONSTRAINTS
- Preserve existing backend/API contracts unless explicitly stated otherwise.
- Reuse shared components and logic; avoid one-off forks.
- Keep changes targeted and minimal for this bead.

VERIFICATION REQUIREMENTS
- Required commands:
  - npm run typecheck
  - npm run lint
  - npm run test
- Report any remaining risks and follow-up beads explicitly.