diff --git a/dashboard/src/components/activity/ActivityTimeline.tsx b/dashboard/src/components/activity/ActivityTimeline.tsx index ec530371..8b3e5932 100644 --- a/dashboard/src/components/activity/ActivityTimeline.tsx +++ b/dashboard/src/components/activity/ActivityTimeline.tsx @@ -1737,12 +1737,12 @@ function summarizeStatusUpdatesForCard(item: LiveActivityItem): string | null { if (isBuffered) { if (updates.length > 0) { const first = updates[0]; - return `Queued ${updates.length} update${updates.length === 1 ? '' : 's'}: ${first.label}${first.status ? ` → ${first.status}` : ''}`; + return `Updates being applied: ${updates.length} queued update${updates.length === 1 ? '' : 's'} · ${first.label}${first.status ? ` → ${first.status}` : ''}`; } if (statusUpdatesApplied !== null && statusUpdatesApplied > 0) { - return `Queued ${statusUpdatesApplied} status update${statusUpdatesApplied === 1 ? '' : 's'} for sync`; + return `Updates being applied: ${statusUpdatesApplied} status update${statusUpdatesApplied === 1 ? '' : 's'} queued for sync`; } - return 'Queued status updates for sync'; + return 'Updates being applied: status updates queued for sync'; } if (statusUpdatesApplied !== null && statusUpdatesApplied > 0) { @@ -6473,20 +6473,25 @@ export const ActivityTimeline = memo(function ActivityTimeline({ const commitSha = metadataString(outcomes, ['commit_sha', 'commitSha']); const commitUrl = metadataString(outcomes, ['commit_url', 'commitUrl']); const tests = asMetadataRecord(outcomes?.tests) as { passed?: number; failed?: number; skipped?: number } | null; + const showStatusUpdatesPanel = + statusUpdates.length > 0 || + (statusUpdatesApplied ?? 0) > 0 || + statusBuffered; + const showUpdatesInOutcomes = + showStatusUpdatesPanel && + (!activeArtifact || eventName.includes('status_updates')); const hasAny = prUrl || commitSha || tests || - statusUpdates.length > 0 || - (statusUpdatesApplied ?? 0) > 0 || - statusBuffered; + showUpdatesInOutcomes; if (!hasAny) return null; return (

- Updates & outcomes + {showUpdatesInOutcomes ? 'Updates & outcomes' : 'Outcomes'}

- {(statusUpdates.length > 0 || (statusUpdatesApplied ?? 0) > 0 || statusBuffered) && ( + {showUpdatesInOutcomes && (
diff --git a/dashboard/src/components/agents/AgentAvatar.tsx b/dashboard/src/components/agents/AgentAvatar.tsx index a7bf2196..4fbf7373 100644 --- a/dashboard/src/components/agents/AgentAvatar.tsx +++ b/dashboard/src/components/agents/AgentAvatar.tsx @@ -1,5 +1,6 @@ import { useMemo, useState } from 'react'; import { getAgentColor, getInitials } from '@/lib/tokens'; +import { UserFractalAvatar } from '@/components/settings/UserFractalAvatar'; interface AgentAvatarProps { name: string; @@ -14,6 +15,15 @@ const sizeMap = { md: 'w-10 h-10 text-body', lg: 'w-16 h-16 text-title', }; +const sizePxMap = { + xs: 24, + sm: 32, + md: 40, + lg: 64, +} as const; + +const USER_NAME_KEY = 'orgx.user.display-name'; +const USER_SEED_SUFFIX_KEY = 'orgx.user.avatar-seed-suffix'; const baseUrl = '/orgx/live/'; const withBaseUrl = (path: string) => `${baseUrl.replace(/\/+$/, '/')}${path}`; @@ -66,6 +76,30 @@ function resolveAgentAvatar(...hints: Array): string return null; } +function normalizeIdentity(value: string | null | undefined): string { + return typeof value === 'string' ? value.trim().toLowerCase() : ''; +} + +function isUserIdentity(value: string | null | undefined): boolean { + const normalized = normalizeIdentity(value); + if (!normalized) return false; + if (normalized === 'you' || normalized === 'main' || normalized === 'me') return true; + if (normalized === 'user' || normalized === 'usr') return true; + if (normalized.startsWith('user_') || normalized.startsWith('usr_')) return true; + return false; +} + +function readUserAvatarSeed(): string { + if (typeof window === 'undefined') return 'orgx-user'; + try { + const displayName = (window.localStorage.getItem(USER_NAME_KEY) ?? '').trim(); + const seedSuffix = window.localStorage.getItem(USER_SEED_SUFFIX_KEY) ?? ''; + return `${displayName || 'orgx-user'}${seedSuffix}`; + } catch { + return 'orgx-user'; + } +} + export function AgentAvatar({ name, size = 'xs', @@ -74,12 +108,20 @@ export function AgentAvatar({ }: AgentAvatarProps) { const color = getAgentColor(name); const [failedToLoad, setFailedToLoad] = useState(false); + const showUserAvatar = useMemo( + () => isUserIdentity(name) || isUserIdentity(hint), + [hint, name] + ); + const userAvatarSeed = useMemo( + () => (showUserAvatar ? readUserAvatarSeed() : null), + [showUserAvatar] + ); const avatarSrc = useMemo(() => { if (src && src.trim()) return src; return resolveAgentAvatar(name, hint); }, [hint, name, src]); - const showImage = Boolean(avatarSrc && !failedToLoad); + const showImage = Boolean(!showUserAvatar && avatarSrc && !failedToLoad); return (
- {showImage ? ( + {showUserAvatar && userAvatarSeed ? ( + + ) : showImage ? ( {name} (
{/* Initiative heading */} -

- {sanitizeDisplayText( - initGroup.initiativeTitle && !isOpaqueId(initGroup.initiativeTitle) - ? initGroup.initiativeTitle - : compactOpaqueLabel(initGroup.initiativeId, 'Initiative') - )} -

+
+ +

+ {sanitizeDisplayText( + initGroup.initiativeTitle && !isOpaqueId(initGroup.initiativeTitle) + ? initGroup.initiativeTitle + : compactOpaqueLabel(initGroup.initiativeId, 'Initiative') + )} +

+
{/* Workstream subsections */} {initGroup.workstreams.map((wsGroup) => { @@ -671,20 +674,15 @@ export const InProgressPanel = memo(function InProgressPanel({ : firstRow?.progress != null ? coerceProgress(firstRow.progress) : null; - const workstreamHeading = sanitizeDisplayText( - firstRow?.workstreamTitle && !isOpaqueId(firstRow.workstreamTitle) - ? firstRow.workstreamTitle - : compactOpaqueLabel(wsGroup.workstreamId, 'Workstream') - ); - return (
- {/* Workstream header with agent persona */} -
+ {/* Workstream context row */} +
+ {agentPersona && (
)} -
-

- {workstreamHeading} + {agentPersona ? ( +

+ {agentPersona.displayLabel}

- {agentPersona ? ( -

- {agentPersona.displayLabel} -

- ) : null} -
+ ) : ( + Workstream + )}
{/* Task position + mini progress bar */} @@ -730,8 +725,7 @@ export const InProgressPanel = memo(function InProgressPanel({
)} - {/* Workstream headers intentionally omit relative time to avoid duplicate cadence signals. - The active row card carries the canonical "last updated" stamp. */} + {/* Header intentionally omits workstream title to avoid duplicate copy with the card title. */}
{/* Active rows */} @@ -921,6 +915,14 @@ function InProgressRowCard({ ? sliceRunByScope.get(`${row.initiativeId}:${row.workstreamId}`) ?? null : null) ?? (row.runId ? sliceRunByScope.get(`run:${row.runId}`) ?? null : null); + const rowEntityType = + row.scope === 'task' + ? 'task' + : row.scope === 'milestone' + ? 'milestone' + : row.scope === 'workstream' || row.source === 'slice' + ? 'workstream' + : 'session'; // Resolve agent persona for the row card const persona = resolveAgentPersona(row.session?.agentId, row.session?.agentName); @@ -966,7 +968,7 @@ function InProgressRowCard({

) : null}
- +

{rowTitleDisplay}

diff --git a/dashboard/src/data/mockData.ts b/dashboard/src/data/mockData.ts index e5a6b514..abfc01da 100644 --- a/dashboard/src/data/mockData.ts +++ b/dashboard/src/data/mockData.ts @@ -6,6 +6,8 @@ export const createMockData = (): LiveData => { new Date(now.getTime() - m * 60 * 1000).toISOString(); const minusHours = (h: number) => new Date(now.getTime() - h * 60 * 60 * 1000).toISOString(); + const minusDays = (d: number) => + new Date(now.getTime() - d * 24 * 60 * 60 * 1000).toISOString(); return { connection: 'connected', @@ -28,6 +30,8 @@ export const createMockData = (): LiveData => { { parentId: 'run-200', childId: 'run-202' }, { parentId: 'run-200', childId: 'run-203' }, { parentId: 'run-300', childId: 'run-301' }, + { parentId: 'run-300', childId: 'run-302' }, + { parentId: 'run-300', childId: 'run-303' }, ], nodes: [ { @@ -319,27 +323,27 @@ export const createMockData = (): LiveData => { title: 'Directory Submissions & External References', agentId: 'ops', agentName: 'Ops', - status: 'queued', - progress: 8, + status: 'running', + progress: 36, initiativeId: 'init-3', workstreamId: null, groupId: 'init-3', groupLabel: 'Directory Submissions & External References', - startedAt: minusHours(2), - updatedAt: minusMinutes(11), - lastEventAt: minusMinutes(11), - lastEventSummary: 'Queued while waiting for vendor API credentials.', - blockers: ['Awaiting API credentials for two external data vendors'], - phase: 'blocked', - state: 'stale', - blockerReason: 'Credential handshake incomplete for enrichment vendors.', - eta: 'Pending access', - checkpointCount: 2, + startedAt: minusHours(3), + updatedAt: minusMinutes(5), + lastEventAt: minusMinutes(5), + lastEventSummary: 'Multi-lane pipeline active: import retries, QA verification, and outreach copy drafting.', + blockers: ['One vendor lane still rate-limited; recovery in progress'], + phase: 'execution', + state: 'running', + blockerReason: 'Partial dependency degradation on one vendor lane.', + eta: '42m', + checkpointCount: 8, runtimeClient: 'openclaw', runtimeLabel: 'OpenClaw Intake', runtimeProvider: 'openclaw', instanceId: 'instance-openclaw-08', - lastHeartbeatAt: minusMinutes(11), + lastHeartbeatAt: minusMinutes(2), }, { id: 'run-301', @@ -369,6 +373,62 @@ export const createMockData = (): LiveData => { instanceId: 'instance-openclaw-09', lastHeartbeatAt: minusMinutes(1), }, + { + id: 'run-302', + parentId: 'run-300', + runId: 'run-302', + title: 'Reference verification QA lane', + agentId: 'kimi', + agentName: 'Kimi', + status: 'running', + progress: 58, + initiativeId: 'init-3', + workstreamId: 'ws-directory-qa', + groupId: 'init-3', + groupLabel: 'Directory Submissions & External References', + startedAt: minusHours(2), + updatedAt: minusMinutes(5), + lastEventAt: minusMinutes(5), + lastEventSummary: 'Citation verification batch completed; QA memo and mismatch ledger attached.', + blockers: [], + phase: 'execution', + state: 'running', + eta: '18m', + checkpointCount: 5, + runtimeClient: 'codex', + runtimeLabel: 'Codex QA Worker', + runtimeProvider: 'codex', + instanceId: 'instance-codex-09', + lastHeartbeatAt: minusMinutes(1), + }, + { + id: 'run-303', + parentId: 'run-300', + runId: 'run-303', + title: 'External partner outreach copy', + agentId: 'mark', + agentName: 'Mark', + status: 'running', + progress: 41, + initiativeId: 'init-3', + workstreamId: 'ws-directory-outreach', + groupId: 'init-3', + groupLabel: 'Directory Submissions & External References', + startedAt: minusHours(2), + updatedAt: minusMinutes(8), + lastEventAt: minusMinutes(8), + lastEventSummary: 'Outreach sequence draft in progress with two channel variants.', + blockers: [], + phase: 'execution', + state: 'running', + eta: '24m', + checkpointCount: 4, + runtimeClient: 'openclaw', + runtimeLabel: 'OpenClaw Content Worker', + runtimeProvider: 'openclaw', + instanceId: 'instance-openclaw-13', + lastHeartbeatAt: minusMinutes(2), + }, { id: 'run-400', parentId: null, @@ -564,6 +624,133 @@ export const createMockData = (): LiveData => { run_id: 'slice-run-102', }, }, + { + id: 'activity-225', + type: 'artifact_created', + title: 'Artifact created: reference QA packet', + description: 'Published mismatch ledger and verification memo for the latest reference batch.', + agentId: 'kimi', + agentName: 'Kimi', + requesterAgentId: 'ops', + requesterAgentName: 'Ops', + executorAgentId: 'kimi', + executorAgentName: 'Kimi', + runId: 'run-302', + initiativeId: 'init-3', + timestamp: minusMinutes(5), + metadata: { + event: 'artifact_registered', + event_name: 'artifact_registered', + source: 'agent', + parsed_status: 'completed', + outcomes: { + artifact_count: 2, + tests: { passed: 14, failed: 0, skipped: 1 }, + task_updates: 2, + }, + task_updates: [ + { title: 'Run citation verification across 120 references', status: 'completed' }, + { title: 'Publish mismatch ledger for reviewer approval', status: 'completed' }, + ], + artifacts: [ + { + id: 'artifact-dir-qa-1', + type: 'report', + title: 'Reference verification memo', + url: null, + }, + { + id: 'artifact-dir-qa-2', + type: 'spreadsheet', + title: 'Citation mismatch ledger', + url: null, + }, + ], + }, + }, + { + id: 'activity-226', + type: 'delegation', + title: 'Dispatch: partner outreach copy lane', + description: 'Orchestrator launched a copy lane to draft partner outreach and handoff templates.', + agentId: 'orgx', + agentName: 'OrgX', + requesterAgentId: 'orgx', + requesterAgentName: 'OrgX', + executorAgentId: 'mark', + executorAgentName: 'Mark', + runId: 'run-300', + initiativeId: 'init-3', + timestamp: minusMinutes(9), + metadata: { + event: 'autopilot_slice_dispatched', + event_name: 'autopilot_slice_dispatched', + source: 'orchestrator', + parsed_status: 'running', + run_id: 'slice-run-112', + dispatch_selection_reason: + 'Reference verification can proceed in parallel with outreach preparation.', + }, + }, + { + id: 'activity-227', + type: 'run_started', + title: 'Slice started: outreach sequence draft', + description: 'Mark began drafting outreach sequence copy for external partners.', + agentId: 'mark', + agentName: 'Mark', + requesterAgentId: 'orgx', + requesterAgentName: 'OrgX', + executorAgentId: 'mark', + executorAgentName: 'Mark', + runId: 'run-303', + initiativeId: 'init-3', + timestamp: minusMinutes(8), + metadata: { + event: 'autopilot_slice_started', + event_name: 'autopilot_slice_started', + source: 'agent', + parsed_status: 'running', + run_id: 'slice-run-112', + }, + }, + { + id: 'activity-228', + type: 'artifact_created', + title: 'Artifact created: outreach copy sequence', + description: 'Drafted email + LinkedIn outreach variants and handoff checklist.', + agentId: 'mark', + agentName: 'Mark', + requesterAgentId: 'mark', + requesterAgentName: 'Mark', + executorAgentId: 'mark', + executorAgentName: 'Mark', + runId: 'run-303', + initiativeId: 'init-3', + timestamp: minusMinutes(6), + metadata: { + event: 'artifact_registered', + event_name: 'artifact_registered', + source: 'agent', + parsed_status: 'completed', + outcomes: { + artifact_count: 1, + tests: { passed: 8, failed: 0, skipped: 0 }, + }, + task_updates: [ + { title: 'Produce 2 outreach channel variants', status: 'completed' }, + { title: 'Attach partner-ready handoff checklist', status: 'completed' }, + ], + artifacts: [ + { + id: 'artifact-dir-outreach-1', + type: 'brief', + title: 'Partner outreach sequence v1', + url: null, + }, + ], + }, + }, { id: 'activity-205', type: 'decision_requested', @@ -791,6 +978,73 @@ export const createMockData = (): LiveData => { blocked_reason: 'Credentials not provisioned in secret manager.', }, }, + { + id: 'activity-230', + type: 'milestone_completed', + title: 'Milestone completed: readability foundation', + description: 'Baseline readability hierarchy shipped and adopted in activity and mission panels.', + agentId: 'dana', + agentName: 'Dana', + requesterAgentId: 'orgx', + requesterAgentName: 'OrgX', + executorAgentId: 'dana', + executorAgentName: 'Dana', + runId: 'run-201', + initiativeId: 'init-2', + timestamp: minusDays(2), + metadata: { + event: 'milestone_completed', + event_name: 'milestone_completed', + source: 'agent', + parsed_status: 'completed', + outcomes: { + artifact_count: 1, + tests: { passed: 22, failed: 0, skipped: 3 }, + }, + artifacts: [ + { + id: 'artifact-ux-activity-legacy', + type: 'design', + title: 'Readability hierarchy baseline', + url: null, + }, + ], + }, + }, + { + id: 'activity-231', + type: 'artifact_created', + title: 'Artifact created: executive notes and next-up alignment', + description: 'You captured final guidance so completed work maps cleanly to what starts next.', + agentId: 'main', + agentName: 'You', + requesterAgentId: 'main', + requesterAgentName: 'You', + executorAgentId: 'main', + executorAgentName: 'You', + runId: 'run-500', + initiativeId: 'init-4', + timestamp: minusDays(1), + metadata: { + event: 'artifact_registered', + event_name: 'artifact_registered', + source: 'user', + parsed_status: 'completed', + outcomes: { + artifact_count: 1, + task_updates: 1, + }, + task_updates: [{ title: 'Connect closeout outputs to next-up backlog', status: 'completed' }], + artifacts: [ + { + id: 'artifact-revenue-7', + type: 'brief', + title: 'Executive closeout notes', + url: null, + }, + ], + }, + }, { id: 'activity-219', type: 'run_started', @@ -1874,6 +2128,99 @@ export const createMockData = (): LiveData => { completedTasks: 1, }, }, + { + id: 'slice-run-111', + sliceRunId: 'slice-run-111', + runId: 'run-302', + initiativeId: 'init-3', + workstreamId: 'ws-directory-qa', + workstreamTitle: 'Reference verification QA lane', + taskIds: ['task-dir-qa-1', 'task-dir-qa-2'], + milestoneIds: ['mile-dir-2'], + status: 'running', + statusExplainer: 'Verification memo published; reconciling final citation mismatches.', + primaryAction: 'review_output', + hasArtifact: true, + artifactCount: 2, + artifacts: [ + { + id: 'artifact-dir-qa-1', + type: 'report', + title: 'Reference verification memo', + url: null, + createdAt: minusMinutes(5), + }, + { + id: 'artifact-dir-qa-2', + type: 'spreadsheet', + title: 'Citation mismatch ledger', + url: null, + createdAt: minusMinutes(5), + }, + ], + decisionCount: 0, + blockingDecisionCount: 0, + decisionOptions: [], + sourceClient: 'codex', + runtimeState: 'active', + startedAt: minusHours(2), + updatedAt: minusMinutes(5), + completedAt: null, + failedAt: null, + archivedAt: null, + lastEventAt: minusMinutes(5), + lastEventSummary: 'QA packet shipped; final mismatch reconciliation in progress.', + correlationId: 'run-302', + confidence: 'high', + scope: 'task', + scopeProgress: { + totalTasks: 4, + completedTasks: 2, + }, + }, + { + id: 'slice-run-112', + sliceRunId: 'slice-run-112', + runId: 'run-303', + initiativeId: 'init-3', + workstreamId: 'ws-directory-outreach', + workstreamTitle: 'External partner outreach copy', + taskIds: ['task-dir-outreach-1', 'task-dir-outreach-2'], + milestoneIds: ['mile-dir-3'], + status: 'running', + statusExplainer: 'Draft sequence underway with channel variants attached for quick review.', + primaryAction: 'review_output', + hasArtifact: true, + artifactCount: 1, + artifacts: [ + { + id: 'artifact-dir-outreach-1', + type: 'brief', + title: 'Partner outreach sequence v1', + url: null, + createdAt: minusMinutes(6), + }, + ], + decisionCount: 0, + blockingDecisionCount: 0, + decisionOptions: [], + sourceClient: 'openclaw', + runtimeState: 'active', + startedAt: minusHours(2), + updatedAt: minusMinutes(6), + completedAt: null, + failedAt: null, + archivedAt: null, + lastEventAt: minusMinutes(6), + lastEventSummary: 'Email + LinkedIn variants drafted; QA polish remaining.', + correlationId: 'run-303', + confidence: 'medium', + scope: 'task', + scopeProgress: { + totalTasks: 3, + completedTasks: 1, + }, + }, ], runtimeInstances: [ { @@ -1960,6 +2307,48 @@ export const createMockData = (): LiveData => { lastMessage: 'Vendor cooldown window reopened.', metadata: { retryAttempt: 3, vendor: 'clearbit-sandbox' }, }, + { + id: 'instance-codex-09', + sourceClient: 'codex', + displayName: 'Codex QA Worker', + providerLogo: 'codex', + state: 'active', + runId: 'run-302', + correlationId: 'run-302', + initiativeId: 'init-3', + workstreamId: 'ws-directory-qa', + taskId: 'task-dir-qa-2', + agentId: 'kimi', + agentName: 'Kimi', + phase: 'execution', + progressPct: 58, + currentTask: 'Reconciling citation mismatches after verification pass', + lastHeartbeatAt: minusMinutes(1), + lastEventAt: minusMinutes(1), + lastMessage: 'QA memo published; validating final mismatch set.', + metadata: { artifactIds: ['artifact-dir-qa-1', 'artifact-dir-qa-2'] }, + }, + { + id: 'instance-openclaw-13', + sourceClient: 'openclaw', + displayName: 'OpenClaw Content Worker', + providerLogo: 'openclaw', + state: 'active', + runId: 'run-303', + correlationId: 'run-303', + initiativeId: 'init-3', + workstreamId: 'ws-directory-outreach', + taskId: 'task-dir-outreach-2', + agentId: 'mark', + agentName: 'Mark', + phase: 'execution', + progressPct: 41, + currentTask: 'Drafting partner outreach sequence', + lastHeartbeatAt: minusMinutes(2), + lastEventAt: minusMinutes(2), + lastMessage: 'Outreach variants drafted; preparing QA polish.', + metadata: { artifactId: 'artifact-dir-outreach-1' }, + }, { id: 'instance-orgx-01', sourceClient: 'api', diff --git a/dashboard/src/hooks/useNextUpQueue.ts b/dashboard/src/hooks/useNextUpQueue.ts index d8f34ebf..494c50a5 100644 --- a/dashboard/src/hooks/useNextUpQueue.ts +++ b/dashboard/src/hooks/useNextUpQueue.ts @@ -161,48 +161,247 @@ function normalizeTransportFailure(err: unknown, fallback: string): string { function buildDemoQueueResponse(initiativeId: string | null): NextUpQueueResponse { const nowIso = new Date().toISOString(); + const minusMinutes = (minutes: number) => + new Date(Date.now() - minutes * 60_000).toISOString(); + const autoState = ( + status: 'running' | 'stopping' | 'stopped', + updatedAt: string, + input?: { + activeTaskId?: string | null; + activeRunId?: string | null; + stopReason?: 'budget_exhausted' | 'blocked' | 'completed' | 'stopped' | 'error' | null; + maxParallelSlices?: number; + parallelMode?: 'iwmt'; + } + ): NonNullable => ({ + status, + activeTaskId: input?.activeTaskId ?? null, + activeRunId: input?.activeRunId ?? null, + stopReason: input?.stopReason ?? null, + maxParallelSlices: input?.maxParallelSlices ?? 3, + parallelMode: input?.parallelMode ?? 'iwmt', + updatedAt, + }); + const items: NextUpQueueItem[] = [ { initiativeId: 'init-1', - initiativeTitle: 'Q4 Feature Ship', + initiativeTitle: 'Content Engine: Dogfood the Larry Playbook', + initiativeStatus: 'active', + initiativePriority: 'critical', + initiativePriorityNum: 11, + workstreamId: 'ws-content-1', + workstreamTitle: 'Long-form article generation', + workstreamStatus: 'in_progress', + nextTaskId: 'task-content-13', + nextTaskTitle: 'Apply editorial revisions for sections 3-4', + nextTaskPriority: 1, + nextTaskDueAt: minusMinutes(-35), + runnerAgentId: 'kimi', + runnerAgentName: 'Kimi', + runnerAgents: [{ id: 'kimi', name: 'Kimi' }], + runnerSource: 'assigned', + queueState: 'running', + blockReason: null, + queueOrigin: 'system', + sliceScope: 'task', + sliceTaskIds: ['task-content-12', 'task-content-13'], + sliceTaskCount: 2, + executionPolicy: { + domain: 'content', + requiredSkills: ['editorial-review', 'voice-consistency'], + profile: 'quality-first', + sliceScopePreference: 'task', + maxSliceTasks: 2, + maxParallelAgents: 2, + dependencyMode: 'strict', + }, + autoContinue: autoState('running', minusMinutes(4), { + activeTaskId: 'task-content-13', + activeRunId: 'run-101', + maxParallelSlices: 2, + }), + }, + { + initiativeId: 'init-1', + initiativeTitle: 'Content Engine: Dogfood the Larry Playbook', + initiativeStatus: 'active', + initiativePriority: 'critical', + initiativePriorityNum: 12, + workstreamId: 'ws-content-legal', + workstreamTitle: 'Persona QA and legal sweep', + workstreamStatus: 'blocked', + nextTaskId: 'task-legal-2', + nextTaskTitle: 'Approve legal-safe copy variant', + nextTaskPriority: 1, + nextTaskDueAt: minusMinutes(-20), + runnerAgentId: 'holt', + runnerAgentName: 'Holt', + runnerAgents: [{ id: 'holt', name: 'Holt' }], + runnerSource: 'assigned', + queueState: 'blocked', + blockReason: 'Waiting on legal-safe variant decision from you', + queueOrigin: 'system', + sliceScope: 'task', + sliceTaskIds: ['task-legal-2'], + sliceTaskCount: 1, + autoContinue: autoState('stopped', minusMinutes(7), { + stopReason: 'blocked', + maxParallelSlices: 2, + }), + }, + { + initiativeId: 'init-2', + initiativeTitle: "Live View UX Redesign — The Conductor's Display", initiativeStatus: 'active', initiativePriority: 'high', - initiativePriorityNum: 25, - workstreamId: 'ws-4', - workstreamTitle: 'Dashboard UI pass', - workstreamStatus: 'active', - nextTaskId: 'task-1', - nextTaskTitle: 'Polish timeline controls', + initiativePriorityNum: 24, + workstreamId: 'ws-ux-devmode', + workstreamTitle: 'Developer-mode feature gating', + workstreamStatus: 'in_progress', + nextTaskId: 'task-ux-33', + nextTaskTitle: 'Capture final mobile + desktop verification', nextTaskPriority: 1, - nextTaskDueAt: null, + nextTaskDueAt: minusMinutes(-15), + runnerAgentId: 'holt', + runnerAgentName: 'Holt', + runnerAgents: [{ id: 'holt', name: 'Holt' }], + runnerSource: 'assigned', + queueState: 'running', + blockReason: null, + queueOrigin: 'system', + sliceScope: 'workstream', + sliceTaskIds: ['task-ux-31', 'task-ux-32', 'task-ux-33'], + sliceTaskCount: 3, + autoContinue: autoState('running', minusMinutes(2), { + activeTaskId: 'task-ux-33', + activeRunId: 'run-202', + }), + }, + { + initiativeId: 'init-2', + initiativeTitle: "Live View UX Redesign — The Conductor's Display", + initiativeStatus: 'active', + initiativePriority: 'high', + initiativePriorityNum: 26, + workstreamId: 'ws-ux-activity', + workstreamTitle: 'Activity feed readability pass', + workstreamStatus: 'in_progress', + nextTaskId: 'task-ux-19', + nextTaskTitle: 'Finalize hierarchy labels and evidence chips', + nextTaskPriority: 2, + nextTaskDueAt: minusMinutes(-28), runnerAgentId: 'dana', runnerAgentName: 'Dana', runnerAgents: [{ id: 'dana', name: 'Dana' }], runnerSource: 'assigned', - queueState: 'running', + queueState: 'queued', blockReason: null, queueOrigin: 'system', - autoContinue: { - status: 'running', - activeTaskId: 'task-1', - activeRunId: 'run-2', - stopReason: null, - updatedAt: nowIso, - }, + sliceScope: 'task', + sliceTaskIds: ['task-ux-18', 'task-ux-19'], + sliceTaskCount: 2, + autoContinue: autoState('running', minusMinutes(6), { + activeTaskId: 'task-ux-19', + activeRunId: 'run-201', + }), }, { initiativeId: 'init-2', - initiativeTitle: 'Black Friday Email', + initiativeTitle: "Live View UX Redesign — The Conductor's Display", + initiativeStatus: 'active', + initiativePriority: 'high', + initiativePriorityNum: 27, + workstreamId: 'ws-ux-collapse', + workstreamTitle: 'Grouped section collapse spacing regression', + workstreamStatus: 'blocked', + nextTaskId: 'task-ux-41', + nextTaskTitle: 'Apply approved collapsed spacing token', + nextTaskPriority: 1, + nextTaskDueAt: minusMinutes(-12), + runnerAgentId: 'kimi', + runnerAgentName: 'Kimi', + runnerAgents: [{ id: 'kimi', name: 'Kimi' }], + runnerSource: 'assigned', + queueState: 'blocked', + blockReason: 'Decision required: choose 8px or 0px collapsed spacing', + queueOrigin: 'system', + sliceScope: 'task', + sliceTaskIds: ['task-ux-41'], + sliceTaskCount: 1, + autoContinue: autoState('stopped', minusMinutes(2), { + stopReason: 'blocked', + }), + }, + { + initiativeId: 'init-3', + initiativeTitle: 'Directory Submissions & External References', initiativeStatus: 'active', initiativePriority: 'medium', - initiativePriorityNum: 50, - workstreamId: 'ws-9', - workstreamTitle: 'Email campaign generation', - workstreamStatus: 'queued', - nextTaskId: 'task-2', - nextTaskTitle: 'Draft launch variants', + initiativePriorityNum: 41, + workstreamId: 'ws-directory-import', + workstreamTitle: 'Enrichment import pipeline', + workstreamStatus: 'in_progress', + nextTaskId: 'task-dir-12', + nextTaskTitle: 'Finish vendor retry batch after cooldown', nextTaskPriority: 2, - nextTaskDueAt: null, + nextTaskDueAt: minusMinutes(-45), + runnerAgentId: 'pace', + runnerAgentName: 'Pace', + runnerAgents: [{ id: 'pace', name: 'Pace' }], + runnerSource: 'assigned', + queueState: 'running', + blockReason: null, + queueOrigin: 'system', + sliceScope: 'task', + sliceTaskIds: ['task-dir-11', 'task-dir-12'], + sliceTaskCount: 2, + autoContinue: autoState('running', minusMinutes(3), { + activeTaskId: 'task-dir-12', + activeRunId: 'run-301', + }), + }, + { + initiativeId: 'init-3', + initiativeTitle: 'Directory Submissions & External References', + initiativeStatus: 'active', + initiativePriority: 'medium', + initiativePriorityNum: 43, + workstreamId: 'ws-directory-qa', + workstreamTitle: 'Reference verification QA lane', + workstreamStatus: 'in_progress', + nextTaskId: 'task-dir-qa-2', + nextTaskTitle: 'Reconcile citation mismatches and publish QA memo', + nextTaskPriority: 2, + nextTaskDueAt: minusMinutes(-38), + runnerAgentId: 'kimi', + runnerAgentName: 'Kimi', + runnerAgents: [{ id: 'kimi', name: 'Kimi' }], + runnerSource: 'assigned', + queueState: 'queued', + blockReason: null, + queueOrigin: 'system', + sliceScope: 'task', + sliceTaskIds: ['task-dir-qa-1', 'task-dir-qa-2'], + sliceTaskCount: 2, + autoContinue: autoState('running', minusMinutes(5), { + activeTaskId: 'task-dir-qa-2', + activeRunId: 'run-302', + }), + }, + { + initiativeId: 'init-3', + initiativeTitle: 'Directory Submissions & External References', + initiativeStatus: 'active', + initiativePriority: 'medium', + initiativePriorityNum: 45, + workstreamId: 'ws-directory-outreach', + workstreamTitle: 'External partner outreach copy', + workstreamStatus: 'in_progress', + nextTaskId: 'task-dir-outreach-2', + nextTaskTitle: 'Finalize outreach sequence copy', + nextTaskPriority: 3, + nextTaskDueAt: minusMinutes(-52), runnerAgentId: 'mark', runnerAgentName: 'Mark', runnerAgents: [{ id: 'mark', name: 'Mark' }], @@ -210,13 +409,71 @@ function buildDemoQueueResponse(initiativeId: string | null): NextUpQueueRespons queueState: 'queued', blockReason: null, queueOrigin: 'system', - autoContinue: { - status: 'stopped', - activeTaskId: null, - activeRunId: null, - stopReason: null, - updatedAt: nowIso, - }, + sliceScope: 'task', + sliceTaskIds: ['task-dir-outreach-1', 'task-dir-outreach-2'], + sliceTaskCount: 2, + autoContinue: autoState('running', minusMinutes(8), { + activeTaskId: 'task-dir-outreach-2', + activeRunId: 'run-303', + }), + }, + { + initiativeId: 'init-5', + initiativeTitle: 'Incident Replay & Reliability', + initiativeStatus: 'blocked', + initiativePriority: 'critical', + initiativePriorityNum: 8, + workstreamId: 'ws-reliability-replay', + workstreamTitle: 'Incident replay reconstruction', + workstreamStatus: 'blocked', + nextTaskId: 'task-rel-44', + nextTaskTitle: 'Approve snapshot restore and rerun replay', + nextTaskPriority: 1, + nextTaskDueAt: minusMinutes(-10), + runnerAgentId: 'ops', + runnerAgentName: 'Ops', + runnerAgents: [{ id: 'ops', name: 'Ops' }], + runnerSource: 'assigned', + queueState: 'blocked', + blockReason: 'Archive restore approval needed to continue replay', + queueOrigin: 'system', + sliceScope: 'milestone', + sliceTaskIds: ['task-rel-44'], + sliceTaskCount: 1, + sliceMilestoneId: 'mile-rel-9', + autoContinue: autoState('stopped', minusMinutes(25), { + stopReason: 'blocked', + maxParallelSlices: 1, + }), + }, + { + initiativeId: 'init-4', + initiativeTitle: 'Revenue Expansion Q2', + initiativeStatus: 'completed', + initiativePriority: 'low', + initiativePriorityNum: 74, + workstreamId: 'ws-revenue-closeout', + workstreamTitle: 'Revenue Expansion Q2 closeout', + workstreamStatus: 'completed', + nextTaskId: null, + nextTaskTitle: 'Completed', + nextTaskPriority: null, + nextTaskDueAt: null, + runnerAgentId: 'mark', + runnerAgentName: 'Mark', + runnerAgents: [{ id: 'mark', name: 'Mark' }], + runnerSource: 'assigned', + queueState: 'completed', + blockReason: null, + queueOrigin: 'system', + sliceScope: 'milestone', + sliceTaskIds: ['task-revenue-19'], + sliceTaskCount: 1, + sliceMilestoneId: 'mile-revenue-4', + autoContinue: autoState('stopped', minusMinutes(40), { + stopReason: 'completed', + maxParallelSlices: 1, + }), }, ]; diff --git a/package.json b/package.json index 7f189477..fa28be3f 100644 --- a/package.json +++ b/package.json @@ -54,6 +54,7 @@ "verify:conduit-mcp": "node ./scripts/verify-conduit-mcp.mjs", "verify:repo-hygiene": "node ./scripts/verify-repo-hygiene.mjs", "dev:main": "node ./scripts/dev-main-sync.mjs", + "dev:live": "node ./scripts/live-dev-serve.mjs", "e2e:auto-continue": "node ./scripts/e2e-auto-continue.mjs", "e2e:agent-suite": "npm run build:core && node ./scripts/e2e-agent-suite-kickoff-3x.mjs", "demo:record": "node ./scripts/record-demo.mjs", diff --git a/src/http/routes/mission-control-read.ts b/src/http/routes/mission-control-read.ts index 59e5eee5..475bf6fb 100644 --- a/src/http/routes/mission-control-read.ts +++ b/src/http/routes/mission-control-read.ts @@ -1405,11 +1405,14 @@ export function registerMissionControlReadRoutes( throw new Error("canonical next-up all-workspaces scope mismatch"); } - const canonicalItems = applyQueueNoiseControls( - normalizeQueueItems(canonicalRecord.items).filter((item) => - includeCompleted ? true : item.queueState !== "completed" + const canonicalItems = await enrichWithMilestoneBreakdown( + applyQueueNoiseControls( + normalizeQueueItems(canonicalRecord.items).filter((item) => + includeCompleted ? true : item.queueState !== "completed" + ), + { noiseThreshold, dedupWindowMs } ), - { noiseThreshold, dedupWindowMs } + deps ); const canonicalTotal = Math.max(