+diff --git a/src/components/shared/thread-drawer.tsx b/src/components/shared/thread-drawer.tsx
+index 79e8978..e58380d 100644
+--- a/src/components/shared/thread-drawer.tsx
++++ b/src/components/shared/thread-drawer.tsx
+@@ -198,23 +198,24 @@ export function ThreadDrawer({
+ };
+
+ const handleCommentSubmit = async () => {
+- if (!projectRoot || !id || !comment.trim()) {
++ const targetIssueId = issue?.id ?? '';
++ if (!projectRoot || !targetIssueId || !comment.trim()) {
+ return;
+ }
+
+ setCommentState('sending');
+
+ try {
+- await postComment(projectRoot, id, comment.trim());
++ await postComment(projectRoot, targetIssueId, comment.trim());
+ setComment('');
+ setCommentState('sent');
+ // Refresh comments
+- const response = await fetch(`/api/beads/${id}/comments?projectRoot=${encodeURIComponent(projectRoot)}`);
++ const response = await fetch(`/api/beads/${targetIssueId}/comments?projectRoot=${encodeURIComponent(projectRoot)}`);
+ const payload = (await response.json()) as { ok: boolean; comments?: CommentFromApi[] };
+ if (payload.ok && payload.comments) {
+ setComments(payload.comments);
+ }
+- await onIssueUpdated?.(id);
++ await onIssueUpdated?.(targetIssueId);
+ setTimeout(() => setCommentState('ready'), 900);
+ } catch (error) {
+ console.error('Comment failed:', error);
+@@ -473,13 +474,13 @@ export function ThreadDrawer({
+ placeholder="Type a message to neighbors..."
+ className="border-0 bg-transparent text-[var(--ui-text-primary)] placeholder:text-[var(--ui-text-muted)]"
+ autoComplete="off"
+- disabled={commentState === 'sending'}
++ disabled={commentState === 'sending' || !issue || !projectRoot}
+ />
+
+diff --git a/src/components/shared/unified-shell.tsx b/src/components/shared/unified-shell.tsx
+index 9db756b..3dbbb13 100644
+--- a/src/components/shared/unified-shell.tsx
++++ b/src/components/shared/unified-shell.tsx
+@@ -19,6 +19,7 @@ import { ContextualRightPanel } from '../activity/contextual-right-panel';
+ import { AssignmentPanel } from '../graph/assignment-panel';
+ import { useSwarmList } from '../../hooks/use-swarm-list';
+ import { useBeadsSubscription } from '../../hooks/use-beads-subscription';
++import { useBdHealth } from '../../hooks/use-bd-health';
+
+ export interface UnifiedShellProps {
+ issues: BeadIssue[];
+@@ -55,6 +56,7 @@ export function UnifiedShell({
+
+ const socialCards = useMemo(() => buildSocialCards(issues), [issues]);
+ const { swarms: swarmCards } = useSwarmList(projectRoot);
++ const bdHealth = useBdHealth(projectRoot);
+
+ const selectedSocialCard = taskId ? socialCards.find(c => c.id === taskId) : null;
+ const selectedSwarmCard = swarmId ? swarmCards.find(c => c.swarmId === swarmId) : null;
+@@ -89,9 +91,11 @@ export function UnifiedShell({
+ }, []);
+
+ // Chat Mode Logic: If a card is selected (drawer='open'), we show Chat popup
+- const isChatOpen = drawer === 'open' && (!!taskId || !!swarmId);
+- const drawerTitle = selectedSocialCard?.title || selectedSwarmCard?.title || '';
+- const drawerId = taskId || swarmId || '';
++ const isChatOpen = drawer === 'open' && (!!taskId || !!swarmId || !!epicId);
++ const selectedEpic = epicId ? issues.find((issue) => issue.id === epicId && issue.issue_type === 'epic') ?? null : null;
++ const drawerTitle = selectedSocialCard?.title || selectedSwarmCard?.title || selectedEpic?.title || '';
++ const drawerId = taskId || swarmId || epicId || '';
++ const selectedItem = selectedEpic ?? selectedIssue;
+
+ // Panel resize hook
+ const { leftWidth, rightWidth, handleLeftResize, handleRightResize } = usePanelResize();
+@@ -149,6 +153,7 @@ export function UnifiedShell({
+ projectRoot={projectRoot}
+ issues={issues}
+ epicId={epicId ?? undefined}
++ onIssueUpdated={async () => { router.refresh(); }}
+ />
+ );
+ }
+@@ -161,6 +166,11 @@ export function UnifiedShell({
+
+ {/* TOP BAR: 3rem fixed */}
+
++ {!bdHealth.loading && !bdHealth.healthy ? (
++
++ BD setup issue: {bdHealth.message}
++
++ ) : null}
+
+ {/* MAIN AREA: Flex layout for resizable panels */}
+
{ setEpicId(id); setDrawer('open'); }}
+ filters={filters}
+ onFiltersChange={setFilters}
+ />
+@@ -209,7 +220,7 @@ export function UnifiedShell({
+ title={drawerTitle}
+ id={drawerId}
+ embedded={true}
+- issue={selectedIssue}
++ issue={selectedItem}
+ projectRoot={projectRoot}
+ onIssueUpdated={async () => {
+ router.refresh();
+diff --git a/src/hooks/use-panel-resize.ts b/src/hooks/use-panel-resize.ts
+index 1c74386..d3f2b80 100644
+--- a/src/hooks/use-panel-resize.ts
++++ b/src/hooks/use-panel-resize.ts
+@@ -10,25 +10,35 @@ export const MIN_LEFT_WIDTH = 192;
+ export const MIN_RIGHT_WIDTH = 256;
+
+ export function usePanelResize() {
+- const [leftWidth, setLeftWidth] = useState(() => {
+- if (typeof window === 'undefined') return DEFAULT_LEFT_WIDTH;
+- const saved = localStorage.getItem(LEFT_PANEL_KEY);
+- return saved ? parseInt(saved, 10) : DEFAULT_LEFT_WIDTH;
+- });
+-
+- const [rightWidth, setRightWidth] = useState(() => {
+- if (typeof window === 'undefined') return DEFAULT_RIGHT_WIDTH;
+- const saved = localStorage.getItem(RIGHT_PANEL_KEY);
+- return saved ? parseInt(saved, 10) : DEFAULT_RIGHT_WIDTH;
+- });
++ const [leftWidth, setLeftWidth] = useState(DEFAULT_LEFT_WIDTH);
++ const [rightWidth, setRightWidth] = useState(DEFAULT_RIGHT_WIDTH);
++ const [mounted, setMounted] = useState(false);
+
+ useEffect(() => {
+- localStorage.setItem(LEFT_PANEL_KEY, String(leftWidth));
+- }, [leftWidth]);
++ const savedLeft = localStorage.getItem(LEFT_PANEL_KEY);
++ const savedRight = localStorage.getItem(RIGHT_PANEL_KEY);
++
++ if (savedLeft) {
++ setLeftWidth(parseInt(savedLeft, 10));
++ }
++ if (savedRight) {
++ setRightWidth(parseInt(savedRight, 10));
++ }
++
++ setMounted(true);
++ }, []);
++
++ useEffect(() => {
++ if (mounted) {
++ localStorage.setItem(LEFT_PANEL_KEY, String(leftWidth));
++ }
++ }, [leftWidth, mounted]);
+
+ useEffect(() => {
+- localStorage.setItem(RIGHT_PANEL_KEY, String(rightWidth));
+- }, [rightWidth]);
++ if (mounted) {
++ localStorage.setItem(RIGHT_PANEL_KEY, String(rightWidth));
++ }
++ }, [rightWidth, mounted]);
+
+ const clampLeftWidth = useCallback((width: number) => {
+ const maxWidth = Math.floor(window.innerWidth * 0.30);
+diff --git a/src/lib/agent-sessions.ts b/src/lib/agent-sessions.ts
+index d46001b..6cc47e8 100644
+--- a/src/lib/agent-sessions.ts
++++ b/src/lib/agent-sessions.ts
+@@ -1,8 +1,13 @@
+ import type { ActivityEvent } from './activity';
+ import type { BeadIssue } from './types';
+ import { listAgents, deriveLiveness } from './agent-registry';
+-import { inboxAgentMessages, type AgentMessage } from './agent-mail';
+-import { statusAgentReservations, classifyOverlap } from './agent-reservations';
++import type { AgentMessage } from './agent-mail';
++import {
++ calculateReservationIncursions,
++ projectInboxFromDisk,
++ projectReservations,
++ readCoordEventsFromDisk,
++} from './coord-projections';
+
+ export type AgentSessionState = 'active' | 'reviewing' | 'deciding' | 'needs_input' | 'completed' | 'stale' | 'evicted' | 'idle' | 'stuck' | 'dead';
+
+@@ -148,56 +153,21 @@ export interface Incursion {
+ /**
+ * Calculates global incursions by comparing all active reservations.
+ */
+-export async function calculateIncursions(): Promise {
+- const statusResult = await statusAgentReservations({});
+- if (!statusResult.ok || !statusResult.data) return [];
+-
+- const reservations = statusResult.data.reservations;
+- const incursions: Incursion[] = [];
+- const processedPairs = new Set();
+-
+- for (let i = 0; i < reservations.length; i++) {
+- for (let j = i + 1; j < reservations.length; j++) {
+- const resA = reservations[i];
+- const resB = reservations[j];
+-
+- // Don't compare an agent against themselves
+- if (resA.agent_id === resB.agent_id) continue;
+-
+- const overlap = classifyOverlap(resA.scope, resB.scope);
+- if (overlap !== 'disjoint') {
+- const key = [resA.agent_id, resB.agent_id].sort().join(':') + ':' + [resA.scope, resB.scope].sort().join('|');
+- if (processedPairs.has(key)) continue;
+- processedPairs.add(key);
+-
+- incursions.push({
+- scope: overlap === 'exact' ? resA.scope : `${resA.scope} ↔ ${resB.scope}`,
+- agents: [resA.agent_id, resB.agent_id],
+- severity: overlap
+- });
+- }
+- }
+- }
+-
+- return incursions;
++export async function calculateIncursions(
++ projectRoot: string = process.cwd(),
++ agentLivenessMap: Record = {},
++): Promise {
++ const events = await readCoordEventsFromDisk(projectRoot);
++ const reservations = projectReservations(events, agentLivenessMap);
++ return calculateReservationIncursions(reservations);
+ }
+
+ /**
+ * Gathers all relevant communication for all agents to build a summary for aggregation.
+ */
+-export async function getCommunicationSummary(): Promise {
+- const agentsResult = await listAgents({});
+- const agents = agentsResult.data ?? [];
+- const allMessages: AgentMessage[] = [];
+-
+- for (const agent of agents) {
+- const inbox = await inboxAgentMessages({ agent: agent.agent_id });
+- if (inbox.data) {
+- allMessages.push(...inbox.data);
+- }
+- }
+-
+- return { messages: allMessages };
++export async function getCommunicationSummary(projectRoot: string = process.cwd()): Promise {
++ const coordMessages = await projectInboxFromDisk(projectRoot);
++ return { messages: coordMessages };
+ }
+
+ export interface AgentMetrics {
+diff --git a/src/lib/bd-path.ts b/src/lib/bd-path.ts
+deleted file mode 100644
+index 6ab3be3..0000000
+--- a/src/lib/bd-path.ts
++++ /dev/null
+@@ -1,78 +0,0 @@
+-import fs from 'node:fs/promises';
+-import path from 'node:path';
+-
+-export interface ResolveBdExecutableOptions {
+- explicitPath?: string | null;
+- env?: NodeJS.ProcessEnv;
+-}
+-
+-export interface BdExecutableResolution {
+- executable: string;
+- source: 'config' | 'path';
+-}
+-
+-export class BdExecutableNotFoundError extends Error {
+- readonly code = 'BD_NOT_FOUND';
+-
+- constructor(message: string) {
+- super(message);
+- this.name = 'BdExecutableNotFoundError';
+- }
+-}
+-
+-async function fileExists(filePath: string): Promise {
+- try {
+- await fs.access(filePath);
+- return true;
+- } catch {
+- return false;
+- }
+-}
+-
+-function splitEnvPath(env: NodeJS.ProcessEnv = process.env): string[] {
+- const value = env.Path ?? env.PATH ?? '';
+- if (!value.trim()) {
+- return [];
+- }
+-
+- return value.split(';').map((segment) => segment.trim()).filter(Boolean);
+-}
+-
+-function executableCandidates(directory: string): string[] {
+- return ['bd.exe', 'bd.cmd', 'bd.bat', 'bd'].map((name) => path.join(directory, name));
+-}
+-
+-function buildNotFoundMessage(explicitPath?: string | null): string {
+- const lines = [
+- 'bd.exe was not found.',
+- 'Install it with: npm install -g @beads/bd',
+- 'Or configure an explicit executable path in request payload/config.',
+- ];
+-
+- if (explicitPath) {
+- lines.push(`Configured path was not found: ${explicitPath}`);
+- }
+-
+- return lines.join(' ');
+-}
+-
+-export async function resolveBdExecutable(options: ResolveBdExecutableOptions = {}): Promise {
+- if (options.explicitPath && options.explicitPath.trim()) {
+- const explicit = path.resolve(options.explicitPath);
+- if (await fileExists(explicit)) {
+- return { executable: explicit, source: 'config' };
+- }
+-
+- throw new BdExecutableNotFoundError(buildNotFoundMessage(options.explicitPath));
+- }
+-
+- for (const dir of splitEnvPath(options.env)) {
+- for (const candidate of executableCandidates(dir)) {
+- if (await fileExists(candidate)) {
+- return { executable: candidate, source: 'path' };
+- }
+- }
+- }
+-
+- throw new BdExecutableNotFoundError(buildNotFoundMessage());
+-}
+diff --git a/src/lib/bridge.ts b/src/lib/bridge.ts
+index 4895e3a..9b0da60 100644
+--- a/src/lib/bridge.ts
++++ b/src/lib/bridge.ts
+@@ -1,10 +1,7 @@
+-import { exec as nodeExec } from 'node:child_process';
+-import { promisify } from 'node:util';
++import { spawn } from 'node:child_process';
+ import path from 'node:path';
+
+-import { BdExecutableNotFoundError, resolveBdExecutable } from './bd-path';
+-
+-const execAsync = promisify(nodeExec);
++import { normalizeProjectRootForRuntime } from './project-root';
+
+ export type BdFailureClassification = 'not_found' | 'timeout' | 'non_zero_exit' | 'bad_args' | 'unknown';
+
+@@ -12,7 +9,9 @@ export interface RunBdCommandOptions {
+ projectRoot: string;
+ args: string[];
+ timeoutMs?: number;
++ // Deprecated: accepted for payload compatibility, ignored by runner.
+ explicitBdPath?: string | null;
++ stdinText?: string;
+ }
+
+ export interface RunBdCommandResult {
+@@ -29,8 +28,10 @@ export interface RunBdCommandResult {
+ }
+
+ interface RunBdCommandDeps {
+- resolveBdExecutable: typeof resolveBdExecutable;
+- exec: (command: string, options: { cwd: string; timeout: number; env: NodeJS.ProcessEnv }) => Promise<{ stdout: string; stderr: string }>;
++ exec: (
++ command: string,
++ options: { cwd: string; timeout: number; env: NodeJS.ProcessEnv; stdinText?: string },
++ ) => Promise<{ stdout: string; stderr: string }>;
+ env: NodeJS.ProcessEnv;
+ }
+
+@@ -39,29 +40,51 @@ function normalizeOutput(text: unknown): string {
+ return text.replaceAll('\r\n', '\n').trim();
+ }
+
++function getExitCode(error: unknown): number | null {
++ if (!error || typeof error !== 'object') return null;
++ const value = (error as { exitCode?: unknown }).exitCode;
++ return typeof value === 'number' ? value : null;
++}
++
+ function toErrorMessage(value: unknown): string {
+ if (value instanceof Error) return value.message;
+ return String(value ?? 'Unknown error');
+ }
+
+ function classifyFailure(error: NodeJS.ErrnoException & { stderr?: string; killed?: boolean; signal?: string }): BdFailureClassification {
++ const exitCode = getExitCode(error);
+ if (error.code === 'ENOENT') return 'not_found';
+ if (error.code === 'ETIMEDOUT' || error.killed || error.signal === 'SIGTERM') return 'timeout';
+ const stderr = normalizeOutput(error.stderr);
+- if (typeof error.code === 'number') {
++ if (
++ /not recognized as an internal or external command/i.test(stderr) ||
++ /command not found/i.test(stderr) ||
++ /["']bd["'] is not recognized/i.test(stderr) ||
++ /bd: not found/i.test(stderr)
++ ) {
++ return 'not_found';
++ }
++ if (typeof error.code === 'number' || exitCode !== null) {
+ if (/(unknown|invalid|required|usage)/i.test(stderr)) return 'bad_args';
+ return 'non_zero_exit';
+ }
+ return 'unknown';
+ }
+
++function buildBdNotFoundMessage(): string {
++ return 'bd command not found in PATH. Install with: npm install -g @beads/bd';
++}
++
+ function buildShellCommand(executable: string, args: string[]): string {
++ const sanitizedExecutable = executable.replace(/^['"]+|['"]+$/g, '');
+ // Normalize to forward slashes for Windows shell compatibility
+- const normalizedExe = executable.split(path.sep).join('/');
++ const normalizedExe = sanitizedExecutable.split(path.sep).join('/');
+
+ if (process.platform === 'win32') {
+- // Windows: quote the executable path, leave simple args unquoted
+- const quotedExe = `"${normalizedExe}"`;
++ // Windows: do not quote plain command tokens like `bd`; quote only when needed.
++ const quotedExe = /[\s&|<>()^"]/.test(normalizedExe)
++ ? `"${normalizedExe.replace(/"/g, '""')}"`
++ : normalizedExe;
+ const quotedArgs = args.map(a => {
+ if (/[\s&|<>()^"]/.test(a)) return `"${a.replace(/"/g, '""')}"`;
+ return a;
+@@ -73,45 +96,109 @@ function buildShellCommand(executable: string, args: string[]): string {
+ }
+ }
+
++async function execShellCommand(
++ command: string,
++ options: { cwd: string; timeout: number; env: NodeJS.ProcessEnv; stdinText?: string },
++): Promise<{ stdout: string; stderr: string }> {
++ return new Promise((resolve, reject) => {
++ const shell = process.platform === 'win32' ? 'cmd.exe' : '/bin/sh';
++ const shellArgs = process.platform === 'win32' ? ['/d', '/s', '/c', command] : ['-lc', command];
++
++ const child = spawn(shell, shellArgs, {
++ cwd: options.cwd,
++ env: options.env,
++ stdio: 'pipe',
++ });
++
++ let stdout = '';
++ let stderr = '';
++ let timedOut = false;
++
++ const timer = setTimeout(() => {
++ timedOut = true;
++ child.kill('SIGTERM');
++ }, options.timeout);
++
++ child.stdout.on('data', (chunk: Buffer | string) => {
++ stdout += chunk.toString();
++ });
++ child.stderr.on('data', (chunk: Buffer | string) => {
++ stderr += chunk.toString();
++ });
++
++ child.on('error', (error) => {
++ clearTimeout(timer);
++ const wrapped = error as NodeJS.ErrnoException & { stdout?: string; stderr?: string };
++ wrapped.stdout = stdout;
++ wrapped.stderr = stderr;
++ reject(wrapped);
++ });
++
++ child.on('close', (code, signal) => {
++ clearTimeout(timer);
++ if (code === 0 && !timedOut) {
++ resolve({ stdout, stderr });
++ return;
++ }
++ const error = new Error(`Command failed with code ${code ?? 'null'}`) as NodeJS.ErrnoException & {
++ stdout?: string;
++ stderr?: string;
++ killed?: boolean;
++ signal?: string;
++ };
++ error.code = timedOut ? 'ETIMEDOUT' : 'BD_EXIT';
++ error.stdout = stdout;
++ error.stderr = stderr;
++ error.killed = timedOut;
++ error.signal = signal ?? undefined;
++ (error as { exitCode?: number }).exitCode = code ?? 1;
++ reject(error);
++ });
++
++ if (options.stdinText !== undefined) {
++ child.stdin.write(options.stdinText);
++ }
++ child.stdin.end();
++ });
++}
++
+ export async function runBdCommand(
+ options: RunBdCommandOptions,
+ injectedDeps?: Partial,
+ ): Promise {
+ const startedAt = Date.now();
+ const timeoutMs = options.timeoutMs ?? 30_000;
+- const cwd = options.projectRoot;
++ const cwd = normalizeProjectRootForRuntime(options.projectRoot);
+ const args = [...options.args];
+ if (process.env.BD_NO_DAEMON === 'true') {
+ args.unshift('--no-daemon');
+ }
+
+ const deps: RunBdCommandDeps = {
+- resolveBdExecutable: injectedDeps?.resolveBdExecutable ?? resolveBdExecutable,
+- exec: injectedDeps?.exec ?? execAsync,
++ exec: injectedDeps?.exec ?? execShellCommand,
+ env: injectedDeps?.env ?? process.env,
+ };
+
+- let command = options.explicitBdPath ?? 'bd';
++ const command = 'bd';
+
+ try {
+- const resolved = await deps.resolveBdExecutable({
+- explicitPath: options.explicitBdPath,
+- env: deps.env,
+- });
+- command = resolved.executable;
+-
+ const shellCommand = buildShellCommand(command, args);
+
+- const mingwBin = 'C:\\msys64\\mingw64\\bin';
+- const existingPath = deps.env.Path ?? deps.env.PATH ?? '';
+- const enhancedPath = existingPath.includes('mingw64')
+- ? existingPath
+- : `${mingwBin};${existingPath}`;
++ let env = deps.env;
++ if (process.platform === 'win32') {
++ const mingwBin = 'C:\\msys64\\mingw64\\bin';
++ const existingPath = deps.env.Path ?? deps.env.PATH ?? '';
++ const enhancedPath = existingPath.includes('mingw64')
++ ? existingPath
++ : `${mingwBin};${existingPath}`;
++ env = { ...deps.env, Path: enhancedPath, PATH: enhancedPath };
++ }
+
+ const { stdout, stderr } = await deps.exec(shellCommand, {
+ cwd,
+ timeout: timeoutMs,
+- env: { ...deps.env, Path: enhancedPath, PATH: enhancedPath },
++ env,
++ stdinText: options.stdinText,
+ });
+
+ return {
+@@ -127,39 +214,25 @@ export async function runBdCommand(
+ error: null,
+ };
+ } catch (rawError) {
+- if (rawError instanceof BdExecutableNotFoundError) {
+- return {
+- success: false,
+- classification: 'not_found',
+- command,
+- args,
+- cwd,
+- stdout: '',
+- stderr: '',
+- code: null,
+- durationMs: Date.now() - startedAt,
+- error: rawError.message,
+- };
+- }
+-
+ const error = rawError as NodeJS.ErrnoException & {
+ stderr?: string;
+ stdout?: string;
+ killed?: boolean;
+ signal?: string;
+ };
++ const classification = classifyFailure(error);
+
+ return {
+ success: false,
+- classification: classifyFailure(error),
++ classification,
+ command,
+ args,
+ cwd,
+ stdout: normalizeOutput(error.stdout),
+ stderr: normalizeOutput(error.stderr),
+- code: typeof error.code === 'number' ? error.code : null,
++ code: typeof error.code === 'number' ? error.code : getExitCode(error),
+ durationMs: Date.now() - startedAt,
+- error: toErrorMessage(error),
++ error: classification === 'not_found' ? buildBdNotFoundMessage() : toErrorMessage(error),
+ };
+ }
+ }
+diff --git a/src/lib/mutations.ts b/src/lib/mutations.ts
+index a7fc5a0..03d08bc 100644
+--- a/src/lib/mutations.ts
++++ b/src/lib/mutations.ts
+@@ -7,6 +7,7 @@ export type MutationStatus = 'open' | 'in_progress' | 'blocked' | 'deferred' | '
+ interface MutationBasePayload {
+ projectRoot: string;
+ bdPath?: string;
++ actor?: string;
+ }
+
+ export interface CreateMutationPayload extends MutationBasePayload {
+@@ -155,6 +156,7 @@ function parseBasePayload(raw: unknown): MutationBasePayload {
+ return {
+ projectRoot: asNonEmptyString(data.projectRoot, 'projectRoot'),
+ bdPath: asOptionalString(data.bdPath),
++ actor: asOptionalString(data.actor),
+ };
+ }
+
+@@ -235,7 +237,7 @@ function pushOptionalArg(args: string[], flag: string, value: string | undefined
+
+ function pushOptionalLabels(args: string[], labels: string[] | undefined): void {
+ if (labels && labels.length > 0) {
+- args.push('-l', labels.join(','));
++ args.push('--set-labels', labels.join(','));
+ }
+ }
+
+@@ -267,7 +269,7 @@ export function buildBdMutationArgs(operation: MutationOperation, payload: Mutat
+ pushOptionalArg(args, '-a', data.assignee);
+ pushOptionalLabels(args, data.labels);
+ if (data.metadata) {
+- args.push('--metadata', JSON.stringify(data.metadata));
++ args.push(`--metadata=${JSON.stringify(data.metadata)}`);
+ }
+ args.push('--json');
+ return args;
+@@ -303,11 +305,12 @@ export async function executeMutation(
+ deps: Partial = {},
+ ): Promise {
+ const runner = deps.runBdCommand ?? runBdCommand;
+- const args = buildBdMutationArgs(operation, payload);
++ const args = payload.actor
++ ? ['--actor', payload.actor, ...buildBdMutationArgs(operation, payload)]
++ : buildBdMutationArgs(operation, payload);
+ const command = await runner({
+ projectRoot: payload.projectRoot,
+ args,
+- explicitBdPath: payload.bdPath,
+ });
+
+ if (!command.success) {
+@@ -317,7 +320,7 @@ export async function executeMutation(
+ command,
+ error: {
+ classification: command.classification ?? 'unknown',
+- message: command.error ?? (command.stderr || 'Mutation command failed.'),
++ message: command.stderr || command.error || 'Mutation command failed.',
+ },
+ };
+ }
+diff --git a/src/styles/themes/light.css b/src/styles/themes/light.css
+index 8d8254a..6dca439 100644
+--- a/src/styles/themes/light.css
++++ b/src/styles/themes/light.css
+@@ -8,104 +8,135 @@
+ /* ==========================================================================
+ 1. SURFACE LAYERS - Softer greys, not pure white
+ ========================================================================== */
+- --surface-backdrop: #e2e8f0;
+- --surface-elevated: #f1f5f9;
+- --surface-primary: #e8edf5;
+- --surface-secondary: #f8fafc;
+- --surface-tertiary: #cbd5e1;
+- --surface-quaternary: #ffffff;
+- --surface-overlay: #f1f5f9;
+- --surface-input: #ffffff;
+- --surface-hover: rgba(15, 23, 42, 0.06);
+- --surface-active: rgba(59, 130, 246, 0.15);
++ --surface-backdrop: #e8edf3;
++ --surface-elevated: #f7f9fc;
++ --surface-primary: #dce4ee;
++ --surface-secondary: #f1f5f9;
++ --surface-tertiary: #e5ebf3;
++ --surface-quaternary: #f8fafc;
++ --surface-overlay: #eef3f8;
++ --surface-input: #fcfdff;
++ --surface-hover: rgba(15, 23, 42, 0.08);
++ --surface-active: rgba(37, 99, 235, 0.22);
+ --surface-tooltip: #1e293b;
+
+ /* ==========================================================================
+ 2. BORDERS - Visible grey
+ ========================================================================== */
+- --border-subtle: rgba(71, 85, 105, 0.2);
+- --border-default: rgba(71, 85, 105, 0.35);
+- --border-strong: rgba(51, 65, 85, 0.5);
+- --border-accent: rgba(37, 99, 235, 0.6);
++ --border-subtle: rgba(51, 65, 85, 0.28);
++ --border-default: rgba(51, 65, 85, 0.44);
++ --border-strong: rgba(30, 41, 59, 0.62);
++ --border-accent: rgba(29, 78, 216, 0.72);
+
+ /* ==========================================================================
+ 3. TEXT - Dark slate (NOT white!)
+ ========================================================================== */
+- --text-primary: #0f172a;
+- --text-secondary: #334155;
+- --text-tertiary: #64748b;
+- --text-disabled: #94a3b8;
++ --text-primary: #0b1324;
++ --text-secondary: #1f344e;
++ --text-tertiary: #4b6078;
++ --text-disabled: #7d92a9;
+ --text-inverse: #f8fafc;
+
+ /* ==========================================================================
+ 4. ACCENTS - Vibrant but not neon
+ ========================================================================== */
+- --accent-info: #2563eb;
+- --accent-success: #16a34a;
+- --accent-warning: #d97706;
+- --accent-danger: #dc2626;
++ --accent-info: #1d4ed8;
++ --accent-success: #15803d;
++ --accent-warning: #b45309;
++ --accent-danger: #b91c1c;
+
+ /* ==========================================================================
+ 5. ACCENT GLOWS - Subtle on light
+ ========================================================================== */
+- --glow-info: 0 0 16px rgba(37, 99, 235, 0.2);
+- --glow-success: 0 0 16px rgba(22, 163, 74, 0.2);
+- --glow-warning: 0 0 16px rgba(217, 119, 6, 0.2);
+- --glow-danger: 0 0 16px rgba(220, 38, 38, 0.2);
++ --glow-info: 0 0 18px rgba(29, 78, 216, 0.24);
++ --glow-success: 0 0 18px rgba(21, 128, 61, 0.22);
++ --glow-warning: 0 0 18px rgba(180, 83, 9, 0.24);
++ --glow-danger: 0 0 18px rgba(185, 28, 28, 0.22);
+
+ /* ==========================================================================
+ 6. GRAPH COLORS
+ ========================================================================== */
+- --graph-node-default: rgba(255, 255, 255, 0.95);
+- --graph-node-epic: rgba(37, 99, 235, 0.15);
+- --graph-edge-default: rgba(71, 85, 105, 0.4);
+- --graph-edge-selected: #2563eb;
+- --graph-edge-cycle: #d97706;
++ --graph-node-default: rgba(248, 250, 252, 0.96);
++ --graph-node-epic: rgba(29, 78, 216, 0.18);
++ --graph-edge-default: rgba(51, 65, 85, 0.48);
++ --graph-edge-selected: #1d4ed8;
++ --graph-edge-cycle: #b45309;
+
+ /* ==========================================================================
+ 7. SEMANTIC ALPHAS - Dark overlays for light theme
+ ========================================================================== */
+- --alpha-white-low: rgba(255, 255, 255, 0.6);
+- --alpha-white-medium: rgba(255, 255, 255, 0.8);
+- --alpha-white-high: rgba(255, 255, 255, 0.95);
+- --alpha-black-low: rgba(15, 23, 42, 0.06);
+- --alpha-black-medium: rgba(15, 23, 42, 0.12);
+- --alpha-black-high: rgba(15, 23, 42, 0.2);
++ --alpha-white-low: rgba(255, 255, 255, 0.55);
++ --alpha-white-medium: rgba(255, 255, 255, 0.75);
++ --alpha-white-high: rgba(255, 255, 255, 0.9);
++ --alpha-black-low: rgba(15, 23, 42, 0.08);
++ --alpha-black-medium: rgba(15, 23, 42, 0.15);
++ --alpha-black-high: rgba(15, 23, 42, 0.24);
+
+ /* ==========================================================================
+ 8. STATUS COLORS - More opaque for visibility
+ ========================================================================== */
+- --status-ready: rgba(22, 163, 74, 0.2);
+- --status-in-progress: rgba(217, 119, 6, 0.2);
+- --status-blocked: rgba(220, 38, 38, 0.2);
+- --status-closed: rgba(100, 116, 139, 0.15);
+- --status-deferred: rgba(100, 116, 139, 0.1);
++ --status-ready: rgba(21, 128, 61, 0.22);
++ --status-in-progress: rgba(180, 83, 9, 0.22);
++ --status-blocked: rgba(185, 28, 28, 0.22);
++ --status-closed: rgba(71, 85, 105, 0.18);
++ --status-deferred: rgba(71, 85, 105, 0.13);
+
+ /* ==========================================================================
+ 9. SHADOWS - Softer on light
+ ========================================================================== */
+- --shadow-sm: 0 1px 3px rgba(15, 23, 42, 0.1);
+- --shadow-md: 0 4px 6px rgba(15, 23, 42, 0.12);
+- --shadow-lg: 0 10px 15px rgba(15, 23, 42, 0.15);
++ --shadow-sm: 0 1px 2px rgba(15, 23, 42, 0.14);
++ --shadow-md: 0 6px 14px rgba(15, 23, 42, 0.16);
++ --shadow-lg: 0 14px 26px rgba(15, 23, 42, 0.2);
+
+ /* ==========================================================================
+ 10. AGENT ROLE COLORS
+ ========================================================================== */
+- --agent-role-ui: #2563eb;
+- --agent-role-graph: #16a34a;
++ --agent-role-ui: #1d4ed8;
++ --agent-role-graph: #15803d;
+ --agent-role-orchestrator: #7c3aed;
+ --agent-role-researcher: #ea580c;
+
+ /* ==========================================================================
+ 11. SCROLLBARS
+ ========================================================================== */
+- --scrollbar-track: rgba(15, 23, 42, 0.05);
+- --scrollbar-thumb: rgba(100, 116, 139, 0.3);
+- --scrollbar-thumb-hover: rgba(71, 85, 105, 0.45);
++ --scrollbar-track: rgba(15, 23, 42, 0.08);
++ --scrollbar-thumb: rgba(71, 85, 105, 0.38);
++ --scrollbar-thumb-hover: rgba(51, 65, 85, 0.52);
+
+ /* ==========================================================================
+ 12. CODE/SYNTAX
+ ========================================================================== */
+- --code-background: #e2e8f0;
+- --code-text: #334155;
++ --code-background: #dbe5f1;
++ --code-text: #1f344e;
++
++ /* ==========================================================================
++ 14. LEGACY UI MAPPINGS - For components using --ui-* variables
++ ========================================================================== */
++ --ui-bg-app: var(--surface-backdrop);
++ --ui-bg-header: var(--surface-primary);
++ --ui-bg-shell: var(--surface-primary);
++ --ui-bg-panel: var(--surface-tertiary);
++ --ui-bg-main: var(--surface-backdrop);
++ --ui-bg-card: var(--surface-quaternary);
++ --ui-bg-elevated: var(--surface-elevated);
++
++ --ui-border-soft: var(--border-subtle);
++ --ui-border-strong: var(--border-default);
++
++ --ui-text-primary: var(--text-primary);
++ --ui-text-muted: var(--text-tertiary);
++
++ --ui-accent-ready: var(--accent-success);
++ --ui-accent-blocked: var(--accent-danger);
++ --ui-accent-warning: var(--accent-warning);
++ --ui-accent-info: var(--accent-info);
++}
++
++[data-theme="light"] body {
++ background-color: var(--ui-bg-app);
++ background-image:
++ radial-gradient(1200px 520px at 24% -12%, rgba(29, 78, 216, 0.09), transparent 62%),
++ radial-gradient(980px 420px at 82% 18%, rgba(21, 128, 61, 0.07), transparent 58%),
++ linear-gradient(180deg, color-mix(in srgb, var(--ui-bg-app) 92%, white), var(--ui-bg-main));
++ background-attachment: fixed;
+ }
+diff --git a/tests/components/unified-shell.test.tsx b/tests/components/unified-shell.test.tsx
+index d2661a6..0c2d538 100644
+--- a/tests/components/unified-shell.test.tsx
++++ b/tests/components/unified-shell.test.tsx
+@@ -40,6 +40,12 @@ test('UnifiedShell - imports AssignmentPanel', async () => {
+ assert.ok(fileContent.includes('AssignmentPanel'), 'Should import AssignmentPanel');
+ });
+
++test('UnifiedShell - checks bd health and renders setup warning', async () => {
++ const fileContent = await fs.readFile(path.join(process.cwd(), 'src/components/shared/unified-shell.tsx'), 'utf-8');
++ assert.ok(fileContent.includes('useBdHealth'), 'Should use bd health hook');
++ assert.ok(fileContent.includes('BD setup issue:'), 'Should show bd setup warning text');
++});
++
+ // Test that AssignmentPanel is rendered conditionally based on view and assignMode
+ test('UnifiedShell - renders AssignmentPanel conditionally', async () => {
+ const fileContent = await fs.readFile(path.join(process.cwd(), 'src/components/shared/unified-shell.tsx'), 'utf-8');
+diff --git a/tests/lib/bd-path.test.ts b/tests/lib/bd-path.test.ts
+deleted file mode 100644
+index bc7d5ed..0000000
+--- a/tests/lib/bd-path.test.ts
++++ /dev/null
+@@ -1,43 +0,0 @@
+-import test from 'node:test';
+-import assert from 'node:assert/strict';
+-import fs from 'node:fs/promises';
+-import os from 'node:os';
+-import path from 'node:path';
+-
+-import { BdExecutableNotFoundError, resolveBdExecutable } from '../../src/lib/bd-path';
+-
+-test('resolveBdExecutable prefers explicit configured path when provided', async () => {
+- const temp = await fs.mkdtemp(path.join(os.tmpdir(), 'beadboard-bd-path-'));
+- const explicit = path.join(temp, 'tools', 'bd.exe');
+- await fs.mkdir(path.dirname(explicit), { recursive: true });
+- await fs.writeFile(explicit, '');
+-
+- const resolved = await resolveBdExecutable({ explicitPath: explicit, env: { Path: '', NODE_ENV: 'test' } });
+-
+- assert.equal(resolved.executable, explicit);
+- assert.equal(resolved.source, 'config');
+-});
+-
+-test('resolveBdExecutable finds bd.exe on PATH when explicit path is not set', async () => {
+- const temp = await fs.mkdtemp(path.join(os.tmpdir(), 'beadboard-bd-path-env-'));
+- const candidate = path.join(temp, 'bd.exe');
+- await fs.writeFile(candidate, '');
+-
+- const resolved = await resolveBdExecutable({ env: { Path: temp, NODE_ENV: 'test' } });
+-
+- assert.equal(resolved.executable, candidate);
+- assert.equal(resolved.source, 'path');
+-});
+-
+-test('resolveBdExecutable throws actionable setup guidance when executable is missing', async () => {
+- await assert.rejects(
+- () => resolveBdExecutable({ env: { Path: '', NODE_ENV: 'test' } }),
+- (error: unknown) => {
+- assert.equal(error instanceof BdExecutableNotFoundError, true);
+- const message = String((error as Error).message).toLowerCase();
+- assert.equal(message.includes('npm install -g @beads/bd'), true);
+- assert.equal(message.includes('bd.exe'), true);
+- return true;
+- },
+- );
+-});
+diff --git a/tests/lib/bridge.test.ts b/tests/lib/bridge.test.ts
+index d4a2259..91077c7 100644
+--- a/tests/lib/bridge.test.ts
++++ b/tests/lib/bridge.test.ts
+@@ -2,6 +2,7 @@ import test from 'node:test';
+ import assert from 'node:assert/strict';
+
+ import { runBdCommand } from '../../src/lib/bridge';
++import { normalizeProjectRootForRuntime } from '../../src/lib/project-root';
+
+ test('runBdCommand returns structured success payload from exec output', async () => {
+ const result = await runBdCommand(
+@@ -12,12 +13,11 @@ test('runBdCommand returns structured success payload from exec output', async (
+ explicitBdPath: 'C:/tools/bd.exe',
+ },
+ {
+- resolveBdExecutable: async () => ({ executable: 'C:/tools/bd.exe', source: 'config' }),
+ exec: async (command: string, options: any) => {
+- assert.ok(command.includes('bd'));
++ assert.ok(command.startsWith('bd '));
+ assert.ok(command.includes('list'));
+ assert.ok(command.includes('--json'));
+- assert.equal(options.cwd, 'C:/repo/project');
++ assert.equal(options.cwd, normalizeProjectRootForRuntime('C:/repo/project'));
+ return { stdout: '[{"id":"bb-1"}]\r\n', stderr: '' };
+ },
+ },
+@@ -32,7 +32,6 @@ test('runBdCommand classifies missing executable as not_found', async () => {
+ const result = await runBdCommand(
+ { projectRoot: 'C:/repo/project', args: ['list'] },
+ {
+- resolveBdExecutable: async () => ({ executable: 'C:/tools/bd.exe', source: 'config' }),
+ exec: async () => {
+ const error = new Error('spawn ENOENT') as NodeJS.ErrnoException;
+ error.code = 'ENOENT';
+@@ -49,7 +48,6 @@ test('runBdCommand classifies timeout failures', async () => {
+ const result = await runBdCommand(
+ { projectRoot: 'C:/repo/project', args: ['list'], timeoutMs: 5 },
+ {
+- resolveBdExecutable: async () => ({ executable: 'C:/tools/bd.exe', source: 'config' }),
+ exec: async () => {
+ const error = new Error('timed out') as NodeJS.ErrnoException & { killed?: boolean; signal?: string };
+ error.code = 'ETIMEDOUT';
+@@ -68,7 +66,6 @@ test('runBdCommand classifies non-zero bad-argument exits', async () => {
+ const result = await runBdCommand(
+ { projectRoot: 'C:/repo/project', args: ['update', '--bad-flag'] },
+ {
+- resolveBdExecutable: async () => ({ executable: 'C:/tools/bd.exe', source: 'config' }),
+ exec: async () => {
+ const error = new Error('exit code 1') as NodeJS.ErrnoException & {
+ stdout?: string;
+@@ -85,3 +82,26 @@ test('runBdCommand classifies non-zero bad-argument exits', async () => {
+ assert.equal(result.success, false);
+ assert.equal(result.classification, 'bad_args');
+ });
++
++test('runBdCommand treats shell "not recognized" stderr as not_found', async () => {
++ const result = await runBdCommand(
++ { projectRoot: 'C:/repo/project', args: ['list'] },
++ {
++ exec: async () => {
++ const error = new Error('exit code 1') as NodeJS.ErrnoException & {
++ stdout?: string;
++ stderr?: string;
++ exitCode?: number;
++ };
++ error.code = 'BD_EXIT';
++ error.stderr = `'bd' is not recognized as an internal or external command`;
++ error.exitCode = 1;
++ throw error;
++ },
++ },
++ );
++
++ assert.equal(result.success, false);
++ assert.equal(result.classification, 'not_found');
++ assert.equal(result.error?.includes('bd command not found in PATH'), true);
++});
+diff --git a/tests/lib/mutations.test.ts b/tests/lib/mutations.test.ts
+index dbf0aed..06bd1b8 100644
+--- a/tests/lib/mutations.test.ts
++++ b/tests/lib/mutations.test.ts
+@@ -63,7 +63,7 @@ test('executeMutation surfaces bridge failures in normalized response', async ()
+ return {
+ success: false,
+ classification: 'non_zero_exit',
+- command: 'bd.exe',
++ command: 'bd',
+ args,
+ cwd: root,
+ stdout: '',
+@@ -93,7 +93,7 @@ test('executeMutation returns successful normalized response', async () => {
+ return {
+ success: true,
+ classification: null,
+- command: 'bd.exe',
++ command: 'bd',
+ args,
+ cwd: root,
+ stdout: '{"id":"bb-123"}',
+@@ -109,3 +109,62 @@ test('executeMutation returns successful normalized response', async () => {
+ assert.equal(result.operation, 'update');
+ assert.equal(result.command.success, true);
+ });
++
++test('executeMutation includes --actor when provided in payload', async () => {
++ const payload = validateMutationPayload('comment', {
++ projectRoot: root,
++ id: 'bb-123',
++ text: 'Operator note',
++ actor: 'zenchant',
++ });
++
++ const result = await executeMutation('comment', payload, {
++ runBdCommand: async ({ args }) => {
++ assert.deepEqual(args, ['--actor', 'zenchant', 'comments', 'add', 'bb-123', 'Operator note', '--json']);
++ return {
++ success: true,
++ classification: null,
++ command: 'bd',
++ args,
++ cwd: root,
++ stdout: '{"ok":true}',
++ stderr: '',
++ code: 0,
++ durationMs: 2,
++ error: null,
++ };
++ },
++ });
++
++ assert.equal(result.ok, true);
++});
++
++test('executeMutation ignores bdPath and uses default runner contract', async () => {
++ const payload = validateMutationPayload('update', {
++ projectRoot: root,
++ id: 'bb-123',
++ status: 'in_progress',
++ bdPath: 'C:/Tools/beads/bd.exe',
++ });
++
++ const result = await executeMutation('update', payload, {
++ runBdCommand: async (options) => {
++ assert.equal(options.explicitBdPath, undefined);
++ assert.deepEqual(options.args, ['update', 'bb-123', '-s', 'in_progress', '--json']);
++ return {
++ success: true,
++ classification: null,
++ command: 'bd',
++ args: options.args,
++ cwd: root,
++ stdout: '{"ok":true}',
++ stderr: '',
++ code: 0,
++ durationMs: 2,
++ error: null,
++ };
++ },
++ });
++
++ assert.equal(result.ok, true);
++});
+diff --git a/tsconfig.json b/tsconfig.json
+index e0d07bd..23ebab9 100644
+--- a/tsconfig.json
++++ b/tsconfig.json
+@@ -20,5 +20,5 @@
+ }
+ },
+ "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
+- "exclude": ["node_modules"]
++ "exclude": ["node_modules", "reference/**"]
+ }
diff --git a/tmp_status.txt b/tmp_status.txt
new file mode 100644
index 0000000..32996c9
--- /dev/null
+++ b/tmp_status.txt
@@ -0,0 +1,87 @@
+On branch feat/themev2
+Changes not staged for commit:
+ (use "git add/rm ..." to update what will be committed)
+ (use "git restore ..." to discard changes in working directory)
+ modified: .beads/.gitignore
+ modified: .beads/config.yaml
+ modified: .beads/metadata.json
+ modified: AGENTS.md
+ modified: README.md
+ modified: docs/protocols/operative-protocol-v1.md
+ modified: eslint.config.mjs
+ modified: next.config.ts
+ modified: package.json
+ modified: src/app/api/beads/_shared.ts
+ modified: src/app/api/sessions/[beadId]/conversation/route.ts
+ modified: src/app/api/sessions/route.ts
+ modified: src/app/api/swarm/list/route.ts
+ modified: src/app/api/swarm/status/route.ts
+ modified: src/app/globals.css
+ deleted: src/app/graph/page.tsx
+ deleted: src/app/mockup/page.tsx
+ deleted: src/app/page-old.tsx
+ deleted: src/app/sessions/page.tsx
+ deleted: src/app/timeline/page.tsx
+ modified: src/components/activity/activity-panel.tsx
+ modified: src/components/activity/swarm-command-feed.tsx
+ modified: src/components/graph/assignment-panel.tsx
+ modified: src/components/kanban/kanban-page.tsx
+ modified: src/components/sessions/conversation-drawer.tsx
+ modified: src/components/shared/left-panel.tsx
+ modified: src/components/shared/thread-drawer.tsx
+ modified: src/components/shared/unified-shell.tsx
+ modified: src/hooks/use-panel-resize.ts
+ modified: src/lib/agent-sessions.ts
+ deleted: src/lib/bd-path.ts
+ modified: src/lib/bridge.ts
+ modified: src/lib/mutations.ts
+ modified: src/styles/themes/light.css
+ modified: tests/components/unified-shell.test.tsx
+ deleted: tests/lib/bd-path.test.ts
+ modified: tests/lib/bridge.test.ts
+ modified: tests/lib/mutations.test.ts
+ modified: tsconfig.json
+
+Untracked files:
+ (use "git add ..." to include in what will be committed)
+ .beads/dolt-backup-20260228-101523/
+ .beads/dolt-config.log
+ .beads/dolt-sql-server.log
+ .beads/dolt-sql-server.pid
+ .beads/hooks/
+ .beads/recovery-20260228-100628/
+ docs/plans/2026-02-28-bd-only-coordination-migration-plan.md
+ docs/prompts/2026-02-28-next-session-dolt-repair.md
+ docs/prompts/2026-02-28-next-session-holistic-ux-critique.md
+ docs/prompts/2026-02-28-next-session-swarm-ux.md
+ docs/prompts/remotion.md
+ docs/protocols/2026-02-28-agent-first-ui-decisions.md
+ docs/protocols/2026-02-28-bb-deprecation-notes.md
+ docs/protocols/2026-02-28-bd-audit-coordination-schema.md
+ image-2.png
+ image-3.png
+ image-4.png
+ image-5.png
+ image-6.png
+ image-7.png
+ image-8.png
+ image-9.png
+ reference/
+ src/app/api/bd/
+ src/app/api/coord/
+ src/hooks/use-bd-health.ts
+ src/lib/coord-events.ts
+ src/lib/coord-projections.ts
+ src/lib/coord-schema.ts
+ src/lib/project-root.ts
+ tests/api/bd-health-route.test.ts
+ tests/api/coord-events-route.test.ts
+ tests/components/sessions/conversation-drawer-coord.test.tsx
+ tests/lib/coord-events.test.ts
+ tests/lib/coord-projections-inbox.test.ts
+ tests/lib/coord-projections-reservations.test.ts
+ tests/lib/coord-schema.test.ts
+ tmp_diff.txt
+ tmp_status.txt
+
+no changes added to commit (use "git add" and/or "git commit -a")
diff --git a/tsconfig.json b/tsconfig.json
index e0d07bd..23ebab9 100644
--- a/tsconfig.json
+++ b/tsconfig.json
@@ -20,5 +20,5 @@
}
},
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
- "exclude": ["node_modules"]
+ "exclude": ["node_modules", "reference/**"]
}