Repository navigation
feat(OMN-12072): Swarm Control Plane widget — decomposition tree, wave view, live counters, savings bar - #125
Conversation
|
Caution Review failedThe pull request is closed. ℹ️ Recent review info⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (17)
📝 WalkthroughWalkthroughThis PR adds backend and frontend integration for monitoring swarm run executions. It includes PostgreSQL projection reader support, a REST API endpoint, typed service adapters, React data hooks, and a comprehensive tabbed dashboard UI with run tables, live metrics aggregation, subtask synthesis, visual decompositions, wave-based execution views, and cost/savings analysis. ChangesSwarm Control Plane
Sequence DiagramsequenceDiagram
participant Client
participant Backend as Backend /api/swarm-runs
participant Pg as Postgres
participant ReactQuery as React Query
participant useSwarmSnapshot as useSwarmSnapshot Hook
participant SwarmControlPlane as SwarmControlPlane Component
Client->>ReactQuery: Initial page load
ReactQuery->>Backend: GET /api/swarm-runs
Backend->>Pg: SELECT ... FROM swarm_runs ORDER BY created_at DESC LIMIT 200
Pg-->>Backend: rows[]
Backend-->>ReactQuery: { rows: [...] }
ReactQuery-->>useSwarmSnapshot: data update
useSwarmSnapshot->>useSwarmSnapshot: normalize runs, compute capturedAt
useSwarmSnapshot-->>SwarmControlPlane: SwarmSnapshot { runs, isLoading, capturedAt }
SwarmControlPlane->>SwarmControlPlane: select active run, render table & tabs
SwarmControlPlane-->>Client: Render SwarmRunTable + EvidenceTabs
Client->>SwarmControlPlane: Click run row
SwarmControlPlane->>SwarmControlPlane: selectedRunId = clicked run
SwarmControlPlane-->>Client: Re-render active tab with selected run
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes Possibly related PRs
Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Comment |
There was a problem hiding this comment.
Actionable comments posted: 7
🧹 Nitpick comments (1)
src/components/dashboard/swarm-control-plane/SwarmControlPlane.test.tsx (1)
87-155: ⚡ Quick winAdd explicit coverage for the
Subtask Detailtab path.The suite validates
overview,decomposition,run detail,wave, andsavings, but it doesn’t exercise thesubtask-rows(Subtask Detail) branch currently wired inSwarmControlPlane.tsx. A small test here would catch tab-routing regressions early.Proposed test addition
@@ it('shows wave visualization in wave tab', () => { @@ }); + + it('shows subtask detail panel in subtask detail tab', () => { + mockUseSwarmSnapshot.mockReturnValue({ + ...EMPTY_SNAPSHOT, + runs: [makeRun()], + hasAnyData: true, + }); + render(<SwarmControlPlane config={{}} />); + fireEvent.click(screen.getByRole('tab', { name: /Subtask Detail/i })); + expect(screen.getByText(/derived from aggregates/i)).toBeTruthy(); + });🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/components/dashboard/swarm-control-plane/SwarmControlPlane.test.tsx` around lines 87 - 155, Add a new test in SwarmControlPlane.test.tsx that exercises the "Subtask Detail" tab path: mockUseSwarmSnapshot to return a snapshot with runs (use makeRun()) that include at least one subtask entry, render(<SwarmControlPlane config={{}} />), then fireEvent.click the tab labeled "Subtask Detail" (or the element that triggers the subtask-rows branch), and assert that elements expected from the subtask-rows branch (e.g., subtask IDs, statuses, or specific text present in the UI) are rendered; target the SwarmControlPlane component and the subtask-rows branch so the test fails if tab routing or rendering for Subtask Detail regresses.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@src/components/dashboard/swarm-control-plane/format.ts`:
- Around line 7-11: fmtUsd currently treats negative numbers as if they were
tiny positives (e.g., -12.34 hits the <0.001 branch); fix by extracting the sign
and using the absolute value for threshold checks and toFixed formatting inside
fmtUsd: compute sign = usd < 0 ? '-' : '' and abs = Math.abs(usd), keep the zero
special-case (compare abs === 0), then apply thresholds against abs and return
the formatted string with sign placed before the dollar sign (e.g.,
`${sign}$${abs.toFixed(...)}`).
In `@src/components/dashboard/swarm-control-plane/SwarmDecompositionTree.tsx`:
- Around line 119-128: The per-subtask status derivation in
SwarmDecompositionTree.tsx (the loop building perSubtaskStatus) currently
ignores run.skipped_count; update the logic in that loop to account for skipped
subtasks by allocating indices after succeeded_count and failed_count for
skipped_count, and only mark remaining ones as 'pending' (or 'skipped'/'pending'
based on isComplete) accordingly; use the existing symbols run.subtask_count,
run.succeeded_count, run.failed_count, run.skipped_count and isComplete to
compute thresholds (succeeded -> failed -> skipped -> pending) so skipped
subtasks are assigned 'skipped' rather than misclassified.
In `@src/components/dashboard/swarm-control-plane/SwarmLiveCounters.tsx`:
- Line 52: The succeededRuns calculation in SwarmLiveCounters uses a
case-sensitive check (const succeededRuns = runs.filter((r) => r.status ===
'succeeded').length) and may miss variants like 'Succeeded' or 'SUCCEEDED';
update the filter to normalize r.status (e.g.,
r.status?.toString().toLowerCase()) before comparison and guard for undefined
statuses so succeededRuns correctly counts all case variants of 'succeeded'.
In `@src/components/dashboard/swarm-control-plane/SwarmRunTable.tsx`:
- Around line 64-71: The table row in SwarmRunTable is only mouse-clickable;
update the <tr> that uses run.run_id and onSelectRun to be keyboard-accessible
by adding tabIndex={0}, a keyDown handler that calls onSelectRun(run.run_id)
when Enter or Space is pressed (preventDefault on Space to avoid scrolling), and
appropriate ARIA semantics such as role="row" (or role="button" if more
appropriate) and aria-selected={isSelected} so screen readers and keyboard users
can activate/select rows the same way as mouse users.
In `@src/components/dashboard/swarm-control-plane/SwarmSavingsBar.tsx`:
- Around line 25-37: The code treats runs[].savings_usd as always non-negative,
which makes the "saved" label and percentages misleading when some runs have
negative savings_usd; update the UI logic in SwarmSavingsBar (references: runs,
totalSavings, totalBaseline, localPct, savingsPct, fmtPct, fmtUsd) to compute
two values: totalPositiveSavings = sum of max(0, r.savings_usd) and
totalNegativeSavings = sum of min(0, r.savings_usd), derive savingsPct from
totalPositiveSavings (and/or show negative percent separately), and change the
displayed label to conditionally render "saved (…)" when totalPositiveSavings >
0 or "over baseline (…)" when totalNegativeSavings < 0, ensuring fmtPct and
fmtUsd are passed the appropriate non-negative or negative totals so percentages
and text match the sign.
In `@src/components/dashboard/swarm-control-plane/SwarmSubtaskDetailPanel.tsx`:
- Around line 24-65: The deriveSubtaskRows function in SwarmSubtaskDetailPanel
synthesizes authoritative per-subtask state (status, costUsd, latencyMs, model)
from aggregate fields on SwarmRunRow; move this logic out of the widget so the
component only renders API/projection data. Replace deriveSubtaskRows with a
thin adapter that expects an explicit array of SwarmSubtaskRow-like objects
provided by upstream (or add a new run.subtasks: SwarmSubtaskRow[] projection on
the API/selector), remove in-component inference of perSubtaskCost,
estimatedSubtaskLatency, status derivation, and model selection, and update all
callers to supply the authoritative subtask array (or call the new projection
helper) instead of relying on deriveSubtaskRows.
In `@src/services/swarm-api.ts`:
- Around line 52-63: The fetchJson/fetchSwarmRuns implementation currently calls
fetch() directly and bypasses the project's data-source abstraction; replace the
direct HTTP call with the approved data-source/projection-query path by
importing and using the snapshot/data-source client (the abstraction selected by
VITE_DATA_SOURCE) instead of fetch and resolveBase. Specifically, update
fetchSwarmRuns (and fetchJson helper) to call the appropriate data-source API or
projection query method (the module exported by src/data-source/* that returns
SwarmRunsResponse) via SwarmApiOptions/resolveBase-aware plumbing, ensure no
hardcoded localhost:3002 remains, and preserve the
SwarmRunsResponse/SwarmApiOptions types when mapping the returned value.
---
Nitpick comments:
In `@src/components/dashboard/swarm-control-plane/SwarmControlPlane.test.tsx`:
- Around line 87-155: Add a new test in SwarmControlPlane.test.tsx that
exercises the "Subtask Detail" tab path: mockUseSwarmSnapshot to return a
snapshot with runs (use makeRun()) that include at least one subtask entry,
render(<SwarmControlPlane config={{}} />), then fireEvent.click the tab labeled
"Subtask Detail" (or the element that triggers the subtask-rows branch), and
assert that elements expected from the subtask-rows branch (e.g., subtask IDs,
statuses, or specific text present in the UI) are rendered; target the
SwarmControlPlane component and the subtask-rows branch so the test fails if tab
routing or rendering for Subtask Detail regresses.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 212972b4-ebb9-42ae-8938-811101b98ffd
📒 Files selected for processing (17)
server/postgres-projection-reader.tsserver/routes.tsshared/types/topics.tssrc/components/dashboard/swarm-control-plane/SwarmControlPlane.test.tsxsrc/components/dashboard/swarm-control-plane/SwarmControlPlane.tsxsrc/components/dashboard/swarm-control-plane/SwarmDecompositionTree.tsxsrc/components/dashboard/swarm-control-plane/SwarmLiveCounters.tsxsrc/components/dashboard/swarm-control-plane/SwarmRunTable.tsxsrc/components/dashboard/swarm-control-plane/SwarmSavingsBar.tsxsrc/components/dashboard/swarm-control-plane/SwarmSubtaskCard.tsxsrc/components/dashboard/swarm-control-plane/SwarmSubtaskDetailPanel.test.tsxsrc/components/dashboard/swarm-control-plane/SwarmSubtaskDetailPanel.tsxsrc/components/dashboard/swarm-control-plane/SwarmWaveVisualization.tsxsrc/components/dashboard/swarm-control-plane/format.tssrc/components/dashboard/swarm-control-plane/swarm-control-plane.types.tssrc/components/dashboard/swarm-control-plane/useSwarmSnapshot.tssrc/services/swarm-api.ts
| export function fmtUsd(usd: number): string { | ||
| if (usd === 0) return '$0.00'; | ||
| if (usd < 0.001) return `$${usd.toFixed(5)}`; | ||
| if (usd < 0.01) return `$${usd.toFixed(4)}`; | ||
| return `$${usd.toFixed(2)}`; |
There was a problem hiding this comment.
Handle negative USD values before tiny-value precision branches.
Negative amounts currently hit the < 0.001 branch, which over-formats values like -12.34 as five decimals. Use absolute value for thresholds and apply sign separately.
💡 Suggested fix
export function fmtUsd(usd: number): string {
- if (usd === 0) return '$0.00';
- if (usd < 0.001) return `$${usd.toFixed(5)}`;
- if (usd < 0.01) return `$${usd.toFixed(4)}`;
- return `$${usd.toFixed(2)}`;
+ if (usd === 0) return '$0.00';
+ const sign = usd < 0 ? '-' : '';
+ const abs = Math.abs(usd);
+ if (abs < 0.001) return `-${'$'}${abs.toFixed(5)}`.replace('-$', `${sign}$`);
+ if (abs < 0.01) return `-${'$'}${abs.toFixed(4)}`.replace('-$', `${sign}$`);
+ return `${sign}$${abs.toFixed(2)}`;
}📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| export function fmtUsd(usd: number): string { | |
| if (usd === 0) return '$0.00'; | |
| if (usd < 0.001) return `$${usd.toFixed(5)}`; | |
| if (usd < 0.01) return `$${usd.toFixed(4)}`; | |
| return `$${usd.toFixed(2)}`; | |
| export function fmtUsd(usd: number): string { | |
| if (usd === 0) return '$0.00'; | |
| const sign = usd < 0 ? '-' : ''; | |
| const abs = Math.abs(usd); | |
| if (abs < 0.001) return `-${'$'}${abs.toFixed(5)}`.replace('-$', `${sign}$`); | |
| if (abs < 0.01) return `-${'$'}${abs.toFixed(4)}`.replace('-$', `${sign}$`); | |
| return `${sign}$${abs.toFixed(2)}`; |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@src/components/dashboard/swarm-control-plane/format.ts` around lines 7 - 11,
fmtUsd currently treats negative numbers as if they were tiny positives (e.g.,
-12.34 hits the <0.001 branch); fix by extracting the sign and using the
absolute value for threshold checks and toFixed formatting inside fmtUsd:
compute sign = usd < 0 ? '-' : '' and abs = Math.abs(usd), keep the zero
special-case (compare abs === 0), then apply thresholds against abs and return
the formatted string with sign placed before the dollar sign (e.g.,
`${sign}$${abs.toFixed(...)}`).
| // Derive per-subtask statuses from aggregate counts | ||
| const perSubtaskStatus: string[] = []; | ||
| for (let i = 0; i < run.subtask_count; i++) { | ||
| if (i < run.succeeded_count) { | ||
| perSubtaskStatus.push('succeeded'); | ||
| } else if (i < run.succeeded_count + run.failed_count) { | ||
| perSubtaskStatus.push('failed'); | ||
| } else { | ||
| perSubtaskStatus.push(isComplete ? 'skipped' : 'pending'); | ||
| } |
There was a problem hiding this comment.
Respect skipped_count when deriving per-subtask statuses.
The derivation ignores run.skipped_count, so skipped subtasks can be rendered as pending/skipped incorrectly depending on completion state.
Proposed fix
for (let i = 0; i < run.subtask_count; i++) {
if (i < run.succeeded_count) {
perSubtaskStatus.push('succeeded');
} else if (i < run.succeeded_count + run.failed_count) {
perSubtaskStatus.push('failed');
+ } else if (i < run.succeeded_count + run.failed_count + run.skipped_count) {
+ perSubtaskStatus.push('skipped');
} else {
perSubtaskStatus.push(isComplete ? 'skipped' : 'pending');
}
}📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| // Derive per-subtask statuses from aggregate counts | |
| const perSubtaskStatus: string[] = []; | |
| for (let i = 0; i < run.subtask_count; i++) { | |
| if (i < run.succeeded_count) { | |
| perSubtaskStatus.push('succeeded'); | |
| } else if (i < run.succeeded_count + run.failed_count) { | |
| perSubtaskStatus.push('failed'); | |
| } else { | |
| perSubtaskStatus.push(isComplete ? 'skipped' : 'pending'); | |
| } | |
| // Derive per-subtask statuses from aggregate counts | |
| const perSubtaskStatus: string[] = []; | |
| for (let i = 0; i < run.subtask_count; i++) { | |
| if (i < run.succeeded_count) { | |
| perSubtaskStatus.push('succeeded'); | |
| } else if (i < run.succeeded_count + run.failed_count) { | |
| perSubtaskStatus.push('failed'); | |
| } else if (i < run.succeeded_count + run.failed_count + run.skipped_count) { | |
| perSubtaskStatus.push('skipped'); | |
| } else { | |
| perSubtaskStatus.push(isComplete ? 'skipped' : 'pending'); | |
| } |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@src/components/dashboard/swarm-control-plane/SwarmDecompositionTree.tsx`
around lines 119 - 128, The per-subtask status derivation in
SwarmDecompositionTree.tsx (the loop building perSubtaskStatus) currently
ignores run.skipped_count; update the logic in that loop to account for skipped
subtasks by allocating indices after succeeded_count and failed_count for
skipped_count, and only mark remaining ones as 'pending' (or 'skipped'/'pending'
based on isComplete) accordingly; use the existing symbols run.subtask_count,
run.succeeded_count, run.failed_count, run.skipped_count and isComplete to
compute thresholds (succeeded -> failed -> skipped -> pending) so skipped
subtasks are assigned 'skipped' rather than misclassified.
| ? runs.reduce((s, r) => s + r.parallelism_speedup_ratio, 0) / runs.length | ||
| : 1; | ||
|
|
||
| const succeededRuns = runs.filter((r) => r.status === 'succeeded').length; |
There was a problem hiding this comment.
Normalize status before counting succeeded runs.
Line 52 uses a case-sensitive raw string check, which can undercount succeeded runs if status casing varies.
🔧 Suggested fix
+import { normalizeStatus } from './swarm-control-plane.types';
...
- const succeededRuns = runs.filter((r) => r.status === 'succeeded').length;
+ const succeededRuns = runs.filter((r) => normalizeStatus(r.status) === 'succeeded').length;📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| const succeededRuns = runs.filter((r) => r.status === 'succeeded').length; | |
| import { normalizeStatus } from './swarm-control-plane.types'; | |
| const succeededRuns = runs.filter((r) => normalizeStatus(r.status) === 'succeeded').length; |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@src/components/dashboard/swarm-control-plane/SwarmLiveCounters.tsx` at line
52, The succeededRuns calculation in SwarmLiveCounters uses a case-sensitive
check (const succeededRuns = runs.filter((r) => r.status ===
'succeeded').length) and may miss variants like 'Succeeded' or 'SUCCEEDED';
update the filter to normalize r.status (e.g.,
r.status?.toString().toLowerCase()) before comparison and guard for undefined
statuses so succeededRuns correctly counts all case variants of 'succeeded'.
| <tr | ||
| key={run.run_id} | ||
| onClick={() => onSelectRun(run.run_id)} | ||
| style={{ | ||
| cursor: 'pointer', | ||
| background: isSelected ? 'var(--panel-2)' : 'transparent', | ||
| borderBottom: '1px solid var(--line-2)', | ||
| }} |
There was a problem hiding this comment.
Add keyboard-accessible row selection for interactive table rows.
Line 66 makes <tr> mouse-clickable only; keyboard users cannot select a run. Add tabIndex, keyboard activation (Enter/Space), and selection semantics.
♿ Suggested fix
<tr
key={run.run_id}
onClick={() => onSelectRun(run.run_id)}
+ onKeyDown={(e) => {
+ if (e.key === 'Enter' || e.key === ' ') {
+ e.preventDefault();
+ onSelectRun(run.run_id);
+ }
+ }}
+ tabIndex={0}
+ role="button"
+ aria-pressed={isSelected}
style={{
cursor: 'pointer',
background: isSelected ? 'var(--panel-2)' : 'transparent',
borderBottom: '1px solid var(--line-2)',
}}
>📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| <tr | |
| key={run.run_id} | |
| onClick={() => onSelectRun(run.run_id)} | |
| style={{ | |
| cursor: 'pointer', | |
| background: isSelected ? 'var(--panel-2)' : 'transparent', | |
| borderBottom: '1px solid var(--line-2)', | |
| }} | |
| <tr | |
| key={run.run_id} | |
| onClick={() => onSelectRun(run.run_id)} | |
| onKeyDown={(e) => { | |
| if (e.key === 'Enter' || e.key === ' ') { | |
| e.preventDefault(); | |
| onSelectRun(run.run_id); | |
| } | |
| }} | |
| tabIndex={0} | |
| role="button" | |
| aria-pressed={isSelected} | |
| style={{ | |
| cursor: 'pointer', | |
| background: isSelected ? 'var(--panel-2)' : 'transparent', | |
| borderBottom: '1px solid var(--line-2)', | |
| }} |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@src/components/dashboard/swarm-control-plane/SwarmRunTable.tsx` around lines
64 - 71, The table row in SwarmRunTable is only mouse-clickable; update the <tr>
that uses run.run_id and onSelectRun to be keyboard-accessible by adding
tabIndex={0}, a keyDown handler that calls onSelectRun(run.run_id) when Enter or
Space is pressed (preventDefault on Space to avoid scrolling), and appropriate
ARIA semantics such as role="row" (or role="button" if more appropriate) and
aria-selected={isSelected} so screen readers and keyboard users can
activate/select rows the same way as mouse users.
| const totalSavings = runs.reduce((s, r) => s + r.savings_usd, 0); | ||
| const totalBaseline = Math.max(totalLocal + totalSavings, totalCloud); | ||
|
|
||
| const localPct = totalBaseline > 0 ? (totalLocal / totalBaseline) * 100 : 0; | ||
| const savingsPct = totalBaseline > 0 ? (totalSavings / totalBaseline) * 100 : 0; | ||
|
|
||
| return ( | ||
| <div style={{ display: 'flex', flexDirection: 'column', gap: 10 }}> | ||
| <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'baseline' }}> | ||
| <Text as="span" size="sm" weight="semibold" color="primary">Actual vs Opus-equivalent cost</Text> | ||
| <Text as="span" size="xs" family="mono" color="ok"> | ||
| {fmtPct(totalSavings, totalBaseline)} saved ({fmtUsd(totalSavings)}) | ||
| </Text> |
There was a problem hiding this comment.
Handle negative savings_usd explicitly (over-baseline runs).
Current logic assumes savings is non-negative, so negative values can render misleading “saved” messaging and percentages.
Proposed fix
- const totalSavings = runs.reduce((s, r) => s + r.savings_usd, 0);
+ const totalSavings = runs.reduce((s, r) => s + r.savings_usd, 0);
+ const hasNetSavings = totalSavings >= 0;
+ const savingsAbs = Math.max(totalSavings, 0);
+ const overrunAbs = Math.max(-totalSavings, 0);
const totalBaseline = Math.max(totalLocal + totalSavings, totalCloud);
const localPct = totalBaseline > 0 ? (totalLocal / totalBaseline) * 100 : 0;
- const savingsPct = totalBaseline > 0 ? (totalSavings / totalBaseline) * 100 : 0;
+ const savingsPct = totalBaseline > 0 ? (savingsAbs / totalBaseline) * 100 : 0;
@@
- <Text as="span" size="xs" family="mono" color="ok">
- {fmtPct(totalSavings, totalBaseline)} saved ({fmtUsd(totalSavings)})
+ <Text as="span" size="xs" family="mono" color={hasNetSavings ? 'ok' : 'bad'}>
+ {hasNetSavings
+ ? `${fmtPct(savingsAbs, totalBaseline)} saved (${fmtUsd(savingsAbs)})`
+ : `${fmtPct(overrunAbs, totalBaseline)} over baseline (${fmtUsd(overrunAbs)})`}
</Text>
@@
- {savingsPct > 0 && (
+ {hasNetSavings && savingsPct > 0 && (
@@
- title={`Savings: ${fmtUsd(totalSavings)}`}
+ title={`Savings: ${fmtUsd(savingsAbs)}`}
@@
- <Text as="span" size="xs" family="mono" color={run.savings_usd > 0 ? 'ok' : 'tertiary'} style={{ flex: '0 0 60px', textAlign: 'right' }}>
- {run.savings_usd > 0 ? `−${fmtUsd(run.savings_usd)}` : run.total_cost_usd > 0 ? fmtUsd(run.total_cost_usd) : '-'}
+ <Text
+ as="span"
+ size="xs"
+ family="mono"
+ color={run.savings_usd > 0 ? 'ok' : run.savings_usd < 0 ? 'bad' : 'tertiary'}
+ style={{ flex: '0 0 60px', textAlign: 'right' }}
+ >
+ {run.savings_usd > 0
+ ? `−${fmtUsd(run.savings_usd)}`
+ : run.savings_usd < 0
+ ? `+${fmtUsd(Math.abs(run.savings_usd))}`
+ : run.total_cost_usd > 0
+ ? fmtUsd(run.total_cost_usd)
+ : '-'}
</Text>Also applies to: 67-83, 95-116
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@src/components/dashboard/swarm-control-plane/SwarmSavingsBar.tsx` around
lines 25 - 37, The code treats runs[].savings_usd as always non-negative, which
makes the "saved" label and percentages misleading when some runs have negative
savings_usd; update the UI logic in SwarmSavingsBar (references: runs,
totalSavings, totalBaseline, localPct, savingsPct, fmtPct, fmtUsd) to compute
two values: totalPositiveSavings = sum of max(0, r.savings_usd) and
totalNegativeSavings = sum of min(0, r.savings_usd), derive savingsPct from
totalPositiveSavings (and/or show negative percent separately), and change the
displayed label to conditionally render "saved (…)" when totalPositiveSavings >
0 or "over baseline (…)" when totalNegativeSavings < 0, ensuring fmtPct and
fmtUsd are passed the appropriate non-negative or negative totals so percentages
and text match the sign.
| export function deriveSubtaskRows(run: SwarmRunRow): SwarmSubtaskRow[] { | ||
| if (run.subtask_count === 0) return []; | ||
|
|
||
| const rows: SwarmSubtaskRow[] = []; | ||
| const runStatus = normalizeStatus(run.status); | ||
| const isComplete = runStatus === 'succeeded' || runStatus === 'failed'; | ||
|
|
||
| // Distribute aggregate costs/latency evenly across subtasks as estimates | ||
| const perSubtaskCost = run.total_cost_usd > 0 | ||
| ? run.total_cost_usd / run.subtask_count | ||
| : null; | ||
| // Estimate individual latency: dispatch time split evenly, plus aggregation overhead | ||
| const estimatedSubtaskLatency = run.dispatch_wall_latency_ms > 0 | ||
| ? Math.round(run.dispatch_wall_latency_ms / Math.max(1, run.subtask_count / (run.parallelism_speedup_ratio || 1))) | ||
| : null; | ||
|
|
||
| for (let i = 0; i < run.subtask_count; i++) { | ||
| let status: SwarmSubtaskRow['status']; | ||
| if (i < run.succeeded_count) { | ||
| status = 'succeeded'; | ||
| } else if (i < run.succeeded_count + run.failed_count) { | ||
| status = 'failed'; | ||
| } else if (i < run.succeeded_count + run.failed_count + run.skipped_count) { | ||
| status = 'skipped'; | ||
| } else { | ||
| status = isComplete ? 'skipped' : 'pending'; | ||
| } | ||
|
|
||
| const model = run.models_used.length > 0 | ||
| ? run.models_used[i % run.models_used.length] | ||
| : 'unknown'; | ||
|
|
||
| rows.push({ | ||
| subtaskId: `${run.run_id.slice(0, 8)}-st${String(i + 1).padStart(2, '0')}`, | ||
| description: `Subtask ${String(i + 1).padStart(2, '0')}`, | ||
| model, | ||
| status, | ||
| tokensInput: null, | ||
| tokensOutput: null, | ||
| costUsd: perSubtaskCost, | ||
| latencyMs: status === 'succeeded' || status === 'failed' ? estimatedSubtaskLatency : null, | ||
| }); |
There was a problem hiding this comment.
Move authoritative per-subtask derivation out of the widget layer.
deriveSubtaskRows synthesizes status/cost/latency/model rows from aggregates. That makes the component own inferred state instead of rendering projection/API truth, so displayed subtask details can drift from backend reality.
As per coding guidelines, "Widgets may render projection/API data and presentation state only. They must not ... implement reducers, or infer authoritative state ... Add upstream projection/API surface before adding component-side reducer or backend client."
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@src/components/dashboard/swarm-control-plane/SwarmSubtaskDetailPanel.tsx`
around lines 24 - 65, The deriveSubtaskRows function in SwarmSubtaskDetailPanel
synthesizes authoritative per-subtask state (status, costUsd, latencyMs, model)
from aggregate fields on SwarmRunRow; move this logic out of the widget so the
component only renders API/projection data. Replace deriveSubtaskRows with a
thin adapter that expects an explicit array of SwarmSubtaskRow-like objects
provided by upstream (or add a new run.subtasks: SwarmSubtaskRow[] projection on
the API/selector), remove in-component inference of perSubtaskCost,
estimatedSubtaskLatency, status derivation, and model selection, and update all
callers to supply the authoritative subtask array (or call the new projection
helper) instead of relying on deriveSubtaskRows.
| async function fetchJson<T>(path: string): Promise<T> { | ||
| const res = await fetch(path); | ||
| if (!res.ok) return { rows: [] } as T; | ||
| const body = (await res.json()) as unknown; | ||
| return body as T; | ||
| } | ||
|
|
||
| // ── Public API ─────────────────────────────────────────────────────────────── | ||
|
|
||
| export function fetchSwarmRuns(opts?: SwarmApiOptions): Promise<SwarmRunsResponse> { | ||
| return fetchJson<SwarmRunsResponse>(`${resolveBase(opts)}/swarm-runs`); | ||
| } |
There was a problem hiding this comment.
Route swarm data access through src/data-source abstractions.
This adapter calls fetch('/api/swarm-runs') directly, which bypasses the configured snapshot-source path and can diverge from VITE_DATA_SOURCE behavior. Please move this through the approved src/data-source/ abstraction flow (or consume the existing projection-query path).
As per coding guidelines **/*.{ts,tsx}: Do NOT hardcode localhost:3002 anywhere. All data access must go through src/data-source/ abstractions (FileSnapshotSource or HttpSnapshotSource), selected by VITE_DATA_SOURCE environment variable.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@src/services/swarm-api.ts` around lines 52 - 63, The fetchJson/fetchSwarmRuns
implementation currently calls fetch() directly and bypasses the project's
data-source abstraction; replace the direct HTTP call with the approved
data-source/projection-query path by importing and using the
snapshot/data-source client (the abstraction selected by VITE_DATA_SOURCE)
instead of fetch and resolveBase. Specifically, update fetchSwarmRuns (and
fetchJson helper) to call the appropriate data-source API or projection query
method (the module exported by src/data-source/* that returns SwarmRunsResponse)
via SwarmApiOptions/resolveBase-aware plumbing, ensure no hardcoded
localhost:3002 remains, and preserve the SwarmRunsResponse/SwarmApiOptions types
when mapping the returned value.
|
Phase 0 quarantine (OMN-12238): Deferred from the May 25 integration release. Merge conflict + 7 unresolved CR threads (including 2 coding guideline violations). Widget shows placeholder data regardless — no integration dependency. Will re-queue after dev-target cutover. |
Implements the full swarm progression UI against the swarm_runs projection: - B2-T1: SwarmDecompositionTree — parent→subtask tree with aggregate-derived per-subtask statuses and model hints; graceful empty state when subtask_count=0 - B2-T2: SwarmSubtaskCard — colored status badge, 3×3 metrics grid, models-used pill tags, registry hash footer - B2-T3: SwarmWaveVisualization — heuristic 60/40 wave split with dependency connector; Wave 1 dimmed until run complete; empty state when subtask_count=0 - B2-T4: SwarmLiveCounters — 6 aggregate cards (total runs, subtasks, avg speedup, wall time, cost, savings) derived from swarm_runs projection - B2-T5: SwarmSavingsBar — stacked local+savings bar vs cloud equivalent baseline, per-run mini bars (up to 10), zero-cost graceful empty state - SwarmRunTable: 8-column run list with row selection, speedup/savings coloring - SwarmControlPlane: tabbed widget (Overview, Decomposition, Run Detail, Wave View, Savings) wired to swarm.runs.v1 projection topic via useProjectionQuery - Adds TOPICS.swarmRuns, swarm-runs.v1 postgres case, GET /api/swarm-runs route - 10 tests passing
…ns/cost/latency table (B5-T3 gap) Adds SwarmSubtaskDetailPanel showing per-subtask rows with: - subtask_id (derived: run_id prefix + padded index) - description (Subtask 01, 02, ...) - model assigned (cycled from run.models_used) - status (succeeded/failed/skipped/pending, derived from aggregate counts) - tokens_input / tokens_output (null — requires node_swarm_subtask_state_reducer) - cost_usd (evenly distributed from total_cost_usd) - latency_ms (estimated from dispatch_wall_latency_ms / parallelism) Panel is clearly marked as aggregate-derived with a fixture notice. Shows "No subtask data" with reducer name when subtask_count=0. Wired as new "Subtask Detail" tab in SwarmControlPlane alongside existing "Run Detail" aggregate card. Uses List icon from lucide-react. Schema confirmed: swarm_runs has no jsonb subtask column and no separate swarm_subtasks table exists yet in stability-test. All per-subtask data requires node_swarm_subtask_state_reducer projection. 15 new tests + 10 existing passing (25 total).
6313fd7 to
0d42769
Compare
* feat(OMN-11979): redesign delegation dashboard — dense metrics, narrative labels, stale .js cleanup (#119) - Replace donut chart with terminal-style KPI tiles + stacked model bar + task breakdown - Rename Quality Gate → Escalation Detail, humanize failure categories - Fix postgres-projection-reader: correct column names, JSONB string coercion - Add /projection Vite proxy for Express bridge - Remove 8 stale compiled .js files that shadowed .tsx source via Vite resolution - "Quality Gate" → "Local Success", "pass/fail" → "local/escalated" throughout * feat(OMN-12069): delegation correlation trace view Adds the delegation correlation trace view, trigger panel, and dashboard assertion updates needed for CI. * feat(OMN-12133): add projection query endpoints for log entries and traces (#122) Adds GET /api/projections/log-entries and GET /api/projections/traces to the Express server, backed by new queryLogEntries and queryTraces methods on PostgresProjectionReader. Adds traceExplorer topic constant to shared/types/topics.ts. All query params are validated; limits are capped (1000 for entries, 500 for traces). is_running = true when last_event_at < 60s ago. 17 new unit tests + 2 route-level tests. * feat(OMN-12089): B6 delegation savings proof pack — printable audit artifact (#123) Adds DelegationSavingsProofPackPanel as a new 'Savings Proof Pack' tab in the delegation control plane evidence tabs. Renders an exportable audit report from live projection data: correlation IDs for every run, routing decisions (model, rule, confidence), quality gate results (pass/fail + detail), pricing manifest version, per-run savings, cumulative savings summary, and a pass-rate totals row. Print/Save as PDF is handled via window.print() with an injected @media print stylesheet that isolates the printable region to the proof pack table. No server-side PDF generation or new backend endpoints required — all data sourced from DelegationEvidenceSnapshot already in context. 7 tests covering empty state, header rendering, run table, quality gate labels, routing display, totals row, and print button. * feat(OMN-12085): add swarm-runs projection topic to PostgresProjectionReader (#124) Adds onex.snapshot.projection.swarm-runs.v1 case to the Postgres projection reader. Queries omnidash_analytics.swarm_runs and returns all T4 display fields: runId, status, subtask/succeeded/failed counts, modelsUsed, totalCostUsd, cloudEquivalentCostUsd, savingsUsd, parallelismSpeedupRatio, all latency fields, and createdAt. The swarm_runs table already exists on stability-test (15436) with 11 rows. * feat(OMN-12135): add TraceExplorerWidget — two-panel event bus trace explorer (#126) Adds TraceExplorerWidget to omnidash for event bus observability Phase 2. The widget consumes onex.snapshot.projection.traces.v1 to render a left-panel trace list (with running/error indicators) and right-panel chronological timeline of log entries for the selected trace. - TraceExplorerWidget.tsx: two-panel layout, trace cards, timeline, search - TraceExplorerWidget.stories.tsx: Empty, Populated, WithRunningTrace, ErrorTraces, Loading - TraceExplorerWidget.test.tsx: 5 unit tests (loading, empty, render, filter, select prompt) - storybook/fixtures/trace-explorer.ts: buildTraceGroups fixture factory - generate-registry.ts: trace-explorer manifest with projectionSchema + displayContract - index.ts: lazy import wired under trace-explorer/TraceExplorerWidget - integration.part2/3: component count bumped 28→29 with trace-explorer Closes OMN-12135. References OMN-12136, OMN-12137, OMN-12138, OMN-12139. * feat(OMN-12082): add Context Effectiveness Heatmap widget (B4) (#127) Implements the context factor effectiveness visualization showing which context segments help/hurt which models across A3 ablation experiments: - Segment × model matrix with color-coded cells: green (≥75% pass rate), amber (26–74%), red (≤25%) - Clickable cells open an examples panel showing specific token impact, pass/fail counts, and qualitative findings per segment×model combination - Fixture mode: when no live data exists, shows OMN-11241 research results (golden_chain green across all models, claude_md/architecture_patterns red for local models, exemplar green for exact-interface tasks, local_failures neutral) - Fixture badge clearly marks when live projection is absent - Reads from context_experiment_scores table via new onex.snapshot.projection.context.experiment-scores.v1 topic - Adds postgres reader case for context_experiment_scores SELECT - 10 tests passing (empty state, matrix render, cell selection, fixture badge, toggle deselect, footer count) * feat(OMN-12145): add Express dispatch bridge and Kafka command topic (#128) - POST /api/dispatch validates body, builds envelope, publishes to onex.cmd.omnidash.dispatch-request.v1 via kafkajs producer - Returns 400 on missing fields, 503 when broker unavailable - Kafka producer connects on server start, graceful disconnect on SIGTERM/SIGINT - COMMAND_TOPICS registry in shared/types/command-topics.ts (separate from TOPICS to preserve golden-chain onex.snapshot.projection.* invariant) - 9 unit tests with mocked producer covering validation, 503, and happy path Closes OMN-12145, OMN-12149 * feat(OMN-12147): add command dispatch UI — trace action buttons + Cmd+K palette (#129) - New `command-dispatch/` directory with: - `useDispatch.ts` hook: POST /api/dispatch, availability probe, error/loading state - `DispatchButton.tsx`: icon button (RotateCw/Square) with loading/success/error states - `CommandPalette.tsx`: Cmd+K/Ctrl+K overlay with node search, JSON payload editor, dispatch result; includes `useCommandPalette` hook for global keyboard listener - `CommandPalette.stories.tsx`: Open, WithResults, Dispatching, Error story exports - `TraceExplorerWidget.tsx`: adds Stop button on running traces, Re-run button on completed traces - `App.tsx`: mounts `CommandPalette` at app root via `useCommandPalette` hook Closes OMN-12147, relates OMN-12148 * fix(OMN-12149): align dispatch command topic to omnimarket namespace (#131) The node_dispatch_request_handler subscribes to onex.cmd.omnimarket.dispatch-request.v1 (topic naming lint requires consumer service namespace). Aligns the Express bridge publisher topic. * chore(OMN-12171): delete orphaned .js files and add CI enforcement gate (#132) * chore(OMN-12171): delete orphaned .js files and add CI enforcement gate Confirms 298 .js artifacts in src/ and shared/ are stale (all have .ts/.tsx counterparts; tsconfig emits nothing to src/; build output goes to dist/). Deletes them from working tree. Adds scripts/check-no-orphaned-js.sh — fails if any .js file in src/ or shared/ shadows a .ts/.tsx source — and wires it as a CI gate in ci.yml. Also fixes broken @storybook/test import in CommandPalette.stories.tsx (package moved to storybook/test in v10), restoring tsc --noEmit clean pass. * fix(OMN-12171): include command topic in server typecheck * refactor(OMN-12187): Consolidate DRY violations in omnidash * refactor(OMN-12187): Consolidate DRY violations in omnidash Extract shared pure-data helpers (timestampValue, mergeDelegationSessions, buildCostSavingsOverviewResult) into server/projection-reader-shared.ts. Both sqlite-projection-reader.ts and postgres-projection-reader.ts delegate to the shared module, eliminating ~120 lines of duplicated logic. Add 13 unit tests for the shared module covering edge cases in timestamp parsing, session merge semantics, and cost overview aggregation. The integration test files (part2/part3/part4) are distinct suites with different test content — they are not duplicates and are left intact. * fix(OMN-12187): remove unused cost overview input * feat(OMN-12072): Swarm Control Plane widget — decomposition tree, wave view, live counters, savings bar (#125) * feat(OMN-12072): add Swarm Control Plane widget (B2-T1 through B2-T5) Implements the full swarm progression UI against the swarm_runs projection: - B2-T1: SwarmDecompositionTree — parent→subtask tree with aggregate-derived per-subtask statuses and model hints; graceful empty state when subtask_count=0 - B2-T2: SwarmSubtaskCard — colored status badge, 3×3 metrics grid, models-used pill tags, registry hash footer - B2-T3: SwarmWaveVisualization — heuristic 60/40 wave split with dependency connector; Wave 1 dimmed until run complete; empty state when subtask_count=0 - B2-T4: SwarmLiveCounters — 6 aggregate cards (total runs, subtasks, avg speedup, wall time, cost, savings) derived from swarm_runs projection - B2-T5: SwarmSavingsBar — stacked local+savings bar vs cloud equivalent baseline, per-run mini bars (up to 10), zero-cost graceful empty state - SwarmRunTable: 8-column run list with row selection, speedup/savings coloring - SwarmControlPlane: tabbed widget (Overview, Decomposition, Run Detail, Wave View, Savings) wired to swarm.runs.v1 projection topic via useProjectionQuery - Adds TOPICS.swarmRuns, swarm-runs.v1 postgres case, GET /api/swarm-runs route - 10 tests passing * feat(OMN-12072): add SwarmSubtaskDetailPanel — per-subtask model/tokens/cost/latency table (B5-T3 gap) Adds SwarmSubtaskDetailPanel showing per-subtask rows with: - subtask_id (derived: run_id prefix + padded index) - description (Subtask 01, 02, ...) - model assigned (cycled from run.models_used) - status (succeeded/failed/skipped/pending, derived from aggregate counts) - tokens_input / tokens_output (null — requires node_swarm_subtask_state_reducer) - cost_usd (evenly distributed from total_cost_usd) - latency_ms (estimated from dispatch_wall_latency_ms / parallelism) Panel is clearly marked as aggregate-derived with a fixture notice. Shows "No subtask data" with reducer name when subtask_count=0. Wired as new "Subtask Detail" tab in SwarmControlPlane alongside existing "Run Detail" aggregate card. Uses List icon from lucide-react. Schema confirmed: swarm_runs has no jsonb subtask column and no separate swarm_subtasks table exists yet in stability-test. All per-subtask data requires node_swarm_subtask_state_reducer projection. 15 new tests + 10 existing passing (25 total). * refactor(OMN-11578/OMN-11596): reduce cyclomatic complexity in ThreeCanvas and parseDashboardDefinition (#137) OMN-11578: Extract advanceFocusProgress, applyFocusedOpacities, applyIdleOpacities, and projectAxisLabels from the ThreeCanvas useEffect god-function (CCN=83). The tick loop is now a thin orchestrator calling four named helpers via a TickRefs context struct. OMN-11596: Extract validateTopLevelFields and validateLayoutItem from parseDashboardDefinition (CCN=31). The parse function is reduced to an 8-line orchestrator; all conditional branches live in the helpers. * fix(OMN-12245): silence unused swarm route request (#139) * feat(OMN-11658): wire SEA demo dashboard projections (#140) * feat(OMN-11658): wire SEA demo dashboard projections * fix(OMN-11658): make trace rows toggle independently * fix(OMN-11658): polish SEA dashboard rendering * feat(OMN-11658): expose delegation trigger in SEA control plane (#141) * fix(OMN-11658): improve SEA prompt feedback (#142) * fix(OMN-11658): let dashboard fill wide viewports (#143) * fix(OMN-11658): balance dashboard gutters (#144) * fix(OMN-11658): keep SEA stream newest first (#145) * fix(OMN-11658): refresh SEA traces after prompt submit (#146) * fix(OMN-11658): center dashboard shell (#147) * fix(OMN-11658): extend dashboard body to bottom (#148) * fix(OMN-11658): repair empty SEA dashboard layout (#149) * chore(DELEG-002): annotate pricing fallbacks, align fixture model names with bifrost config (#150) * feat(OMN-12070): wire delegation trigger through omnimarket runtime (#151) - Add COMMAND_TOPICS.delegateSkill ('onex.cmd.omnimarket.delegate-skill.v1') to shared/types/command-topics.ts so the topic is contract-declared rather than hardcoded in server/routes.ts - Declare the delegation trigger command topic in contract.yaml with handler node, ingress path, payload shape, and OMNIDASH_RUNTIME_EFFECTS_URL env note - server/routes.ts uses COMMAND_TOPICS.delegateSkill constant; runtime targets stability-test :18085/v1/effects via OMNIDASH_RUNTIME_EFFECTS_URL env var - DelegationRunContext exposes pendingCorrelationId + setPendingCorrelationId so a freshly-dispatched correlation_id propagates to all child panels before projection rows materialize - DelegationRunHeader wires onCorrelationId callback to DelegationTriggerPanel - DelegationEvidenceTabs auto-navigates to correlation-trace tab on dispatch and displays a pending indicator with the correlation_id - DelegationCorrelationTracePanel falls back to pendingCorrelationId when no run is selected and shows appropriate pending vs awaiting-events messages - Test: setPendingCorrelationId updates context value (DelegationRunContext.test) * fix(OMN-12243): add 'delegation' short-topic alias to postgres + sqlite projection readers (#152) GET /projection/delegation was returning rows:[] because the query() switch had no 'delegation' case — only the full versioned topic name. Adds a fall-through alias in both PostgresProjectionReader and SqliteProjectionReader so the short topic name routes to the same decisions.v1 query logic. Test: new routes.test.ts case seeds a SQLite DB and asserts the short alias returns the same rows as the full topic name. * fix(OMN-12287): close fixture gaps and add file-mode banners to dashboard widgets (#153) - Add fileMode prop to ComponentWrapper: renders a [File Mode] badge in the widget header when active, analogous to the [Live] badge, using Text component to comply with the no-typography-inline ESLint rule - Wire fileMode={!isLiveDataSource(mode)} on all 13 widgets that use useDataSourceMode: TraceExplorer, LiveEventStream, ContextHeatmap, DelegationControlPlane, EventStream, ReadinessGate, DelegationMetrics2D, DelegationSavingsWidget, DelegationTokenUsageWidget, DelegationModelRoutingWidget, DelegationQualityGateWidget, AbCompareWidget - Improve emptyHint messages on all updated widgets to include file-mode fixture directory paths so developers know exactly where to add test data - 1124/1124 tests pass * feat(OMN-12288): deep-link delegation runs to trace explorer (#154) - Add traceFilter + setTraceFilter to UISlice so a correlation_id can be passed to TraceExplorer before navigating there (no URL routing required) - DelegationRunTable adds a GitCommitHorizontal icon button per row; clicking it calls setTraceFilter(correlationId) + setActivePage('trace') and stops event propagation so the row-select handler is not also triggered - TraceExplorerWidget initializes its query state from traceFilter on mount and immediately clears the store value so subsequent navigations start fresh - Tests: DelegationRunTable.test.tsx (7 cases), TraceExplorerWidget deep-link test confirms filter pre-fill + store clear (1132 total tests pass) * feat(T22): add DelegationCostComparisonWidget — per-run cost proof surface (#155) - New widget DelegationCostComparisonWidget in delegation/ directory reads from TOPICS.delegationSavings (same source as DelegationSavingsWidget) and surfaces a per-session cost comparison table with columns: Session, Task, Model, Tokens, Latency, Local $, Cloud $, Saved, Provenance - Provenance badges (measured/estimated) on each row per the § 1.3 spec — green for measured (llm_call_metrics), amber for estimated (routing-rule inferred) - Summary KPI bar shows cumulative savings, local/cloud cost, session count, total tokens, measured vs estimated breakdown - Sort controls: date (default), savings descending, tokens descending - maxRows config cap with overflow notice; showProvenance=false hides source column - Registered as 'delegation-cost-comparison' in generate-registry.ts and index.ts - Integration tests updated for count 29 → 30 - 1132/1132 tests pass * fix(OMN-12402): unify CommandPalette node-type badges and Dispatch button contrast (#156) Issue 1: kindColor() mixed design tokens (var(--accent), var(--text-tertiary)) with hardcoded hex (#d97706, #7c3aed), so ORCHESTRATOR/COMPUTE badges rendered as weak plain text while EFFECT/REDUCER read as vivid chips. Drive all four badges from the design-system node-type role tokens (--<kind>{,-soft,-ink}), which exist for both themes, so every badge is a consistent filled chip. Issue 2: the Dispatch button used var(--accent) as its fill (which resolves to --brand-soft, a pale hover tint) plus literal color:'white' text — invisible in light mode. Use the brand fill + on-brand text token (--brand / --primary-foreground) so the CTA stays legible in both themes. Adds CommandPalette.test.tsx asserting both fixes. Visual proof: docs/evidence/OMN-12402-{badges,dispatch-light}.png * fix(OMN-12367): render Correlation Trace tab gracefully in file mode (#159) In VITE_DATA_SOURCE=file mode the /api/delegation/* proxy is not registered (it is wired only when VITE_PROJECTION_API_URL is set), so fetchCorrelationTrace hit the Vite SPA fallback, received index.html, and surfaced a raw 'JSON.parse: unexpected character at line 1 column 1' error. Detect file mode up front in fetchCorrelationTrace and read a per-correlation fixture under fixtures/onex.snapshot.projection.delegation.correlation-trace.v1/ so the surface renders a representative event chain without postgres. In live mode, parse defensively (res.json().catch) so any non-JSON body degrades to an empty trace and the panel shows its graceful no-events message rather than a raw parse error. Adds the delegationCorrelationTrace topic, a representative 2-event fixture for the file-mode decisions correlation (069e55b5...), and tests covering file-mode fixture fetch and non-JSON hardening. * fix(OMN-12399): register Context Effectiveness Heatmap widget in palette (#158) The widget source (component, hook, fixtures, types, test) existed but was registered in zero of the three Track A surfaces, so it never appeared in the widget palette and was unreachable through the supported user flow. - Add lazy import to componentImports in src/components/dashboard/index.ts - Add context-effectiveness-heatmap manifest to MVP_COMPONENTS in scripts/generate-registry.ts (quality category, contextExperimentScores projection, OMN-11241 fixture fallback) - Regenerate src/registry/component-registry.json (30 -> 31 components) - Update integration.part2/part3 component-count + name-list assertions Visual proof (OMN-7093): palette entry + widget rendered on dashboard in file mode using the OMN-11241 research fixture. * fix(OMN-12366): give Delegation Control Plane active tab a clear visual indicator (#157) The 10-tab evidence strip distinguished active from inactive tabs only by background (var(--panel-2) vs transparent); border, text color, and weight were identical, so the strip did not read as tabs. Active tab now uses the --brand accent token: brand border, a 2px brand bottom-border underline, --brand-soft background, and brand-colored semibold label text. All via existing design tokens (no hardcoded hex), verified to read clearly in both light and dark themes. Visual evidence (OMN-7093): - docs/evidence/OMN-12366-tab-strip-dark.png - docs/evidence/OMN-12366-tab-strip-light.png * feat(OMN-12139): add Trace Explorer fixture for onex.snapshot.projection.traces.v1 (#161) Wires a _trace_groups() factory into scripts/generate_fixtures.py and emits five representative TraceGroup records under fixtures/onex.snapshot.projection.traces.v1/, covering every TraceExplorer UI state: multi-node completed, single-node completed, running, error, and a longer multi-node trace. The widget reads each file as one TraceGroup via the legacy useProjectionQuery readAll path; the file-source middleware auto-generates index.json. Shapes mirror the widget's TraceGroup interface exactly (no entity_id); each record is keyed by its correlation_id so the fixture filename matches. DoD: VITE_DATA_SOURCE=file dev server renders 5 traces (1 running, 1 error) in the Trace Explorer in File Mode; Playwright screenshot under docs/evidence/OMN-12139-trace-explorer-file-mode.png proves populated/running/ error states (OMN-7093). npm run build, lint, and full vitest suite (1172 tests) green. * fix(OMN-12397): make Savings Proof Pack source line dynamic by data mode (#160) The printable proof pack hardcoded "Source: OmniNode delegation_events projection" even in file mode, so a PDF exported from fixture data was misleading once removed from the parent Fixture banner. Derive the "Source:" line and reproducibility sentence from useDataSourceMode(): file -> Local fixture file (fixtures/.../demo-delegation-snapshot.json) sqlite -> Local SQLite projection http -> OmniNode projection API via Express bridge postgres -> OmniNode delegation_events projection Non-live modes (file/sqlite) state the report is from a local development data source and is not a live projection. Renamed fixture live-delegation-demo.json -> demo-delegation-snapshot.json; the word 'live' inside fixtures/ was second-order misleading. The file is discovered by directory glob (vite fixturesMiddleware readdirSync), so no code references the old name. dod_evidence: rendered_output screenshot docs/evidence/OMN-12397-proof-pack-file-mode-source.png shows the file-mode footer with the fixture source string and 15 fixture rows. * fix(OMN-12392): repoint run-generate-fixtures.sh to local script and correct CLAUDE.md fixture claim (#162) OMN-12392: `run-generate-fixtures.sh` was looking for `generate_fixtures.py` at `$OMNIBASE_INFRA_PATH/scripts/generate_fixtures.py`, but that path does not exist in omnibase_infra. The script lives in this repo at `scripts/generate_fixtures.py`. Fix: set SCRIPT_PATH to `$REPO/scripts/generate_fixtures.py` while preserving `--project "$OMNIBASE_INFRA_PATH"` for uv dependency resolution (the script imports from omnibase_infra). `npm run generate:fixtures` now works without a non-standard OMNIBASE_INFRA_PATH. Also fix CLAUDE.md hard rule #6 which claimed "Fixtures and layouts are not committed" — fixtures/ IS tracked via the OMN-10305 gitignore carve-out (!fixtures/, !fixtures/**). Correct the claim and add the carve-out explanation. OMN-12393: add a lineage classification table to CLAUDE.md documenting that omnidash/ (dev) is CANONICAL_ACTIVE, omnidash-v2/ is SUPERSEDED, and omnidash-v2-new/ is a TEMP_STAGING clone. Cite verification evidence. * test(OMN-10947): pin dashboard e2e fixture proof (#163) * test(OMN-10947): scope dashboard e2e locators (#164) * fix(OMN-12394): add .env to omnidash .gitignore to prevent credential leak (#165) The .env file holds OMNIDASH_ANALYTICS_DB_URL with a live Postgres password. The existing rule only covered .env.local* — .env itself was unprotected. Adds an explicit .env entry so git never tracks the credential file. * fix: move omnidash event bus config to contract overlay (#166) * chore(OMN-12459): add universal managed gitignore block to omnidash (#168) Appends the onex-managed universal block to .gitignore, adding .env.*, !.env.example, *.log, .idea/, .vscode/, test-results/, and playwright-report/ coverage. .env and .DS_Store were already present individually; the block makes the policy explicit and machine-readable. No tracked files required untracking. * ci(OMN-12477): add dev-only main-target-guard (forward-port from main) (#167) Forward-ports the main-target-guard workflow to dev (the file existed on main but was absent on dev — main<->dev drift). Adds the dev-only variant WITHOUT the hotfix/* bypass: main accepts only dev promotion PRs carrying promotion-receipt: OCC-N. The guard only triggers on base==main, so it is inert on dev-targeting PRs and takes effect at the next dev->main promotion. OMN-12477 * feat(OMN-12674): remove reviewdog workflows and configs (#170) Removes the reviewdog caller workflow from omnidash. No reviewdog required-status-check bindings existed on main protection, dev protection, or any repo ruleset, so this is the only active reviewdog surface in the repo. dod_evidence: OMN-12674 * fix(OMN-12739): tighten delegation dashboard formatting (#171) * fix(OMN-12740): add browser-safe dashboard id fallback (#172) * ci: preserve hotfix main-target allowance for release
* feat(OMN-11979): redesign delegation dashboard — dense metrics, narrative labels, stale .js cleanup (#119)
- Replace donut chart with terminal-style KPI tiles + stacked model bar + task breakdown
- Rename Quality Gate → Escalation Detail, humanize failure categories
- Fix postgres-projection-reader: correct column names, JSONB string coercion
- Add /projection Vite proxy for Express bridge
- Remove 8 stale compiled .js files that shadowed .tsx source via Vite resolution
- "Quality Gate" → "Local Success", "pass/fail" → "local/escalated" throughout
* feat(OMN-12069): delegation correlation trace view
Adds the delegation correlation trace view, trigger panel, and dashboard assertion updates needed for CI.
* feat(OMN-12133): add projection query endpoints for log entries and traces (#122)
Adds GET /api/projections/log-entries and GET /api/projections/traces
to the Express server, backed by new queryLogEntries and queryTraces
methods on PostgresProjectionReader. Adds traceExplorer topic constant
to shared/types/topics.ts. All query params are validated; limits are
capped (1000 for entries, 500 for traces). is_running = true when
last_event_at < 60s ago. 17 new unit tests + 2 route-level tests.
* feat(OMN-12089): B6 delegation savings proof pack — printable audit artifact (#123)
Adds DelegationSavingsProofPackPanel as a new 'Savings Proof Pack' tab in
the delegation control plane evidence tabs. Renders an exportable audit
report from live projection data: correlation IDs for every run, routing
decisions (model, rule, confidence), quality gate results (pass/fail +
detail), pricing manifest version, per-run savings, cumulative savings
summary, and a pass-rate totals row.
Print/Save as PDF is handled via window.print() with an injected @media
print stylesheet that isolates the printable region to the proof pack table.
No server-side PDF generation or new backend endpoints required — all data
sourced from DelegationEvidenceSnapshot already in context.
7 tests covering empty state, header rendering, run table, quality gate
labels, routing display, totals row, and print button.
* feat(OMN-12085): add swarm-runs projection topic to PostgresProjectionReader (#124)
Adds onex.snapshot.projection.swarm-runs.v1 case to the Postgres projection
reader. Queries omnidash_analytics.swarm_runs and returns all T4 display
fields: runId, status, subtask/succeeded/failed counts, modelsUsed,
totalCostUsd, cloudEquivalentCostUsd, savingsUsd, parallelismSpeedupRatio,
all latency fields, and createdAt. The swarm_runs table already exists on
stability-test (15436) with 11 rows.
* feat(OMN-12135): add TraceExplorerWidget — two-panel event bus trace explorer (#126)
Adds TraceExplorerWidget to omnidash for event bus observability Phase 2.
The widget consumes onex.snapshot.projection.traces.v1 to render a
left-panel trace list (with running/error indicators) and right-panel
chronological timeline of log entries for the selected trace.
- TraceExplorerWidget.tsx: two-panel layout, trace cards, timeline, search
- TraceExplorerWidget.stories.tsx: Empty, Populated, WithRunningTrace, ErrorTraces, Loading
- TraceExplorerWidget.test.tsx: 5 unit tests (loading, empty, render, filter, select prompt)
- storybook/fixtures/trace-explorer.ts: buildTraceGroups fixture factory
- generate-registry.ts: trace-explorer manifest with projectionSchema + displayContract
- index.ts: lazy import wired under trace-explorer/TraceExplorerWidget
- integration.part2/3: component count bumped 28→29 with trace-explorer
Closes OMN-12135. References OMN-12136, OMN-12137, OMN-12138, OMN-12139.
* feat(OMN-12082): add Context Effectiveness Heatmap widget (B4) (#127)
Implements the context factor effectiveness visualization showing which
context segments help/hurt which models across A3 ablation experiments:
- Segment × model matrix with color-coded cells:
green (≥75% pass rate), amber (26–74%), red (≤25%)
- Clickable cells open an examples panel showing specific token impact,
pass/fail counts, and qualitative findings per segment×model combination
- Fixture mode: when no live data exists, shows OMN-11241 research results
(golden_chain green across all models, claude_md/architecture_patterns
red for local models, exemplar green for exact-interface tasks,
local_failures neutral)
- Fixture badge clearly marks when live projection is absent
- Reads from context_experiment_scores table via new
onex.snapshot.projection.context.experiment-scores.v1 topic
- Adds postgres reader case for context_experiment_scores SELECT
- 10 tests passing (empty state, matrix render, cell selection, fixture
badge, toggle deselect, footer count)
* feat(OMN-12145): add Express dispatch bridge and Kafka command topic (#128)
- POST /api/dispatch validates body, builds envelope, publishes to
onex.cmd.omnidash.dispatch-request.v1 via kafkajs producer
- Returns 400 on missing fields, 503 when broker unavailable
- Kafka producer connects on server start, graceful disconnect on SIGTERM/SIGINT
- COMMAND_TOPICS registry in shared/types/command-topics.ts (separate from
TOPICS to preserve golden-chain onex.snapshot.projection.* invariant)
- 9 unit tests with mocked producer covering validation, 503, and happy path
Closes OMN-12145, OMN-12149
* feat(OMN-12147): add command dispatch UI — trace action buttons + Cmd+K palette (#129)
- New `command-dispatch/` directory with:
- `useDispatch.ts` hook: POST /api/dispatch, availability probe, error/loading state
- `DispatchButton.tsx`: icon button (RotateCw/Square) with loading/success/error states
- `CommandPalette.tsx`: Cmd+K/Ctrl+K overlay with node search, JSON payload editor, dispatch result; includes `useCommandPalette` hook for global keyboard listener
- `CommandPalette.stories.tsx`: Open, WithResults, Dispatching, Error story exports
- `TraceExplorerWidget.tsx`: adds Stop button on running traces, Re-run button on completed traces
- `App.tsx`: mounts `CommandPalette` at app root via `useCommandPalette` hook
Closes OMN-12147, relates OMN-12148
* fix(OMN-12149): align dispatch command topic to omnimarket namespace (#131)
The node_dispatch_request_handler subscribes to
onex.cmd.omnimarket.dispatch-request.v1 (topic naming lint requires
consumer service namespace). Aligns the Express bridge publisher topic.
* chore(OMN-12171): delete orphaned .js files and add CI enforcement gate (#132)
* chore(OMN-12171): delete orphaned .js files and add CI enforcement gate
Confirms 298 .js artifacts in src/ and shared/ are stale (all have .ts/.tsx
counterparts; tsconfig emits nothing to src/; build output goes to dist/).
Deletes them from working tree.
Adds scripts/check-no-orphaned-js.sh — fails if any .js file in src/ or shared/
shadows a .ts/.tsx source — and wires it as a CI gate in ci.yml.
Also fixes broken @storybook/test import in CommandPalette.stories.tsx
(package moved to storybook/test in v10), restoring tsc --noEmit clean pass.
* fix(OMN-12171): include command topic in server typecheck
* refactor(OMN-12187): Consolidate DRY violations in omnidash
* refactor(OMN-12187): Consolidate DRY violations in omnidash
Extract shared pure-data helpers (timestampValue, mergeDelegationSessions,
buildCostSavingsOverviewResult) into server/projection-reader-shared.ts.
Both sqlite-projection-reader.ts and postgres-projection-reader.ts delegate
to the shared module, eliminating ~120 lines of duplicated logic.
Add 13 unit tests for the shared module covering edge cases in timestamp
parsing, session merge semantics, and cost overview aggregation.
The integration test files (part2/part3/part4) are distinct suites with
different test content — they are not duplicates and are left intact.
* fix(OMN-12187): remove unused cost overview input
* feat(OMN-12072): Swarm Control Plane widget — decomposition tree, wave view, live counters, savings bar (#125)
* feat(OMN-12072): add Swarm Control Plane widget (B2-T1 through B2-T5)
Implements the full swarm progression UI against the swarm_runs projection:
- B2-T1: SwarmDecompositionTree — parent→subtask tree with aggregate-derived
per-subtask statuses and model hints; graceful empty state when subtask_count=0
- B2-T2: SwarmSubtaskCard — colored status badge, 3×3 metrics grid, models-used
pill tags, registry hash footer
- B2-T3: SwarmWaveVisualization — heuristic 60/40 wave split with dependency
connector; Wave 1 dimmed until run complete; empty state when subtask_count=0
- B2-T4: SwarmLiveCounters — 6 aggregate cards (total runs, subtasks, avg speedup,
wall time, cost, savings) derived from swarm_runs projection
- B2-T5: SwarmSavingsBar — stacked local+savings bar vs cloud equivalent baseline,
per-run mini bars (up to 10), zero-cost graceful empty state
- SwarmRunTable: 8-column run list with row selection, speedup/savings coloring
- SwarmControlPlane: tabbed widget (Overview, Decomposition, Run Detail, Wave View,
Savings) wired to swarm.runs.v1 projection topic via useProjectionQuery
- Adds TOPICS.swarmRuns, swarm-runs.v1 postgres case, GET /api/swarm-runs route
- 10 tests passing
* feat(OMN-12072): add SwarmSubtaskDetailPanel — per-subtask model/tokens/cost/latency table (B5-T3 gap)
Adds SwarmSubtaskDetailPanel showing per-subtask rows with:
- subtask_id (derived: run_id prefix + padded index)
- description (Subtask 01, 02, ...)
- model assigned (cycled from run.models_used)
- status (succeeded/failed/skipped/pending, derived from aggregate counts)
- tokens_input / tokens_output (null — requires node_swarm_subtask_state_reducer)
- cost_usd (evenly distributed from total_cost_usd)
- latency_ms (estimated from dispatch_wall_latency_ms / parallelism)
Panel is clearly marked as aggregate-derived with a fixture notice.
Shows "No subtask data" with reducer name when subtask_count=0.
Wired as new "Subtask Detail" tab in SwarmControlPlane alongside existing
"Run Detail" aggregate card. Uses List icon from lucide-react.
Schema confirmed: swarm_runs has no jsonb subtask column and no separate
swarm_subtasks table exists yet in stability-test. All per-subtask data
requires node_swarm_subtask_state_reducer projection.
15 new tests + 10 existing passing (25 total).
* refactor(OMN-11578/OMN-11596): reduce cyclomatic complexity in ThreeCanvas and parseDashboardDefinition (#137)
OMN-11578: Extract advanceFocusProgress, applyFocusedOpacities,
applyIdleOpacities, and projectAxisLabels from the ThreeCanvas
useEffect god-function (CCN=83). The tick loop is now a thin
orchestrator calling four named helpers via a TickRefs context struct.
OMN-11596: Extract validateTopLevelFields and validateLayoutItem from
parseDashboardDefinition (CCN=31). The parse function is reduced to
an 8-line orchestrator; all conditional branches live in the helpers.
* fix(OMN-12245): silence unused swarm route request (#139)
* feat(OMN-11658): wire SEA demo dashboard projections (#140)
* feat(OMN-11658): wire SEA demo dashboard projections
* fix(OMN-11658): make trace rows toggle independently
* fix(OMN-11658): polish SEA dashboard rendering
* feat(OMN-11658): expose delegation trigger in SEA control plane (#141)
* fix(OMN-11658): improve SEA prompt feedback (#142)
* fix(OMN-11658): let dashboard fill wide viewports (#143)
* fix(OMN-11658): balance dashboard gutters (#144)
* fix(OMN-11658): keep SEA stream newest first (#145)
* fix(OMN-11658): refresh SEA traces after prompt submit (#146)
* fix(OMN-11658): center dashboard shell (#147)
* fix(OMN-11658): extend dashboard body to bottom (#148)
* fix(OMN-11658): repair empty SEA dashboard layout (#149)
* chore(DELEG-002): annotate pricing fallbacks, align fixture model names with bifrost config (#150)
* feat(OMN-12070): wire delegation trigger through omnimarket runtime (#151)
- Add COMMAND_TOPICS.delegateSkill ('onex.cmd.omnimarket.delegate-skill.v1')
to shared/types/command-topics.ts so the topic is contract-declared rather
than hardcoded in server/routes.ts
- Declare the delegation trigger command topic in contract.yaml with handler
node, ingress path, payload shape, and OMNIDASH_RUNTIME_EFFECTS_URL env note
- server/routes.ts uses COMMAND_TOPICS.delegateSkill constant; runtime targets
stability-test :18085/v1/effects via OMNIDASH_RUNTIME_EFFECTS_URL env var
- DelegationRunContext exposes pendingCorrelationId + setPendingCorrelationId
so a freshly-dispatched correlation_id propagates to all child panels before
projection rows materialize
- DelegationRunHeader wires onCorrelationId callback to DelegationTriggerPanel
- DelegationEvidenceTabs auto-navigates to correlation-trace tab on dispatch
and displays a pending indicator with the correlation_id
- DelegationCorrelationTracePanel falls back to pendingCorrelationId when no
run is selected and shows appropriate pending vs awaiting-events messages
- Test: setPendingCorrelationId updates context value (DelegationRunContext.test)
* fix(OMN-12243): add 'delegation' short-topic alias to postgres + sqlite projection readers (#152)
GET /projection/delegation was returning rows:[] because the query() switch
had no 'delegation' case — only the full versioned topic name. Adds a
fall-through alias in both PostgresProjectionReader and SqliteProjectionReader
so the short topic name routes to the same decisions.v1 query logic.
Test: new routes.test.ts case seeds a SQLite DB and asserts the short alias
returns the same rows as the full topic name.
* fix(OMN-12287): close fixture gaps and add file-mode banners to dashboard widgets (#153)
- Add fileMode prop to ComponentWrapper: renders a [File Mode] badge in the
widget header when active, analogous to the [Live] badge, using Text component
to comply with the no-typography-inline ESLint rule
- Wire fileMode={!isLiveDataSource(mode)} on all 13 widgets that use
useDataSourceMode: TraceExplorer, LiveEventStream, ContextHeatmap,
DelegationControlPlane, EventStream, ReadinessGate, DelegationMetrics2D,
DelegationSavingsWidget, DelegationTokenUsageWidget,
DelegationModelRoutingWidget, DelegationQualityGateWidget, AbCompareWidget
- Improve emptyHint messages on all updated widgets to include file-mode
fixture directory paths so developers know exactly where to add test data
- 1124/1124 tests pass
* feat(OMN-12288): deep-link delegation runs to trace explorer (#154)
- Add traceFilter + setTraceFilter to UISlice so a correlation_id can be
passed to TraceExplorer before navigating there (no URL routing required)
- DelegationRunTable adds a GitCommitHorizontal icon button per row; clicking
it calls setTraceFilter(correlationId) + setActivePage('trace') and stops
event propagation so the row-select handler is not also triggered
- TraceExplorerWidget initializes its query state from traceFilter on mount
and immediately clears the store value so subsequent navigations start fresh
- Tests: DelegationRunTable.test.tsx (7 cases), TraceExplorerWidget deep-link
test confirms filter pre-fill + store clear (1132 total tests pass)
* feat(T22): add DelegationCostComparisonWidget — per-run cost proof surface (#155)
- New widget DelegationCostComparisonWidget in delegation/ directory reads from
TOPICS.delegationSavings (same source as DelegationSavingsWidget) and surfaces
a per-session cost comparison table with columns: Session, Task, Model, Tokens,
Latency, Local $, Cloud $, Saved, Provenance
- Provenance badges (measured/estimated) on each row per the § 1.3 spec — green
for measured (llm_call_metrics), amber for estimated (routing-rule inferred)
- Summary KPI bar shows cumulative savings, local/cloud cost, session count,
total tokens, measured vs estimated breakdown
- Sort controls: date (default), savings descending, tokens descending
- maxRows config cap with overflow notice; showProvenance=false hides source column
- Registered as 'delegation-cost-comparison' in generate-registry.ts and index.ts
- Integration tests updated for count 29 → 30
- 1132/1132 tests pass
* fix(OMN-12402): unify CommandPalette node-type badges and Dispatch button contrast (#156)
Issue 1: kindColor() mixed design tokens (var(--accent), var(--text-tertiary))
with hardcoded hex (#d97706, #7c3aed), so ORCHESTRATOR/COMPUTE badges rendered
as weak plain text while EFFECT/REDUCER read as vivid chips. Drive all four
badges from the design-system node-type role tokens (--<kind>{,-soft,-ink}),
which exist for both themes, so every badge is a consistent filled chip.
Issue 2: the Dispatch button used var(--accent) as its fill (which resolves to
--brand-soft, a pale hover tint) plus literal color:'white' text — invisible in
light mode. Use the brand fill + on-brand text token (--brand /
--primary-foreground) so the CTA stays legible in both themes.
Adds CommandPalette.test.tsx asserting both fixes. Visual proof:
docs/evidence/OMN-12402-{badges,dispatch-light}.png
* fix(OMN-12367): render Correlation Trace tab gracefully in file mode (#159)
In VITE_DATA_SOURCE=file mode the /api/delegation/* proxy is not registered
(it is wired only when VITE_PROJECTION_API_URL is set), so fetchCorrelationTrace
hit the Vite SPA fallback, received index.html, and surfaced a raw
'JSON.parse: unexpected character at line 1 column 1' error.
Detect file mode up front in fetchCorrelationTrace and read a per-correlation
fixture under fixtures/onex.snapshot.projection.delegation.correlation-trace.v1/
so the surface renders a representative event chain without postgres. In live
mode, parse defensively (res.json().catch) so any non-JSON body degrades to an
empty trace and the panel shows its graceful no-events message rather than a raw
parse error.
Adds the delegationCorrelationTrace topic, a representative 2-event fixture for
the file-mode decisions correlation (069e55b5...), and tests covering file-mode
fixture fetch and non-JSON hardening.
* fix(OMN-12399): register Context Effectiveness Heatmap widget in palette (#158)
The widget source (component, hook, fixtures, types, test) existed but was
registered in zero of the three Track A surfaces, so it never appeared in the
widget palette and was unreachable through the supported user flow.
- Add lazy import to componentImports in src/components/dashboard/index.ts
- Add context-effectiveness-heatmap manifest to MVP_COMPONENTS in
scripts/generate-registry.ts (quality category, contextExperimentScores
projection, OMN-11241 fixture fallback)
- Regenerate src/registry/component-registry.json (30 -> 31 components)
- Update integration.part2/part3 component-count + name-list assertions
Visual proof (OMN-7093): palette entry + widget rendered on dashboard in
file mode using the OMN-11241 research fixture.
* fix(OMN-12366): give Delegation Control Plane active tab a clear visual indicator (#157)
The 10-tab evidence strip distinguished active from inactive tabs only by
background (var(--panel-2) vs transparent); border, text color, and weight
were identical, so the strip did not read as tabs.
Active tab now uses the --brand accent token: brand border, a 2px brand
bottom-border underline, --brand-soft background, and brand-colored
semibold label text. All via existing design tokens (no hardcoded hex),
verified to read clearly in both light and dark themes.
Visual evidence (OMN-7093):
- docs/evidence/OMN-12366-tab-strip-dark.png
- docs/evidence/OMN-12366-tab-strip-light.png
* feat(OMN-12139): add Trace Explorer fixture for onex.snapshot.projection.traces.v1 (#161)
Wires a _trace_groups() factory into scripts/generate_fixtures.py and emits
five representative TraceGroup records under
fixtures/onex.snapshot.projection.traces.v1/, covering every TraceExplorer
UI state: multi-node completed, single-node completed, running, error, and a
longer multi-node trace. The widget reads each file as one TraceGroup via the
legacy useProjectionQuery readAll path; the file-source middleware
auto-generates index.json.
Shapes mirror the widget's TraceGroup interface exactly (no entity_id); each
record is keyed by its correlation_id so the fixture filename matches.
DoD: VITE_DATA_SOURCE=file dev server renders 5 traces (1 running, 1 error)
in the Trace Explorer in File Mode; Playwright screenshot under
docs/evidence/OMN-12139-trace-explorer-file-mode.png proves populated/running/
error states (OMN-7093). npm run build, lint, and full vitest suite (1172
tests) green.
* fix(OMN-12397): make Savings Proof Pack source line dynamic by data mode (#160)
The printable proof pack hardcoded "Source: OmniNode delegation_events
projection" even in file mode, so a PDF exported from fixture data was
misleading once removed from the parent Fixture banner. Derive the
"Source:" line and reproducibility sentence from useDataSourceMode():
file -> Local fixture file (fixtures/.../demo-delegation-snapshot.json)
sqlite -> Local SQLite projection
http -> OmniNode projection API via Express bridge
postgres -> OmniNode delegation_events projection
Non-live modes (file/sqlite) state the report is from a local
development data source and is not a live projection.
Renamed fixture live-delegation-demo.json -> demo-delegation-snapshot.json;
the word 'live' inside fixtures/ was second-order misleading. The file is
discovered by directory glob (vite fixturesMiddleware readdirSync), so no
code references the old name.
dod_evidence: rendered_output screenshot
docs/evidence/OMN-12397-proof-pack-file-mode-source.png shows the
file-mode footer with the fixture source string and 15 fixture rows.
* fix(OMN-12392): repoint run-generate-fixtures.sh to local script and correct CLAUDE.md fixture claim (#162)
OMN-12392: `run-generate-fixtures.sh` was looking for `generate_fixtures.py` at
`$OMNIBASE_INFRA_PATH/scripts/generate_fixtures.py`, but that path does not exist in
omnibase_infra. The script lives in this repo at `scripts/generate_fixtures.py`.
Fix: set SCRIPT_PATH to `$REPO/scripts/generate_fixtures.py` while preserving
`--project "$OMNIBASE_INFRA_PATH"` for uv dependency resolution (the script imports
from omnibase_infra). `npm run generate:fixtures` now works without a non-standard
OMNIBASE_INFRA_PATH.
Also fix CLAUDE.md hard rule #6 which claimed "Fixtures and layouts are not committed"
— fixtures/ IS tracked via the OMN-10305 gitignore carve-out (!fixtures/, !fixtures/**).
Correct the claim and add the carve-out explanation.
OMN-12393: add a lineage classification table to CLAUDE.md documenting that
omnidash/ (dev) is CANONICAL_ACTIVE, omnidash-v2/ is SUPERSEDED, and
omnidash-v2-new/ is a TEMP_STAGING clone. Cite verification evidence.
* test(OMN-10947): pin dashboard e2e fixture proof (#163)
* test(OMN-10947): scope dashboard e2e locators (#164)
* fix(OMN-12394): add .env to omnidash .gitignore to prevent credential leak (#165)
The .env file holds OMNIDASH_ANALYTICS_DB_URL with a live Postgres password.
The existing rule only covered .env.local* — .env itself was unprotected.
Adds an explicit .env entry so git never tracks the credential file.
* fix: move omnidash event bus config to contract overlay (#166)
* chore(OMN-12459): add universal managed gitignore block to omnidash (#168)
Appends the onex-managed universal block to .gitignore, adding
.env.*, !.env.example, *.log, .idea/, .vscode/, test-results/, and
playwright-report/ coverage. .env and .DS_Store were already present
individually; the block makes the policy explicit and machine-readable.
No tracked files required untracking.
* ci(OMN-12477): add dev-only main-target-guard (forward-port from main) (#167)
Forward-ports the main-target-guard workflow to dev (the file existed on
main but was absent on dev — main<->dev drift). Adds the dev-only variant
WITHOUT the hotfix/* bypass: main accepts only dev promotion PRs carrying
promotion-receipt: OCC-N. The guard only triggers on base==main, so it is
inert on dev-targeting PRs and takes effect at the next dev->main promotion.
OMN-12477
* feat(OMN-12674): remove reviewdog workflows and configs (#170)
Removes the reviewdog caller workflow from omnidash. No reviewdog
required-status-check bindings existed on main protection, dev
protection, or any repo ruleset, so this is the only active reviewdog
surface in the repo.
dod_evidence: OMN-12674
* fix(OMN-12739): tighten delegation dashboard formatting (#171)
* fix(OMN-12740): add browser-safe dashboard id fallback (#172)
* feat(OMN-12745): add DelegationModelOutputWidget rendering inference generated_text (#177)
Implements the demo-blocking deliverable for OMN-12745: DelegationModelOutputWidget
renders ModelLlmInferenceResponse.generated_text sourced from bus topic
onex.evt.omnibase-infra.inference-response.v1 in fixture mode (offline, no .201
dependency). Live surfacing is gated on the OMN-12746 demo redeploy.
Changes:
- src/components/dashboard/delegation/DelegationModelOutputWidget.tsx — new widget
rendering latest generated_text, model identity, task type, token count, latency,
and a recent response history list
- src/components/dashboard/delegation/DelegationModelOutputWidget.test.tsx — 8 unit
tests (TDD-first: loading, empty, generated_text, model identity, topic label,
upstream notice, provisioned, token count)
- src/components/dashboard/delegation/DelegationModelOutputWidget.stories.tsx — 6
stories: Empty, Loading, Populated, FixtureMode, Provisioned, SingleResponse
- shared/types/topics.ts — adds inferenceResponseText topic key
- src/storybook/fixtures/delegation-routing.ts — adds buildInferenceResponseProjection
- src/storybook/fixtures/index.ts — exports new fixture builder
- scripts/generate-registry.ts — adds delegation-model-output MVP manifest entry
- src/registry/component-registry.json — regenerated (32 components)
- src/components/dashboard/index.ts — lazy-import registration
- src/templates/sea-demo.ts — delegation-model-output added to SEA Demo template
- src/templates/templates.test.ts — updated SEA Demo widget order assertions
- src/integration.part2.test.tsx — component count 31 to 32 and name list
- src/integration.part3.test.tsx — component count 31 to 32
- src/storybook-coverage-compliance.test.ts — adds story file to compliance list
- fixtures/onex.snapshot.projection.delegation.inference-response-text.v1/snapshot.json
— fixture data for file mode (offline demo)
Evidence-Source OCC SHA: c872f8edbabda4306e3dd290dbb8995031da3d7c
* feat(OMN-12748): render delegated prompt/response from contract-declared projection (#178)
The Correlation Trace panel already renders prompt_text/response_text per run,
but fetchCorrelationTrace called a bespoke /api/delegation/correlation-trace/<id>
REST route that the deployed contract-driven projection API does not implement
(404), so the panel always showed 'No events found' and the dashboard could not
surface the actual delegated work.
Read the per-correlation trace from the contract-declared projection instead:
/projection/onex.snapshot.projection.delegation.correlation-trace.v1?correlation_id=<id>
(a read path the projection API already supports). Selecting a run now renders
its prompt + response. File mode still reads the per-correlation fixture.
Pairs with omnimarket OMN-12748, which exposes that projection over
delegation_events. Canonical: client renders a contract-declared projection; no
bespoke REST route.
* chore(release)(OMN-12245): backmerge omnidash v1.1.1 to dev (#176)
* fix(OMN-12756): publish delegation trigger through command bus (#179)
* feat(OMN-12748): render delegated prompt/response from contract-declared projection
The Correlation Trace panel already renders prompt_text/response_text per run,
but fetchCorrelationTrace called a bespoke /api/delegation/correlation-trace/<id>
REST route that the deployed contract-driven projection API does not implement
(404), so the panel always showed 'No events found' and the dashboard could not
surface the actual delegated work.
Read the per-correlation trace from the contract-declared projection instead:
/projection/onex.snapshot.projection.delegation.correlation-trace.v1?correlation_id=<id>
(a read path the projection API already supports). Selecting a run now renders
its prompt + response. File mode still reads the per-correlation fixture.
Pairs with omnimarket OMN-12748, which exposes that projection over
delegation_events. Canonical: client renders a contract-declared projection; no
bespoke REST route.
* fix(OMN-12756): route delegation API to dashboard server
* chore(OMN-12765): backmerge omnidash main to dev (#181)
* feat(OMN-11614, OMN-11617): deduplicate projection readers and integration test utilities (#120)
* feat(OMN-11979): redesign delegation dashboard — dense metrics, narrative labels, stale .js cleanup (#119)
- Replace donut chart with terminal-style KPI tiles + stacked model bar + task breakdown
- Rename Quality Gate → Escalation Detail, humanize failure categories
- Fix postgres-projection-reader: correct column names, JSONB string coercion
- Add /projection Vite proxy for Express bridge
- Remove 8 stale compiled .js files that shadowed .tsx source via Vite resolution
- "Quality Gate" → "Local Success", "pass/fail" → "local/escalated" throughout
* feat(OMN-11614, OMN-11617): deduplicate projection readers and integration test utilities
OMN-11614: Extract shared session-merge and cost-savings-overview aggregation
logic from postgres-projection-reader.ts and sqlite-projection-reader.ts into
server/projection-utils.ts. Removes ~160 lines of duplicated code addressed
by the TODO(OMN-10976) comment. Fixes pre-existing test failures in
postgres-projection-reader.test.ts where string-typed token assertions
contradicted the numeric coercion the reader actually applies.
OMN-11617: Extract manifest loading and QueryClient factory into
src/test-utils/integrationHelpers.ts. Updates integration.part2/3/4.test.tsx
to import TEST_MANIFEST and createTestQueryClient instead of duplicating the
readFileSync + JSON.parse pattern and bare QueryClient constructor.
17 new unit tests for projection-utils.ts cover timestampValue, sessionKey,
mergeDelegationSessions, sessionTokens, and buildCostSavingsOverview.
* fix(OMN-11614, OMN-11617): update delegation widget tests to match OMN-11979 redesign
Tests were written against the old design (KPI tiles with "Pass rate",
"Passed", "Failed" labels; donut panel 420px wide; "QG Pass" column;
"Failure categories" heading; "Recent checks" label). OMN-11979 shipped
a dense terminal-style redesign that changed these rendered strings.
Update assertions to match the current component output:
- DelegationMetrics3D: maxWidth 420→240, canvas height 200→140
- DelegationMetrics2D: "N/M pass" → "N/M local"
- DelegationModelRoutingWidget: column "QG Pass" → "Local Rate"
- DelegationQualityGateWidget: empty msg, check-type labels, section
headings, recent-checks label all updated to current rendered text
All 1044 tests pass (0 new failures introduced).
* chore: release v1.1.0 (OMN-12245) (#135)
* feat(OMN-11979): redesign delegation dashboard — dense metrics, narrative labels, stale .js cleanup (#119)
- Replace donut chart with terminal-style KPI tiles + stacked model bar + task breakdown
- Rename Quality Gate → Escalation Detail, humanize failure categories
- Fix postgres-projection-reader: correct column names, JSONB string coercion
- Add /projection Vite proxy for Express bridge
- Remove 8 stale compiled .js files that shadowed .tsx source via Vite resolution
- "Quality Gate" → "Local Success", "pass/fail" → "local/escalated" throughout
* feat(OMN-12069): delegation correlation trace view
Adds the delegation correlation trace view, trigger panel, and dashboard assertion updates needed for CI.
* feat(OMN-12133): add projection query endpoints for log entries and traces (#122)
Adds GET /api/projections/log-entries and GET /api/projections/traces
to the Express server, backed by new queryLogEntries and queryTraces
methods on PostgresProjectionReader. Adds traceExplorer topic constant
to shared/types/topics.ts. All query params are validated; limits are
capped (1000 for entries, 500 for traces). is_running = true when
last_event_at < 60s ago. 17 new unit tests + 2 route-level tests.
* feat(OMN-12089): B6 delegation savings proof pack — printable audit artifact (#123)
Adds DelegationSavingsProofPackPanel as a new 'Savings Proof Pack' tab in
the delegation control plane evidence tabs. Renders an exportable audit
report from live projection data: correlation IDs for every run, routing
decisions (model, rule, confidence), quality gate results (pass/fail +
detail), pricing manifest version, per-run savings, cumulative savings
summary, and a pass-rate totals row.
Print/Save as PDF is handled via window.print() with an injected @media
print stylesheet that isolates the printable region to the proof pack table.
No server-side PDF generation or new backend endpoints required — all data
sourced from DelegationEvidenceSnapshot already in context.
7 tests covering empty state, header rendering, run table, quality gate
labels, routing display, totals row, and print button.
* feat(OMN-12085): add swarm-runs projection topic to PostgresProjectionReader (#124)
Adds onex.snapshot.projection.swarm-runs.v1 case to the Postgres projection
reader. Queries omnidash_analytics.swarm_runs and returns all T4 display
fields: runId, status, subtask/succeeded/failed counts, modelsUsed,
totalCostUsd, cloudEquivalentCostUsd, savingsUsd, parallelismSpeedupRatio,
all latency fields, and createdAt. The swarm_runs table already exists on
stability-test (15436) with 11 rows.
* feat(OMN-12135): add TraceExplorerWidget — two-panel event bus trace explorer (#126)
Adds TraceExplorerWidget to omnidash for event bus observability Phase 2.
The widget consumes onex.snapshot.projection.traces.v1 to render a
left-panel trace list (with running/error indicators) and right-panel
chronological timeline of log entries for the selected trace.
- TraceExplorerWidget.tsx: two-panel layout, trace cards, timeline, search
- TraceExplorerWidget.stories.tsx: Empty, Populated, WithRunningTrace, ErrorTraces, Loading
- TraceExplorerWidget.test.tsx: 5 unit tests (loading, empty, render, filter, select prompt)
- storybook/fixtures/trace-explorer.ts: buildTraceGroups fixture factory
- generate-registry.ts: trace-explorer manifest with projectionSchema + displayContract
- index.ts: lazy import wired under trace-explorer/TraceExplorerWidget
- integration.part2/3: component count bumped 28→29 with trace-explorer
Closes OMN-12135. References OMN-12136, OMN-12137, OMN-12138, OMN-12139.
* feat(OMN-12082): add Context Effectiveness Heatmap widget (B4) (#127)
Implements the context factor effectiveness visualization showing which
context segments help/hurt which models across A3 ablation experiments:
- Segment × model matrix with color-coded cells:
green (≥75% pass rate), amber (26–74%), red (≤25%)
- Clickable cells open an examples panel showing specific token impact,
pass/fail counts, and qualitative findings per segment×model combination
- Fixture mode: when no live data exists, shows OMN-11241 research results
(golden_chain green across all models, claude_md/architecture_patterns
red for local models, exemplar green for exact-interface tasks,
local_failures neutral)
- Fixture badge clearly marks when live projection is absent
- Reads from context_experiment_scores table via new
onex.snapshot.projection.context.experiment-scores.v1 topic
- Adds postgres reader case for context_experiment_scores SELECT
- 10 tests passing (empty state, matrix render, cell selection, fixture
badge, toggle deselect, footer count)
* feat(OMN-12145): add Express dispatch bridge and Kafka command topic (#128)
- POST /api/dispatch validates body, builds envelope, publishes to
onex.cmd.omnidash.dispatch-request.v1 via kafkajs producer
- Returns 400 on missing fields, 503 when broker unavailable
- Kafka producer connects on server start, graceful disconnect on SIGTERM/SIGINT
- COMMAND_TOPICS registry in shared/types/command-topics.ts (separate from
TOPICS to preserve golden-chain onex.snapshot.projection.* invariant)
- 9 unit tests with mocked producer covering validation, 503, and happy path
Closes OMN-12145, OMN-12149
* feat(OMN-12147): add command dispatch UI — trace action buttons + Cmd+K palette (#129)
- New `command-dispatch/` directory with:
- `useDispatch.ts` hook: POST /api/dispatch, availability probe, error/loading state
- `DispatchButton.tsx`: icon button (RotateCw/Square) with loading/success/error states
- `CommandPalette.tsx`: Cmd+K/Ctrl+K overlay with node search, JSON payload editor, dispatch result; includes `useCommandPalette` hook for global keyboard listener
- `CommandPalette.stories.tsx`: Open, WithResults, Dispatching, Error story exports
- `TraceExplorerWidget.tsx`: adds Stop button on running traces, Re-run button on completed traces
- `App.tsx`: mounts `CommandPalette` at app root via `useCommandPalette` hook
Closes OMN-12147, relates OMN-12148
* fix(OMN-12149): align dispatch command topic to omnimarket namespace (#131)
The node_dispatch_request_handler subscribes to
onex.cmd.omnimarket.dispatch-request.v1 (topic naming lint requires
consumer service namespace). Aligns the Express bridge publisher topic.
* chore(OMN-12171): delete orphaned .js files and add CI enforcement gate (#132)
* chore(OMN-12171): delete orphaned .js files and add CI enforcement gate
Confirms 298 .js artifacts in src/ and shared/ are stale (all have .ts/.tsx
counterparts; tsconfig emits nothing to src/; build output goes to dist/).
Deletes them from working tree.
Adds scripts/check-no-orphaned-js.sh — fails if any .js file in src/ or shared/
shadows a .ts/.tsx source — and wires it as a CI gate in ci.yml.
Also fixes broken @storybook/test import in CommandPalette.stories.tsx
(package moved to storybook/test in v10), restoring tsc --noEmit clean pass.
* fix(OMN-12171): include command topic in server typecheck
* refactor(OMN-12187): Consolidate DRY violations in omnidash
* refactor(OMN-12187): Consolidate DRY violations in omnidash
Extract shared pure-data helpers (timestampValue, mergeDelegationSessions,
buildCostSavingsOverviewResult) into server/projection-reader-shared.ts.
Both sqlite-projection-reader.ts and postgres-projection-reader.ts delegate
to the shared module, eliminating ~120 lines of duplicated logic.
Add 13 unit tests for the shared module covering edge cases in timestamp
parsing, session merge semantics, and cost overview aggregation.
The integration test files (part2/part3/part4) are distinct suites with
different test content — they are not duplicates and are left intact.
* fix(OMN-12187): remove unused cost overview input
* chore: release v1.1.0
* ci: re-trigger CI for release promotion PR
* feat(OMN-6877): add 4 observability pages — trace explorer, session replay, sandbox monitor, voice sessions (#136)
- TraceExplorerPage: wraps existing TraceExplorerWidget as a full-page view
- SessionReplayPage: step/play/rewind through recorded sessions with state reconstruction and progress bar
- SandboxMonitorPage: real-time allow/deny stream, deny-rate bar, per-policy breakdown table
- VoiceSessionPage: audio timeline, transcript overlay, turn-taking visualization
- Extends AppPage type and App.tsx routing switch for all 4 new pages
- Adds Observability section to Sidebar with GitBranch/RotateCcw/Shield/Mic icons
- Adds sandboxDecisions, voiceSessions, sessionReplay topics to TOPICS constant
* ci(OMN-12243): add main target guard (#138)
* release: promote omnidash dev to main (2026-06-06) (#174)
* feat(OMN-11979): redesign delegation dashboard — dense metrics, narrative labels, stale .js cleanup (#119)
- Replace donut chart with terminal-style KPI tiles + stacked model bar + task breakdown
- Rename Quality Gate → Escalation Detail, humanize failure categories
- Fix postgres-projection-reader: correct column names, JSONB string coercion
- Add /projection Vite proxy for Express bridge
- Remove 8 stale compiled .js files that shadowed .tsx source via Vite resolution
- "Quality Gate" → "Local Success", "pass/fail" → "local/escalated" throughout
* feat(OMN-12069): delegation correlation trace view
Adds the delegation correlation trace view, trigger panel, and dashboard assertion updates needed for CI.
* feat(OMN-12133): add projection query endpoints for log entries and traces (#122)
Adds GET /api/projections/log-entries and GET /api/projections/traces
to the Express server, backed by new queryLogEntries and queryTraces
methods on PostgresProjectionReader. Adds traceExplorer topic constant
to shared/types/topics.ts. All query params are validated; limits are
capped (1000 for entries, 500 for traces). is_running = true when
last_event_at < 60s ago. 17 new unit tests + 2 route-level tests.
* feat(OMN-12089): B6 delegation savings proof pack — printable audit artifact (#123)
Adds DelegationSavingsProofPackPanel as a new 'Savings Proof Pack' tab in
the delegation control plane evidence tabs. Renders an exportable audit
report from live projection data: correlation IDs for every run, routing
decisions (model, rule, confidence), quality gate results (pass/fail +
detail), pricing manifest version, per-run savings, cumulative savings
summary, and a pass-rate totals row.
Print/Save as PDF is handled via window.print() with an injected @media
print stylesheet that isolates the printable region to the proof pack table.
No server-side PDF generation or new backend endpoints required — all data
sourced from DelegationEvidenceSnapshot already in context.
7 tests covering empty state, header rendering, run table, quality gate
labels, routing display, totals row, and print button.
* feat(OMN-12085): add swarm-runs projection topic to PostgresProjectionReader (#124)
Adds onex.snapshot.projection.swarm-runs.v1 case to the Postgres projection
reader. Queries omnidash_analytics.swarm_runs and returns all T4 display
fields: runId, status, subtask/succeeded/failed counts, modelsUsed,
totalCostUsd, cloudEquivalentCostUsd, savingsUsd, parallelismSpeedupRatio,
all latency fields, and createdAt. The swarm_runs table already exists on
stability-test (15436) with 11 rows.
* feat(OMN-12135): add TraceExplorerWidget — two-panel event bus trace explorer (#126)
Adds TraceExplorerWidget to omnidash for event bus observability Phase 2.
The widget consumes onex.snapshot.projection.traces.v1 to render a
left-panel trace list (with running/error indicators) and right-panel
chronological timeline of log entries for the selected trace.
- TraceExplorerWidget.tsx: two-panel layout, trace cards, timeline, search
- TraceExplorerWidget.stories.tsx: Empty, Populated, WithRunningTrace, ErrorTraces, Loading
- TraceExplorerWidget.test.tsx: 5 unit tests (loading, empty, render, filter, select prompt)
- storybook/fixtures/trace-explorer.ts: buildTraceGroups fixture factory
- generate-registry.ts: trace-explorer manifest with projectionSchema + displayContract
- index.ts: lazy import wired under trace-explorer/TraceExplorerWidget
- integration.part2/3: component count bumped 28→29 with trace-explorer
Closes OMN-12135. References OMN-12136, OMN-12137, OMN-12138, OMN-12139.
* feat(OMN-12082): add Context Effectiveness Heatmap widget (B4) (#127)
Implements the context factor effectiveness visualization showing which
context segments help/hurt which models across A3 ablation experiments:
- Segment × model matrix with color-coded cells:
green (≥75% pass rate), amber (26–74%), red (≤25%)
- Clickable cells open an examples panel showing specific token impact,
pass/fail counts, and qualitative findings per segment×model combination
- Fixture mode: when no live data exists, shows OMN-11241 research results
(golden_chain green across all models, claude_md/architecture_patterns
red for local models, exemplar green for exact-interface tasks,
local_failures neutral)
- Fixture badge clearly marks when live projection is absent
- Reads from context_experiment_scores table via new
onex.snapshot.projection.context.experiment-scores.v1 topic
- Adds postgres reader case for context_experiment_scores SELECT
- 10 tests passing (empty state, matrix render, cell selection, fixture
badge, toggle deselect, footer count)
* feat(OMN-12145): add Express dispatch bridge and Kafka command topic (#128)
- POST /api/dispatch validates body, builds envelope, publishes to
onex.cmd.omnidash.dispatch-request.v1 via kafkajs producer
- Returns 400 on missing fields, 503 when broker unavailable
- Kafka producer connects on server start, graceful disconnect on SIGTERM/SIGINT
- COMMAND_TOPICS registry in shared/types/command-topics.ts (separate from
TOPICS to preserve golden-chain onex.snapshot.projection.* invariant)
- 9 unit tests with mocked producer covering validation, 503, and happy path
Closes OMN-12145, OMN-12149
* feat(OMN-12147): add command dispatch UI — trace action buttons + Cmd+K palette (#129)
- New `command-dispatch/` directory with:
- `useDispatch.ts` hook: POST /api/dispatch, availability probe, error/loading state
- `DispatchButton.tsx`: icon button (RotateCw/Square) with loading/success/error states
- `CommandPalette.tsx`: Cmd+K/Ctrl+K overlay with node search, JSON payload editor, dispatch result; includes `useCommandPalette` hook for global keyboard listener
- `CommandPalette.stories.tsx`: Open, WithResults, Dispatching, Error story exports
- `TraceExplorerWidget.tsx`: adds Stop button on running traces, Re-run button on completed traces
- `App.tsx`: mounts `CommandPalette` at app root via `useCommandPalette` hook
Closes OMN-12147, relates OMN-12148
* fix(OMN-12149): align dispatch command topic to omnimarket namespace (#131)
The node_dispatch_request_handler subscribes to
onex.cmd.omnimarket.dispatch-request.v1 (topic naming lint requires
consumer service namespace). Aligns the Express bridge publisher topic.
* chore(OMN-12171): delete orphaned .js files and add CI enforcement gate (#132)
* chore(OMN-12171): delete orphaned .js files and add CI enforcement gate
Confirms 298 .js artifacts in src/ and shared/ are stale (all have .ts/.tsx
counterparts; tsconfig emits nothing to src/; build output goes to dist/).
Deletes them from working tree.
Adds scripts/check-no-orphaned-js.sh — fails if any .js file in src/ or shared/
shadows a .ts/.tsx source — and wires it as a CI gate in ci.yml.
Also fixes broken @storybook/test import in CommandPalette.stories.tsx
(package moved to storybook/test in v10), restoring tsc --noEmit clean pass.
* fix(OMN-12171): include command topic in server typecheck
* refactor(OMN-12187): Consolidate DRY violations in omnidash
* refactor(OMN-12187): Consolidate DRY violations in omnidash
Extract shared pure-data helpers (timestampValue, mergeDelegationSessions,
buildCostSavingsOverviewResult) into server/projection-reader-shared.ts.
Both sqlite-projection-reader.ts and postgres-projection-reader.ts delegate
to the shared module, eliminating ~120 lines of duplicated logic.
Add 13 unit tests for the shared module covering edge cases in timestamp
parsing, session merge semantics, and cost overview aggregation.
The integration test files (part2/part3/part4) are distinct suites with
different test content — they are not duplicates and are left intact.
* fix(OMN-12187): remove unused cost overview input
* feat(OMN-12072): Swarm Control Plane widget — decomposition tree, wave view, live counters, savings bar (#125)
* feat(OMN-12072): add Swarm Control Plane widget (B2-T1 through B2-T5)
Implements the full swarm progression UI against the swarm_runs projection:
- B2-T1: SwarmDecompositionTree — parent→subtask tree with aggregate-derived
per-subtask statuses and model hints; graceful empty state when subtask_count=0
- B2-T2: SwarmSubtaskCard — colored status badge, 3×3 metrics grid, models-used
pill tags, registry hash footer
- B2-T3: SwarmWaveVisualization — heuristic 60/40 wave split with dependency
connector; Wave 1 dimmed until run complete; empty state when subtask_count=0
- B2-T4: SwarmLiveCounters — 6 aggregate cards (total runs, subtasks, avg speedup,
wall time, cost, savings) derived from swarm_runs projection
- B2-T5: SwarmSavingsBar — stacked local+savings bar vs cloud equivalent baseline,
per-run mini bars (up to 10), zero-cost graceful empty state
- SwarmRunTable: 8-column run list with row selection, speedup/savings coloring
- SwarmControlPlane: tabbed widget (Overview, Decomposition, Run Detail, Wave View,
Savings) wired to swarm.runs.v1 projection topic via useProjectionQuery
- Adds TOPICS.swarmRuns, swarm-runs.v1 postgres case, GET /api/swarm-runs route
- 10 tests passing
* feat(OMN-12072): add SwarmSubtaskDetailPanel — per-subtask model/tokens/cost/latency table (B5-T3 gap)
Adds SwarmSubtaskDetailPanel showing per-subtask rows with:
- subtask_id (derived: run_id prefix + padded index)
- description (Subtask 01, 02, ...)
- model assigned (cycled from run.models_used)
- status (succeeded/failed/skipped/pending, derived from aggregate counts)
- tokens_input / tokens_output (null — requires node_swarm_subtask_state_reducer)
- cost_usd (evenly distributed from total_cost_usd)
- latency_ms (estimated from dispatch_wall_latency_ms / parallelism)
Panel is clearly marked as aggregate-derived with a fixture notice.
Shows "No subtask data" with reducer name when subtask_count=0.
Wired as new "Subtask Detail" tab in SwarmControlPlane alongside existing
"Run Detail" aggregate card. Uses List icon from lucide-react.
Schema confirmed: swarm_runs has no jsonb subtask column and no separate
swarm_subtasks table exists yet in stability-test. All per-subtask data
requires node_swarm_subtask_state_reducer projection.
15 new tests + 10 existing passing (25 total).
* refactor(OMN-11578/OMN-11596): reduce cyclomatic complexity in ThreeCanvas and parseDashboardDefinition (#137)
OMN-11578: Extract advanceFocusProgress, applyFocusedOpacities,
applyIdleOpacities, and projectAxisLabels from the ThreeCanvas
useEffect god-function (CCN=83). The tick loop is now a thin
orchestrator calling four named helpers via a TickRefs context struct.
OMN-11596: Extract validateTopLevelFields and validateLayoutItem from
parseDashboardDefinition (CCN=31). The parse function is reduced to
an 8-line orchestrator; all conditional branches live in the helpers.
* fix(OMN-12245): silence unused swarm route request (#139)
* feat(OMN-11658): wire SEA demo dashboard projections (#140)
* feat(OMN-11658): wire SEA demo dashboard projections
* fix(OMN-11658): make trace rows toggle independently
* fix(OMN-11658): polish SEA dashboard rendering
* feat(OMN-11658): expose delegation trigger in SEA control plane (#141)
* fix(OMN-11658): improve SEA prompt feedback (#142)
* fix(OMN-11658): let dashboard fill wide viewports (#143)
* fix(OMN-11658): balance dashboard gutters (#144)
* fix(OMN-11658): keep SEA stream newest first (#145)
* fix(OMN-11658): refresh SEA traces after prompt submit (#146)
* fix(OMN-11658): center dashboard shell (#147)
* fix(OMN-11658): extend dashboard body to bottom (#148)
* fix(OMN-11658): repair empty SEA dashboard layout (#149)
* chore(DELEG-002): annotate pricing fallbacks, align fixture model names with bifrost config (#150)
* feat(OMN-12070): wire delegation trigger through omnimarket runtime (#151)
- Add COMMAND_TOPICS.delegateSkill ('onex.cmd.omnimarket.delegate-skill.v1')
to shared/types/command-topics.ts so the topic is contract-declared rather
than hardcoded in server/routes.ts
- Declare the delegation trigger command topic in contract.yaml with handler
node, ingress path, payload shape, and OMNIDASH_RUNTIME_EFFECTS_URL env note
- server/routes.ts uses COMMAND_TOPICS.delegateSkill constant; runtime targets
stability-test :18085/v1/effects via OMNIDASH_RUNTIME_EFFECTS_URL env var
- DelegationRunContext exposes pendingCorrelationId + setPendingCorrelationId
so a freshly-dispatched correlation_id propagates to all child panels before
projection rows materialize
- DelegationRunHeader wires onCorrelationId callback to DelegationTriggerPanel
- DelegationEvidenceTabs auto-navigates to correlation-trace tab on dispatch
and displays a pending indicator with the correlation_id
- DelegationCorrelationTracePanel falls back to pendingCorrelationId when no
run is selected and shows appropriate pending vs awaiting-events messages
- Test: setPendingCorrelationId updates context value (DelegationRunContext.test)
* fix(OMN-12243): add 'delegation' short-topic alias to postgres + sqlite projection readers (#152)
GET /projection/delegation was returning rows:[] because the query() switch
had no 'delegation' case — only the full versioned topic name. Adds a
fall-through alias in both PostgresProjectionReader and SqliteProjectionReader
so the short topic name routes to the same decisions.v1 query logic.
Test: new routes.test.ts case seeds a SQLite DB and asserts the short alias
returns the same rows as the full topic name.
* fix(OMN-12287): close fixture gaps and add file-mode banners to dashboard widgets (#153)
- Add fileMode prop to ComponentWrapper: renders a [File Mode] badge in the
widget header when active, analogous to the [Live] badge, using Text component
to comply with the no-typography-inline ESLint rule
- Wire fileMode={!isLiveDataSource(mode)} on all 13 widgets that use
useDataSourceMode: TraceExplorer, LiveEventStream, ContextHeatmap,
DelegationControlPlane, EventStream, ReadinessGate, DelegationMetrics2D,
DelegationSavingsWidget, DelegationTokenUsageWidget,
DelegationModelRoutingWidget, DelegationQualityGateWidget, AbCompareWidget
- Improve emptyHint messages on all updated widgets to include file-mode
fixture directory paths so developers know exactly where to add test data
- 1124/1124 tests pass
* feat(OMN-12288): deep-link delegation runs to trace explorer (#154)
- Add traceFilter + setTraceFilter to UISlice so a correlation_id can be
passed to TraceExplorer before navigating there (no URL routing required)
- DelegationRunTable adds a GitCommitHorizontal icon button per row; clicking
it calls setTraceFilter(correlationId) + setActivePage('trace') and stops
event propagation so the row-select handler is not also triggered
- TraceExplorerWidget initializes its query state from traceFilter on mount
and immediately clears the store value so subsequent navigations start fresh
- Tests: DelegationRunTable.test.tsx (7 cases), TraceExplorerWidget deep-link
test confirms filter pre-fill + store clear (1132 total tests pass)
* feat(T22): add DelegationCostComparisonWidget — per-run cost proof surface (#155)
- New widget DelegationCostComparisonWidget in delegation/ directory reads from
TOPICS.delegationSavings (same source as DelegationSavingsWidget) and surfaces
a per-session cost comparison table with columns: Session, Task, Model, Tokens,
Latency, Local $, Cloud $, Saved, Provenance
- Provenance badges (measured/estimated) on each row per the § 1.3 spec — green
for measured (llm_call_metrics), amber for estimated (routing-rule inferred)
- Summary KPI bar shows cumulative savings, local/cloud cost, session count,
total tokens, measured vs estimated breakdown
- Sort controls: date (default), savings descending, tokens descending
- maxRows config cap with overflow notice; showProvenance=false hides source column
- Registered as 'delegation-cost-comparison' in generate-registry.ts and index.ts
- Integration tests updated for count 29 → 30
- 1132/1132 tests pass
* fix(OMN-12402): unify CommandPalette node-type badges and Dispatch button contrast (#156)
Issue 1: kindColor() mixed design tokens (var(--accent), var(--text-tertiary))
with hardcoded hex (#d97706, #7c3aed), so ORCHESTRATOR/COMPUTE badges rendered
as weak plain text while EFFECT/REDUCER read as vivid chips. Drive all four
badges from the design-system node-type role tokens (--<kind>{,-soft,-ink}),
which exist for both themes, so every badge is a consistent filled chip.
Issue 2: the Dispatch button used var(--accent) as its fill (which resolves to
--brand-soft, a pale hover tint) plus literal color:'white' text — invisible in
light mode. Use the brand fill + on-brand text token (--brand /
--primary-foreground) so the CTA stays legible in both themes.
Adds CommandPalette.test.tsx asserting both fixes. Visual proof:
docs/evidence/OMN-12402-{badges,dispatch-light}.png
* fix(OMN-12367): render Correlation Trace tab gracefully in file mode (#159)
In VITE_DATA_SOURCE=file mode the /api/delegation/* proxy is not registered
(it is wired only when VITE_PROJECTION_API_URL is set), so fetchCorrelationTrace
hit the Vite SPA fallback, received index.html, and surfaced a raw
'JSON.parse: unexpected character at line 1 column 1' error.
Detect file mode up front in fetchCorrelationTrace and read a per-correlation
fixture under fixtures/onex.snapshot.projection.delegation.correlation-trace.v1/
so the surface renders a representative event chain without postgres. In live
mode, parse defensively (res.json().catch) so any non-JSON body degrades to an
empty trace and the panel shows its graceful no-events message rather than a raw
parse error.
Adds the delegationCorrelationTrace topic, a representative 2-event fixture for
the file-mode decisions correlation (069e55b5...), and tests covering file-mode
fixture fetch and non-JSON hardening.
* fix(OMN-12399): register Context Effectiveness Heatmap widget in palette (#158)
The widget source (component, hook, fixtures, types, test) existed but was
registered in zero of the three Track A surfaces, so it never appeared in the
widget palette and was unreachable through the supported user flow.
- Add lazy import to componentImports in src/components/dashboard/index.ts
- Add context-effectiveness-heatmap manifest to MVP_COMPONENTS in
scripts/generate-registry.ts (quality category, contextExperimentScores
projection, OMN-11241 fixture fallback)
- Regenerate src/registry/component-registry.json (30 -> 31 components)
- Update integration.part2/part3 component-count + name-list assertions
Visual proof (OMN-7093): palette entry + widget rendered on dashboard in
file mode using the OMN-11241 research fixture.
* fix(OMN-12366): give Delegation Control Plane active tab a clear visual indicator (#157)
The 10-tab evidence strip distinguished active from inactive tabs only by
background (var(--panel-2) vs transparent); border, text color, and weight
were identical, so the strip did not read as tabs.
Active tab now uses the --brand accent token: brand border, a 2px brand
bottom-border underline, --brand-soft background, and brand-colored
semibold label text. All via existing design tokens (no hardcoded hex),
verified to read clearly in both light and dark themes.
Visual evidence (OMN-7093):
- docs/evidence/OMN-12366-tab-strip-dark.png
- docs/evidence/OMN-12366-tab-strip-light.png
* feat(OMN-12139): add Trace Explorer fixture for onex.snapshot.projection.traces.v1 (#161)
Wires a _trace_groups() factory into scripts/generate_fixtures.py and emits
five representative TraceGroup records under
fixtures/onex.snapshot.projection.traces.v1/, covering every TraceExplorer
UI state: multi-node completed, single-node completed, running, error, and a
longer multi-node trace. The widget reads each file as one TraceGroup via the
legacy useProjectionQuery readAll path; the file-source middleware
auto-generates index.json.
Shapes mirror the widget's TraceGroup interface exactly (no entity_id); each
record is keyed by its correlation_id so the fixture filename matches.
DoD: VITE_DATA_SOURCE=file dev server renders 5 traces (1 running, 1 error)
in the Trace Explorer in File Mode; Playwright screenshot under
docs/evidence/OMN-12139-trace-explorer-file-mode.png proves populated/running/
error states (OMN-7093). npm run build, lint, and full vitest suite (1172
tests) green.
* fix(OMN-12397): make Savings Proof Pack source line dynamic by data mode (#160)
The printable proof pack hardcoded "Source: OmniNode delegation_events
projection" even in file mode, so a PDF exported from fixture data was
misleading once removed from the parent Fixture banner. Derive the
"Source:" line and reproducibility sentence from useDataSourceMode():
file -> Local fixture file (fixtures/.../demo-delegation-snapshot.json)
sqlite -> Local SQLite projection
http -> OmniNode projection API via Express bridge
postgres -> OmniNode delegation_events projection
Non-live modes (file/sqlite) state the report is from a local
development data source and is not a live projection.
Renamed fixture live-delegation-demo.json -> demo-delegation-snapshot.json;
the word 'live' inside fixtures/ was second-order misleading. The file is
discovered by directory glob (vite fixturesMiddleware readdirSync), so no
code references the old name.
dod_evidence: rendered_output screenshot
docs/evidence/OMN-12397-proof-pack-file-mode-source.png shows the
file-mode footer with the fixture source string and 15 fixture rows.
* fix(OMN-12392): repoint run-generate-fixtures.sh to local script and correct CLAUDE.md fixture claim (#162)
OMN-12392: `run-generate-fixtures.sh` was looking for `generate_fixtures.py` at
`$OMNIBASE_INFRA_PATH/scripts/generate_fixtures.py`, but that path does not exist in
omnibase_infra. The script lives in this repo at `scripts/generate_fixtures.py`.
Fix: set SCRIPT_PATH to `$REPO/scripts/generate_fixtures.py` while preserving
`--project "$OMNIBASE_INFRA_PATH"` for uv dependency resolution (the script imports
from omnibase_infra). `npm run generate:fixtures` now works without a non-standard
OMNIBASE_INFRA_PATH.
Also fix CLAUDE.md hard rule #6 which claimed "Fixtures and layouts are not committed"
— fixtures/ IS tracked via the OMN-10305 gitignore carve-out (!fixtures/, !fixtures/**).
Correct the claim and add the carve-out explanation.
OMN-12393: add a lineage classification table to CLAUDE.md documenting that
omnidash/ (dev) is CANONICAL_ACTIVE, omnidash-v2/ is SUPERSEDED, and
omnidash-v2-new/ is a TEMP_STAGING clone. Cite verification evidence.
* test(OMN-10947): pin dashboard e2e fixture proof (#163)
* test(OMN-10947): scope dashboard e2e locators (#164)
* fix(OMN-12394): add .env to omnidash .gitignore to prevent credential leak (#165)
The .env file holds OMNIDASH_ANALYTICS_DB_URL with a live Postgres password.
The existing rule only covered .env.local* — .env itself was unprotected.
Adds an explicit .env entry so git never tracks the credential file.
* fix: move omnidash event bus config to contract overlay (#166)
* chore(OMN-12459): add universal managed gitignore block to omnidash (#168)
Appends the onex-managed universal block to .gitignore, adding
.env.*, !.env.example, *.log, .idea/, .vscode/, test-results/, and
playwright-report/ coverage. .env and .DS_Store were already present
individually; the block makes the policy explicit and machine-readable.
No tracked files required untracking.
* ci(OMN-12477): add dev-only main-target-guard (forward-port from main) (#167)
Forward-ports the main-target-guard workflow to dev (the file existed on
main but was absent on dev — main<->dev drift). Adds the dev-only variant
WITHOUT the hotfix/* bypass: main accepts only dev promotion PRs carrying
promotion-receipt: OCC-N. The guard only triggers on base==main, so it is
inert on dev-targeting PRs and takes effect at the next dev->main promotion.
OMN-12477
* feat(OMN-12674): remove reviewdog workflows and configs (#170)
Removes the reviewdog caller workflow from omnidash. No reviewdog
required-status-check bindings existed on main protection, dev
protection, or any repo ruleset, so this is the only active reviewdog
surface in the repo.
dod_evidence: OMN-12674
* fix(OMN-12739): tighten delegation dashboard formatting (#171)
* fix(OMN-12740): add browser-safe dashboard id fallback (#172)
* ci: preserve hotfix main-target allowance for release
* chore(release)(OMN-12245): bump omnidash to v1.1.1
* chore(OMN-12765): backmerge current main into dash dev lane (#183)
* feat(OMN-11614, OMN-11617): deduplicate projection readers and integration test utilities (#120)
* feat(OMN-11979): redesign delegation dashboard — dense metrics, narrative labels, stale .js cleanup (#119)
- Replace donut chart with terminal-style KPI tiles + stacked model bar + task breakdown
- Rename Quality Gate → Escalation Detail, humanize failure categories
- Fix postgres-projection-reader: correct column names, JSONB string coercion
- Add /projection Vite proxy for Express bridge
- Remove 8 stale compiled .js files that shadowed .tsx source via Vite resolution
- "Quality Gate" → "Local Success", "pass/fail" → "local/escalated" throughout
* feat(OMN-11614, OMN-11617): deduplicate projection readers and integration test utilities
OMN-11614: Extract shared session-merge and cost-savings-overview aggregation
logic from postgres-projection-reader.ts and sqlite-projection-reader.ts into
server/projection-utils.ts. Removes ~160 lines of duplicated code addressed
by the TODO(OMN-10976) comment. Fixes pre-existing test failures in
postgres-projection-reader.test.ts where string-typed token assertions
contradicted the numeric coercion the reader actually applies.
OMN-11617: Extrac…
…mniNode-ai#201) (OmniNode-ai#202) * feat(OMN-11614, OMN-11617): deduplicate projection readers and integration test utilities (#120) * feat(OMN-11979): redesign delegation dashboard — dense metrics, narrative labels, stale .js cleanup (#119) - Replace donut chart with terminal-style KPI tiles + stacked model bar + task breakdown - Rename Quality Gate → Escalation Detail, humanize failure categories - Fix postgres-projection-reader: correct column names, JSONB string coercion - Add /projection Vite proxy for Express bridge - Remove 8 stale compiled .js files that shadowed .tsx source via Vite resolution - "Quality Gate" → "Local Success", "pass/fail" → "local/escalated" throughout * feat(OMN-11614, OMN-11617): deduplicate projection readers and integration test utilities OMN-11614: Extract shared session-merge and cost-savings-overview aggregation logic from postgres-projection-reader.ts and sqlite-projection-reader.ts into server/projection-utils.ts. Removes ~160 lines of duplicated code addressed by the TODO(OMN-10976) comment. Fixes pre-existing test failures in postgres-projection-reader.test.ts where string-typed token assertions contradicted the numeric coercion the reader actually applies. OMN-11617: Extract manifest loading and QueryClient factory into src/test-utils/integrationHelpers.ts. Updates integration.part2/3/4.test.tsx to import TEST_MANIFEST and createTestQueryClient instead of duplicating the readFileSync + JSON.parse pattern and bare QueryClient constructor. 17 new unit tests for projection-utils.ts cover timestampValue, sessionKey, mergeDelegationSessions, sessionTokens, and buildCostSavingsOverview. * fix(OMN-11614, OMN-11617): update delegation widget tests to match OMN-11979 redesign Tests were written against the old design (KPI tiles with "Pass rate", "Passed", "Failed" labels; donut panel 420px wide; "QG Pass" column; "Failure categories" heading; "Recent checks" label). OMN-11979 shipped a dense terminal-style redesign that changed these rendered strings. Update assertions to match the current component output: - DelegationMetrics3D: maxWidth 420→240, canvas height 200→140 - DelegationMetrics2D: "N/M pass" → "N/M local" - DelegationModelRoutingWidget: column "QG Pass" → "Local Rate" - DelegationQualityGateWidget: empty msg, check-type labels, section headings, recent-checks label all updated to current rendered text All 1044 tests pass (0 new failures introduced). * chore: release v1.1.0 (OMN-12245) (#135) * feat(OMN-11979): redesign delegation dashboard — dense metrics, narrative labels, stale .js cleanup (#119) - Replace donut chart with terminal-style KPI tiles + stacked model bar + task breakdown - Rename Quality Gate → Escalation Detail, humanize failure categories - Fix postgres-projection-reader: correct column names, JSONB string coercion - Add /projection Vite proxy for Express bridge - Remove 8 stale compiled .js files that shadowed .tsx source via Vite resolution - "Quality Gate" → "Local Success", "pass/fail" → "local/escalated" throughout * feat(OMN-12069): delegation correlation trace view Adds the delegation correlation trace view, trigger panel, and dashboard assertion updates needed for CI. * feat(OMN-12133): add projection query endpoints for log entries and traces (#122) Adds GET /api/projections/log-entries and GET /api/projections/traces to the Express server, backed by new queryLogEntries and queryTraces methods on PostgresProjectionReader. Adds traceExplorer topic constant to shared/types/topics.ts. All query params are validated; limits are capped (1000 for entries, 500 for traces). is_running = true when last_event_at < 60s ago. 17 new unit tests + 2 route-level tests. * feat(OMN-12089): B6 delegation savings proof pack — printable audit artifact (#123) Adds DelegationSavingsProofPackPanel as a new 'Savings Proof Pack' tab in the delegation control plane evidence tabs. Renders an exportable audit report from live projection data: correlation IDs for every run, routing decisions (model, rule, confidence), quality gate results (pass/fail + detail), pricing manifest version, per-run savings, cumulative savings summary, and a pass-rate totals row. Print/Save as PDF is handled via window.print() with an injected @media print stylesheet that isolates the printable region to the proof pack table. No server-side PDF generation or new backend endpoints required — all data sourced from DelegationEvidenceSnapshot already in context. 7 tests covering empty state, header rendering, run table, quality gate labels, routing display, totals row, and print button. * feat(OMN-12085): add swarm-runs projection topic to PostgresProjectionReader (#124) Adds onex.snapshot.projection.swarm-runs.v1 case to the Postgres projection reader. Queries omnidash_analytics.swarm_runs and returns all T4 display fields: runId, status, subtask/succeeded/failed counts, modelsUsed, totalCostUsd, cloudEquivalentCostUsd, savingsUsd, parallelismSpeedupRatio, all latency fields, and createdAt. The swarm_runs table already exists on stability-test (15436) with 11 rows. * feat(OMN-12135): add TraceExplorerWidget — two-panel event bus trace explorer (#126) Adds TraceExplorerWidget to omnidash for event bus observability Phase 2. The widget consumes onex.snapshot.projection.traces.v1 to render a left-panel trace list (with running/error indicators) and right-panel chronological timeline of log entries for the selected trace. - TraceExplorerWidget.tsx: two-panel layout, trace cards, timeline, search - TraceExplorerWidget.stories.tsx: Empty, Populated, WithRunningTrace, ErrorTraces, Loading - TraceExplorerWidget.test.tsx: 5 unit tests (loading, empty, render, filter, select prompt) - storybook/fixtures/trace-explorer.ts: buildTraceGroups fixture factory - generate-registry.ts: trace-explorer manifest with projectionSchema + displayContract - index.ts: lazy import wired under trace-explorer/TraceExplorerWidget - integration.part2/3: component count bumped 28→29 with trace-explorer Closes OMN-12135. References OMN-12136, OMN-12137, OMN-12138, OMN-12139. * feat(OMN-12082): add Context Effectiveness Heatmap widget (B4) (#127) Implements the context factor effectiveness visualization showing which context segments help/hurt which models across A3 ablation experiments: - Segment × model matrix with color-coded cells: green (≥75% pass rate), amber (26–74%), red (≤25%) - Clickable cells open an examples panel showing specific token impact, pass/fail counts, and qualitative findings per segment×model combination - Fixture mode: when no live data exists, shows OMN-11241 research results (golden_chain green across all models, claude_md/architecture_patterns red for local models, exemplar green for exact-interface tasks, local_failures neutral) - Fixture badge clearly marks when live projection is absent - Reads from context_experiment_scores table via new onex.snapshot.projection.context.experiment-scores.v1 topic - Adds postgres reader case for context_experiment_scores SELECT - 10 tests passing (empty state, matrix render, cell selection, fixture badge, toggle deselect, footer count) * feat(OMN-12145): add Express dispatch bridge and Kafka command topic (#128) - POST /api/dispatch validates body, builds envelope, publishes to onex.cmd.omnidash.dispatch-request.v1 via kafkajs producer - Returns 400 on missing fields, 503 when broker unavailable - Kafka producer connects on server start, graceful disconnect on SIGTERM/SIGINT - COMMAND_TOPICS registry in shared/types/command-topics.ts (separate from TOPICS to preserve golden-chain onex.snapshot.projection.* invariant) - 9 unit tests with mocked producer covering validation, 503, and happy path Closes OMN-12145, OMN-12149 * feat(OMN-12147): add command dispatch UI — trace action buttons + Cmd+K palette (#129) - New `command-dispatch/` directory with: - `useDispatch.ts` hook: POST /api/dispatch, availability probe, error/loading state - `DispatchButton.tsx`: icon button (RotateCw/Square) with loading/success/error states - `CommandPalette.tsx`: Cmd+K/Ctrl+K overlay with node search, JSON payload editor, dispatch result; includes `useCommandPalette` hook for global keyboard listener - `CommandPalette.stories.tsx`: Open, WithResults, Dispatching, Error story exports - `TraceExplorerWidget.tsx`: adds Stop button on running traces, Re-run button on completed traces - `App.tsx`: mounts `CommandPalette` at app root via `useCommandPalette` hook Closes OMN-12147, relates OMN-12148 * fix(OMN-12149): align dispatch command topic to omnimarket namespace (#131) The node_dispatch_request_handler subscribes to onex.cmd.omnimarket.dispatch-request.v1 (topic naming lint requires consumer service namespace). Aligns the Express bridge publisher topic. * chore(OMN-12171): delete orphaned .js files and add CI enforcement gate (#132) * chore(OMN-12171): delete orphaned .js files and add CI enforcement gate Confirms 298 .js artifacts in src/ and shared/ are stale (all have .ts/.tsx counterparts; tsconfig emits nothing to src/; build output goes to dist/). Deletes them from working tree. Adds scripts/check-no-orphaned-js.sh — fails if any .js file in src/ or shared/ shadows a .ts/.tsx source — and wires it as a CI gate in ci.yml. Also fixes broken @storybook/test import in CommandPalette.stories.tsx (package moved to storybook/test in v10), restoring tsc --noEmit clean pass. * fix(OMN-12171): include command topic in server typecheck * refactor(OMN-12187): Consolidate DRY violations in omnidash * refactor(OMN-12187): Consolidate DRY violations in omnidash Extract shared pure-data helpers (timestampValue, mergeDelegationSessions, buildCostSavingsOverviewResult) into server/projection-reader-shared.ts. Both sqlite-projection-reader.ts and postgres-projection-reader.ts delegate to the shared module, eliminating ~120 lines of duplicated logic. Add 13 unit tests for the shared module covering edge cases in timestamp parsing, session merge semantics, and cost overview aggregation. The integration test files (part2/part3/part4) are distinct suites with different test content — they are not duplicates and are left intact. * fix(OMN-12187): remove unused cost overview input * chore: release v1.1.0 * ci: re-trigger CI for release promotion PR * feat(OMN-6877): add 4 observability pages — trace explorer, session replay, sandbox monitor, voice sessions (#136) - TraceExplorerPage: wraps existing TraceExplorerWidget as a full-page view - SessionReplayPage: step/play/rewind through recorded sessions with state reconstruction and progress bar - SandboxMonitorPage: real-time allow/deny stream, deny-rate bar, per-policy breakdown table - VoiceSessionPage: audio timeline, transcript overlay, turn-taking visualization - Extends AppPage type and App.tsx routing switch for all 4 new pages - Adds Observability section to Sidebar with GitBranch/RotateCcw/Shield/Mic icons - Adds sandboxDecisions, voiceSessions, sessionReplay topics to TOPICS constant * ci(OMN-12243): add main target guard (#138) * release: promote omnidash dev to main (2026-06-06) (#174) * feat(OMN-11979): redesign delegation dashboard — dense metrics, narrative labels, stale .js cleanup (#119) - Replace donut chart with terminal-style KPI tiles + stacked model bar + task breakdown - Rename Quality Gate → Escalation Detail, humanize failure categories - Fix postgres-projection-reader: correct column names, JSONB string coercion - Add /projection Vite proxy for Express bridge - Remove 8 stale compiled .js files that shadowed .tsx source via Vite resolution - "Quality Gate" → "Local Success", "pass/fail" → "local/escalated" throughout * feat(OMN-12069): delegation correlation trace view Adds the delegation correlation trace view, trigger panel, and dashboard assertion updates needed for CI. * feat(OMN-12133): add projection query endpoints for log entries and traces (#122) Adds GET /api/projections/log-entries and GET /api/projections/traces to the Express server, backed by new queryLogEntries and queryTraces methods on PostgresProjectionReader. Adds traceExplorer topic constant to shared/types/topics.ts. All query params are validated; limits are capped (1000 for entries, 500 for traces). is_running = true when last_event_at < 60s ago. 17 new unit tests + 2 route-level tests. * feat(OMN-12089): B6 delegation savings proof pack — printable audit artifact (#123) Adds DelegationSavingsProofPackPanel as a new 'Savings Proof Pack' tab in the delegation control plane evidence tabs. Renders an exportable audit report from live projection data: correlation IDs for every run, routing decisions (model, rule, confidence), quality gate results (pass/fail + detail), pricing manifest version, per-run savings, cumulative savings summary, and a pass-rate totals row. Print/Save as PDF is handled via window.print() with an injected @media print stylesheet that isolates the printable region to the proof pack table. No server-side PDF generation or new backend endpoints required — all data sourced from DelegationEvidenceSnapshot already in context. 7 tests covering empty state, header rendering, run table, quality gate labels, routing display, totals row, and print button. * feat(OMN-12085): add swarm-runs projection topic to PostgresProjectionReader (#124) Adds onex.snapshot.projection.swarm-runs.v1 case to the Postgres projection reader. Queries omnidash_analytics.swarm_runs and returns all T4 display fields: runId, status, subtask/succeeded/failed counts, modelsUsed, totalCostUsd, cloudEquivalentCostUsd, savingsUsd, parallelismSpeedupRatio, all latency fields, and createdAt. The swarm_runs table already exists on stability-test (15436) with 11 rows. * feat(OMN-12135): add TraceExplorerWidget — two-panel event bus trace explorer (#126) Adds TraceExplorerWidget to omnidash for event bus observability Phase 2. The widget consumes onex.snapshot.projection.traces.v1 to render a left-panel trace list (with running/error indicators) and right-panel chronological timeline of log entries for the selected trace. - TraceExplorerWidget.tsx: two-panel layout, trace cards, timeline, search - TraceExplorerWidget.stories.tsx: Empty, Populated, WithRunningTrace, ErrorTraces, Loading - TraceExplorerWidget.test.tsx: 5 unit tests (loading, empty, render, filter, select prompt) - storybook/fixtures/trace-explorer.ts: buildTraceGroups fixture factory - generate-registry.ts: trace-explorer manifest with projectionSchema + displayContract - index.ts: lazy import wired under trace-explorer/TraceExplorerWidget - integration.part2/3: component count bumped 28→29 with trace-explorer Closes OMN-12135. References OMN-12136, OMN-12137, OMN-12138, OMN-12139. * feat(OMN-12082): add Context Effectiveness Heatmap widget (B4) (#127) Implements the context factor effectiveness visualization showing which context segments help/hurt which models across A3 ablation experiments: - Segment × model matrix with color-coded cells: green (≥75% pass rate), amber (26–74%), red (≤25%) - Clickable cells open an examples panel showing specific token impact, pass/fail counts, and qualitative findings per segment×model combination - Fixture mode: when no live data exists, shows OMN-11241 research results (golden_chain green across all models, claude_md/architecture_patterns red for local models, exemplar green for exact-interface tasks, local_failures neutral) - Fixture badge clearly marks when live projection is absent - Reads from context_experiment_scores table via new onex.snapshot.projection.context.experiment-scores.v1 topic - Adds postgres reader case for context_experiment_scores SELECT - 10 tests passing (empty state, matrix render, cell selection, fixture badge, toggle deselect, footer count) * feat(OMN-12145): add Express dispatch bridge and Kafka command topic (#128) - POST /api/dispatch validates body, builds envelope, publishes to onex.cmd.omnidash.dispatch-request.v1 via kafkajs producer - Returns 400 on missing fields, 503 when broker unavailable - Kafka producer connects on server start, graceful disconnect on SIGTERM/SIGINT - COMMAND_TOPICS registry in shared/types/command-topics.ts (separate from TOPICS to preserve golden-chain onex.snapshot.projection.* invariant) - 9 unit tests with mocked producer covering validation, 503, and happy path Closes OMN-12145, OMN-12149 * feat(OMN-12147): add command dispatch UI — trace action buttons + Cmd+K palette (#129) - New `command-dispatch/` directory with: - `useDispatch.ts` hook: POST /api/dispatch, availability probe, error/loading state - `DispatchButton.tsx`: icon button (RotateCw/Square) with loading/success/error states - `CommandPalette.tsx`: Cmd+K/Ctrl+K overlay with node search, JSON payload editor, dispatch result; includes `useCommandPalette` hook for global keyboard listener - `CommandPalette.stories.tsx`: Open, WithResults, Dispatching, Error story exports - `TraceExplorerWidget.tsx`: adds Stop button on running traces, Re-run button on completed traces - `App.tsx`: mounts `CommandPalette` at app root via `useCommandPalette` hook Closes OMN-12147, relates OMN-12148 * fix(OMN-12149): align dispatch command topic to omnimarket namespace (#131) The node_dispatch_request_handler subscribes to onex.cmd.omnimarket.dispatch-request.v1 (topic naming lint requires consumer service namespace). Aligns the Express bridge publisher topic. * chore(OMN-12171): delete orphaned .js files and add CI enforcement gate (#132) * chore(OMN-12171): delete orphaned .js files and add CI enforcement gate Confirms 298 .js artifacts in src/ and shared/ are stale (all have .ts/.tsx counterparts; tsconfig emits nothing to src/; build output goes to dist/). Deletes them from working tree. Adds scripts/check-no-orphaned-js.sh — fails if any .js file in src/ or shared/ shadows a .ts/.tsx source — and wires it as a CI gate in ci.yml. Also fixes broken @storybook/test import in CommandPalette.stories.tsx (package moved to storybook/test in v10), restoring tsc --noEmit clean pass. * fix(OMN-12171): include command topic in server typecheck * refactor(OMN-12187): Consolidate DRY violations in omnidash * refactor(OMN-12187): Consolidate DRY violations in omnidash Extract shared pure-data helpers (timestampValue, mergeDelegationSessions, buildCostSavingsOverviewResult) into server/projection-reader-shared.ts. Both sqlite-projection-reader.ts and postgres-projection-reader.ts delegate to the shared module, eliminating ~120 lines of duplicated logic. Add 13 unit tests for the shared module covering edge cases in timestamp parsing, session merge semantics, and cost overview aggregation. The integration test files (part2/part3/part4) are distinct suites with different test content — they are not duplicates and are left intact. * fix(OMN-12187): remove unused cost overview input * feat(OMN-12072): Swarm Control Plane widget — decomposition tree, wave view, live counters, savings bar (#125) * feat(OMN-12072): add Swarm Control Plane widget (B2-T1 through B2-T5) Implements the full swarm progression UI against the swarm_runs projection: - B2-T1: SwarmDecompositionTree — parent→subtask tree with aggregate-derived per-subtask statuses and model hints; graceful empty state when subtask_count=0 - B2-T2: SwarmSubtaskCard — colored status badge, 3×3 metrics grid, models-used pill tags, registry hash footer - B2-T3: SwarmWaveVisualization — heuristic 60/40 wave split with dependency connector; Wave 1 dimmed until run complete; empty state when subtask_count=0 - B2-T4: SwarmLiveCounters — 6 aggregate cards (total runs, subtasks, avg speedup, wall time, cost, savings) derived from swarm_runs projection - B2-T5: SwarmSavingsBar — stacked local+savings bar vs cloud equivalent baseline, per-run mini bars (up to 10), zero-cost graceful empty state - SwarmRunTable: 8-column run list with row selection, speedup/savings coloring - SwarmControlPlane: tabbed widget (Overview, Decomposition, Run Detail, Wave View, Savings) wired to swarm.runs.v1 projection topic via useProjectionQuery - Adds TOPICS.swarmRuns, swarm-runs.v1 postgres case, GET /api/swarm-runs route - 10 tests passing * feat(OMN-12072): add SwarmSubtaskDetailPanel — per-subtask model/tokens/cost/latency table (B5-T3 gap) Adds SwarmSubtaskDetailPanel showing per-subtask rows with: - subtask_id (derived: run_id prefix + padded index) - description (Subtask 01, 02, ...) - model assigned (cycled from run.models_used) - status (succeeded/failed/skipped/pending, derived from aggregate counts) - tokens_input / tokens_output (null — requires node_swarm_subtask_state_reducer) - cost_usd (evenly distributed from total_cost_usd) - latency_ms (estimated from dispatch_wall_latency_ms / parallelism) Panel is clearly marked as aggregate-derived with a fixture notice. Shows "No subtask data" with reducer name when subtask_count=0. Wired as new "Subtask Detail" tab in SwarmControlPlane alongside existing "Run Detail" aggregate card. Uses List icon from lucide-react. Schema confirmed: swarm_runs has no jsonb subtask column and no separate swarm_subtasks table exists yet in stability-test. All per-subtask data requires node_swarm_subtask_state_reducer projection. 15 new tests + 10 existing passing (25 total). * refactor(OMN-11578/OMN-11596): reduce cyclomatic complexity in ThreeCanvas and parseDashboardDefinition (#137) OMN-11578: Extract advanceFocusProgress, applyFocusedOpacities, applyIdleOpacities, and projectAxisLabels from the ThreeCanvas useEffect god-function (CCN=83). The tick loop is now a thin orchestrator calling four named helpers via a TickRefs context struct. OMN-11596: Extract validateTopLevelFields and validateLayoutItem from parseDashboardDefinition (CCN=31). The parse function is reduced to an 8-line orchestrator; all conditional branches live in the helpers. * fix(OMN-12245): silence unused swarm route request (#139) * feat(OMN-11658): wire SEA demo dashboard projections (#140) * feat(OMN-11658): wire SEA demo dashboard projections * fix(OMN-11658): make trace rows toggle independently * fix(OMN-11658): polish SEA dashboard rendering * feat(OMN-11658): expose delegation trigger in SEA control plane (#141) * fix(OMN-11658): improve SEA prompt feedback (#142) * fix(OMN-11658): let dashboard fill wide viewports (#143) * fix(OMN-11658): balance dashboard gutters (#144) * fix(OMN-11658): keep SEA stream newest first (#145) * fix(OMN-11658): refresh SEA traces after prompt submit (#146) * fix(OMN-11658): center dashboard shell (#147) * fix(OMN-11658): extend dashboard body to bottom (#148) * fix(OMN-11658): repair empty SEA dashboard layout (#149) * chore(DELEG-002): annotate pricing fallbacks, align fixture model names with bifrost config (#150) * feat(OMN-12070): wire delegation trigger through omnimarket runtime (#151) - Add COMMAND_TOPICS.delegateSkill ('onex.cmd.omnimarket.delegate-skill.v1') to shared/types/command-topics.ts so the topic is contract-declared rather than hardcoded in server/routes.ts - Declare the delegation trigger command topic in contract.yaml with handler node, ingress path, payload shape, and OMNIDASH_RUNTIME_EFFECTS_URL env note - server/routes.ts uses COMMAND_TOPICS.delegateSkill constant; runtime targets stability-test :18085/v1/effects via OMNIDASH_RUNTIME_EFFECTS_URL env var - DelegationRunContext exposes pendingCorrelationId + setPendingCorrelationId so a freshly-dispatched correlation_id propagates to all child panels before projection rows materialize - DelegationRunHeader wires onCorrelationId callback to DelegationTriggerPanel - DelegationEvidenceTabs auto-navigates to correlation-trace tab on dispatch and displays a pending indicator with the correlation_id - DelegationCorrelationTracePanel falls back to pendingCorrelationId when no run is selected and shows appropriate pending vs awaiting-events messages - Test: setPendingCorrelationId updates context value (DelegationRunContext.test) * fix(OMN-12243): add 'delegation' short-topic alias to postgres + sqlite projection readers (#152) GET /projection/delegation was returning rows:[] because the query() switch had no 'delegation' case — only the full versioned topic name. Adds a fall-through alias in both PostgresProjectionReader and SqliteProjectionReader so the short topic name routes to the same decisions.v1 query logic. Test: new routes.test.ts case seeds a SQLite DB and asserts the short alias returns the same rows as the full topic name. * fix(OMN-12287): close fixture gaps and add file-mode banners to dashboard widgets (#153) - Add fileMode prop to ComponentWrapper: renders a [File Mode] badge in the widget header when active, analogous to the [Live] badge, using Text component to comply with the no-typography-inline ESLint rule - Wire fileMode={!isLiveDataSource(mode)} on all 13 widgets that use useDataSourceMode: TraceExplorer, LiveEventStream, ContextHeatmap, DelegationControlPlane, EventStream, ReadinessGate, DelegationMetrics2D, DelegationSavingsWidget, DelegationTokenUsageWidget, DelegationModelRoutingWidget, DelegationQualityGateWidget, AbCompareWidget - Improve emptyHint messages on all updated widgets to include file-mode fixture directory paths so developers know exactly where to add test data - 1124/1124 tests pass * feat(OMN-12288): deep-link delegation runs to trace explorer (#154) - Add traceFilter + setTraceFilter to UISlice so a correlation_id can be passed to TraceExplorer before navigating there (no URL routing required) - DelegationRunTable adds a GitCommitHorizontal icon button per row; clicking it calls setTraceFilter(correlationId) + setActivePage('trace') and stops event propagation so the row-select handler is not also triggered - TraceExplorerWidget initializes its query state from traceFilter on mount and immediately clears the store value so subsequent navigations start fresh - Tests: DelegationRunTable.test.tsx (7 cases), TraceExplorerWidget deep-link test confirms filter pre-fill + store clear (1132 total tests pass) * feat(T22): add DelegationCostComparisonWidget — per-run cost proof surface (#155) - New widget DelegationCostComparisonWidget in delegation/ directory reads from TOPICS.delegationSavings (same source as DelegationSavingsWidget) and surfaces a per-session cost comparison table with columns: Session, Task, Model, Tokens, Latency, Local $, Cloud $, Saved, Provenance - Provenance badges (measured/estimated) on each row per the § 1.3 spec — green for measured (llm_call_metrics), amber for estimated (routing-rule inferred) - Summary KPI bar shows cumulative savings, local/cloud cost, session count, total tokens, measured vs estimated breakdown - Sort controls: date (default), savings descending, tokens descending - maxRows config cap with overflow notice; showProvenance=false hides source column - Registered as 'delegation-cost-comparison' in generate-registry.ts and index.ts - Integration tests updated for count 29 → 30 - 1132/1132 tests pass * fix(OMN-12402): unify CommandPalette node-type badges and Dispatch button contrast (#156) Issue 1: kindColor() mixed design tokens (var(--accent), var(--text-tertiary)) with hardcoded hex (#d97706, #7c3aed), so ORCHESTRATOR/COMPUTE badges rendered as weak plain text while EFFECT/REDUCER read as vivid chips. Drive all four badges from the design-system node-type role tokens (--<kind>{,-soft,-ink}), which exist for both themes, so every badge is a consistent filled chip. Issue 2: the Dispatch button used var(--accent) as its fill (which resolves to --brand-soft, a pale hover tint) plus literal color:'white' text — invisible in light mode. Use the brand fill + on-brand text token (--brand / --primary-foreground) so the CTA stays legible in both themes. Adds CommandPalette.test.tsx asserting both fixes. Visual proof: docs/evidence/OMN-12402-{badges,dispatch-light}.png * fix(OMN-12367): render Correlation Trace tab gracefully in file mode (#159) In VITE_DATA_SOURCE=file mode the /api/delegation/* proxy is not registered (it is wired only when VITE_PROJECTION_API_URL is set), so fetchCorrelationTrace hit the Vite SPA fallback, received index.html, and surfaced a raw 'JSON.parse: unexpected character at line 1 column 1' error. Detect file mode up front in fetchCorrelationTrace and read a per-correlation fixture under fixtures/onex.snapshot.projection.delegation.correlation-trace.v1/ so the surface renders a representative event chain without postgres. In live mode, parse defensively (res.json().catch) so any non-JSON body degrades to an empty trace and the panel shows its graceful no-events message rather than a raw parse error. Adds the delegationCorrelationTrace topic, a representative 2-event fixture for the file-mode decisions correlation (069e55b5...), and tests covering file-mode fixture fetch and non-JSON hardening. * fix(OMN-12399): register Context Effectiveness Heatmap widget in palette (#158) The widget source (component, hook, fixtures, types, test) existed but was registered in zero of the three Track A surfaces, so it never appeared in the widget palette and was unreachable through the supported user flow. - Add lazy import to componentImports in src/components/dashboard/index.ts - Add context-effectiveness-heatmap manifest to MVP_COMPONENTS in scripts/generate-registry.ts (quality category, contextExperimentScores projection, OMN-11241 fixture fallback) - Regenerate src/registry/component-registry.json (30 -> 31 components) - Update integration.part2/part3 component-count + name-list assertions Visual proof (OMN-7093): palette entry + widget rendered on dashboard in file mode using the OMN-11241 research fixture. * fix(OMN-12366): give Delegation Control Plane active tab a clear visual indicator (#157) The 10-tab evidence strip distinguished active from inactive tabs only by background (var(--panel-2) vs transparent); border, text color, and weight were identical, so the strip did not read as tabs. Active tab now uses the --brand accent token: brand border, a 2px brand bottom-border underline, --brand-soft background, and brand-colored semibold label text. All via existing design tokens (no hardcoded hex), verified to read clearly in both light and dark themes. Visual evidence (OMN-7093): - docs/evidence/OMN-12366-tab-strip-dark.png - docs/evidence/OMN-12366-tab-strip-light.png * feat(OMN-12139): add Trace Explorer fixture for onex.snapshot.projection.traces.v1 (#161) Wires a _trace_groups() factory into scripts/generate_fixtures.py and emits five representative TraceGroup records under fixtures/onex.snapshot.projection.traces.v1/, covering every TraceExplorer UI state: multi-node completed, single-node completed, running, error, and a longer multi-node trace. The widget reads each file as one TraceGroup via the legacy useProjectionQuery readAll path; the file-source middleware auto-generates index.json. Shapes mirror the widget's TraceGroup interface exactly (no entity_id); each record is keyed by its correlation_id so the fixture filename matches. DoD: VITE_DATA_SOURCE=file dev server renders 5 traces (1 running, 1 error) in the Trace Explorer in File Mode; Playwright screenshot under docs/evidence/OMN-12139-trace-explorer-file-mode.png proves populated/running/ error states (OMN-7093). npm run build, lint, and full vitest suite (1172 tests) green. * fix(OMN-12397): make Savings Proof Pack source line dynamic by data mode (#160) The printable proof pack hardcoded "Source: OmniNode delegation_events projection" even in file mode, so a PDF exported from fixture data was misleading once removed from the parent Fixture banner. Derive the "Source:" line and reproducibility sentence from useDataSourceMode(): file -> Local fixture file (fixtures/.../demo-delegation-snapshot.json) sqlite -> Local SQLite projection http -> OmniNode projection API via Express bridge postgres -> OmniNode delegation_events projection Non-live modes (file/sqlite) state the report is from a local development data source and is not a live projection. Renamed fixture live-delegation-demo.json -> demo-delegation-snapshot.json; the word 'live' inside fixtures/ was second-order misleading. The file is discovered by directory glob (vite fixturesMiddleware readdirSync), so no code references the old name. dod_evidence: rendered_output screenshot docs/evidence/OMN-12397-proof-pack-file-mode-source.png shows the file-mode footer with the fixture source string and 15 fixture rows. * fix(OMN-12392): repoint run-generate-fixtures.sh to local script and correct CLAUDE.md fixture claim (#162) OMN-12392: `run-generate-fixtures.sh` was looking for `generate_fixtures.py` at `$OMNIBASE_INFRA_PATH/scripts/generate_fixtures.py`, but that path does not exist in omnibase_infra. The script lives in this repo at `scripts/generate_fixtures.py`. Fix: set SCRIPT_PATH to `$REPO/scripts/generate_fixtures.py` while preserving `--project "$OMNIBASE_INFRA_PATH"` for uv dependency resolution (the script imports from omnibase_infra). `npm run generate:fixtures` now works without a non-standard OMNIBASE_INFRA_PATH. Also fix CLAUDE.md hard rule #6 which claimed "Fixtures and layouts are not committed" — fixtures/ IS tracked via the OMN-10305 gitignore carve-out (!fixtures/, !fixtures/**). Correct the claim and add the carve-out explanation. OMN-12393: add a lineage classification table to CLAUDE.md documenting that omnidash/ (dev) is CANONICAL_ACTIVE, omnidash-v2/ is SUPERSEDED, and omnidash-v2-new/ is a TEMP_STAGING clone. Cite verification evidence. * test(OMN-10947): pin dashboard e2e fixture proof (#163) * test(OMN-10947): scope dashboard e2e locators (#164) * fix(OMN-12394): add .env to omnidash .gitignore to prevent credential leak (#165) The .env file holds OMNIDASH_ANALYTICS_DB_URL with a live Postgres password. The existing rule only covered .env.local* — .env itself was unprotected. Adds an explicit .env entry so git never tracks the credential file. * fix: move omnidash event bus config to contract overlay (#166) * chore(OMN-12459): add universal managed gitignore block to omnidash (#168) Appends the onex-managed universal block to .gitignore, adding .env.*, !.env.example, *.log, .idea/, .vscode/, test-results/, and playwright-report/ coverage. .env and .DS_Store were already present individually; the block makes the policy explicit and machine-readable. No tracked files required untracking. * ci(OMN-12477): add dev-only main-target-guard (forward-port from main) (#167) Forward-ports the main-target-guard workflow to dev (the file existed on main but was absent on dev — main<->dev drift). Adds the dev-only variant WITHOUT the hotfix/* bypass: main accepts only dev promotion PRs carrying promotion-receipt: OCC-N. The guard only triggers on base==main, so it is inert on dev-targeting PRs and takes effect at the next dev->main promotion. OMN-12477 * feat(OMN-12674): remove reviewdog workflows and configs (#170) Removes the reviewdog caller workflow from omnidash. No reviewdog required-status-check bindings existed on main protection, dev protection, or any repo ruleset, so this is the only active reviewdog surface in the repo. dod_evidence: OMN-12674 * fix(OMN-12739): tighten delegation dashboard formatting (#171) * fix(OMN-12740): add browser-safe dashboard id fallback (#172) * ci: preserve hotfix main-target allowance for release * chore(release)(OMN-12245): bump omnidash to v1.1.1 * release(OMN-12765): promote dash dev lane to main (#185) * chore(release)(OMN-12765): bump omnidash to v1.1.2 (#187) * chore(release)(OMN-12765): bump omnidash to v1.1.2 * chore(release)(OMN-12765): refresh hotfix release gates * chore(release)(OMN-12765): promote dev integration to main (#190) * feat(OMN-11979): redesign delegation dashboard — dense metrics, narrative labels, stale .js cleanup (#119) - Replace donut chart with terminal-style KPI tiles + stacked model bar + task breakdown - Rename Quality Gate → Escalation Detail, humanize failure categories - Fix postgres-projection-reader: correct column names, JSONB string coercion - Add /projection Vite proxy for Express bridge - Remove 8 stale compiled .js files that shadowed .tsx source via Vite resolution - "Quality Gate" → "Local Success", "pass/fail" → "local/escalated" throughout * feat(OMN-12069): delegation correlation trace view Adds the delegation correlation trace view, trigger panel, and dashboard assertion updates needed for CI. * feat(OMN-12133): add projection query endpoints for log entries and traces (#122) Adds GET /api/projections/log-entries and GET /api/projections/traces to the Express server, backed by new queryLogEntries and queryTraces methods on PostgresProjectionReader. Adds traceExplorer topic constant to shared/types/topics.ts. All query params are validated; limits are capped (1000 for entries, 500 for traces). is_running = true when last_event_at < 60s ago. 17 new unit tests + 2 route-level tests. * feat(OMN-12089): B6 delegation savings proof pack — printable audit artifact (#123) Adds DelegationSavingsProofPackPanel as a new 'Savings Proof Pack' tab in the delegation control plane evidence tabs. Renders an exportable audit report from live projection data: correlation IDs for every run, routing decisions (model, rule, confidence), quality gate results (pass/fail + detail), pricing manifest version, per-run savings, cumulative savings summary, and a pass-rate totals row. Print/Save as PDF is handled via window.print() with an injected @media print stylesheet that isolates the printable region to the proof pack table. No server-side PDF generation or new backend endpoints required — all data sourced from DelegationEvidenceSnapshot already in context. 7 tests covering empty state, header rendering, run table, quality gate labels, routing display, totals row, and print button. * feat(OMN-12085): add swarm-runs projection topic to PostgresProjectionReader (#124) Adds onex.snapshot.projection.swarm-runs.v1 case to the Postgres projection reader. Queries omnidash_analytics.swarm_runs and returns all T4 display fields: runId, status, subtask/succeeded/failed counts, modelsUsed, totalCostUsd, cloudEquivalentCostUsd, savingsUsd, parallelismSpeedupRatio, all latency fields, and createdAt. The swarm_runs table already exists on stability-test (15436) with 11 rows. * feat(OMN-12135): add TraceExplorerWidget — two-panel event bus trace explorer (#126) Adds TraceExplorerWidget to omnidash for event bus observability Phase 2. The widget consumes onex.snapshot.projection.traces.v1 to render a left-panel trace list (with running/error indicators) and right-panel chronological timeline of log entries for the selected trace. - TraceExplorerWidget.tsx: two-panel layout, trace cards, timeline, search - TraceExplorerWidget.stories.tsx: Empty, Populated, WithRunningTrace, ErrorTraces, Loading - TraceExplorerWidget.test.tsx: 5 unit tests (loading, empty, render, filter, select prompt) - storybook/fixtures/trace-explorer.ts: buildTraceGroups fixture factory - generate-registry.ts: trace-explorer manifest with projectionSchema + displayContract - index.ts: lazy import wired under trace-explorer/TraceExplorerWidget - integration.part2/3: component count bumped 28→29 with trace-explorer Closes OMN-12135. References OMN-12136, OMN-12137, OMN-12138, OMN-12139. * feat(OMN-12082): add Context Effectiveness Heatmap widget (B4) (#127) Implements the context factor effectiveness visualization showing which context segments help/hurt which models across A3 ablation experiments: - Segment × model matrix with color-coded cells: green (≥75% pass rate), amber (26–74%), red (≤25%) - Clickable cells open an examples panel showing specific token impact, pass/fail counts, and qualitative findings per segment×model combination - Fixture mode: when no live data exists, shows OMN-11241 research results (golden_chain green across all models, claude_md/architecture_patterns red for local models, exemplar green for exact-interface tasks, local_failures neutral) - Fixture badge clearly marks when live projection is absent - Reads from context_experiment_scores table via new onex.snapshot.projection.context.experiment-scores.v1 topic - Adds postgres reader case for context_experiment_scores SELECT - 10 tests passing (empty state, matrix render, cell selection, fixture badge, toggle deselect, footer count) * feat(OMN-12145): add Express dispatch bridge and Kafka command topic (#128) - POST /api/dispatch validates body, builds envelope, publishes to onex.cmd.omnidash.dispatch-request.v1 via kafkajs producer - Returns 400 on missing fields, 503 when broker unavailable - Kafka producer connects on server start, graceful disconnect on SIGTERM/SIGINT - COMMAND_TOPICS registry in shared/types/command-topics.ts (separate from TOPICS to preserve golden-chain onex.snapshot.projection.* invariant) - 9 unit tests with mocked producer covering validation, 503, and happy path Closes OMN-12145, OMN-12149 * feat(OMN-12147): add command dispatch UI — trace action buttons + Cmd+K palette (#129) - New `command-dispatch/` directory with: - `useDispatch.ts` hook: POST /api/dispatch, availability probe, error/loading state - `DispatchButton.tsx`: icon button (RotateCw/Square) with loading/success/error states - `CommandPalette.tsx`: Cmd+K/Ctrl+K overlay with node search, JSON payload editor, dispatch result; includes `useCommandPalette` hook for global keyboard listener - `CommandPalette.stories.tsx`: Open, WithResults, Dispatching, Error story exports - `TraceExplorerWidget.tsx`: adds Stop button on running traces, Re-run button on completed traces - `App.tsx`: mounts `CommandPalette` at app root via `useCommandPalette` hook Closes OMN-12147, relates OMN-12148 * fix(OMN-12149): align dispatch command topic to omnimarket namespace (#131) The node_dispatch_request_handler subscribes to onex.cmd.omnimarket.dispatch-request.v1 (topic naming lint requires consumer service namespace). Aligns the Express bridge publisher topic. * chore(OMN-12171): delete orphaned .js files and add CI enforcement gate (#132) * chore(OMN-12171): delete orphaned .js files and add CI enforcement gate Confirms 298 .js artifacts in src/ and shared/ are stale (all have .ts/.tsx counterparts; tsconfig emits nothing to src/; build output goes to dist/). Deletes them from working tree. Adds scripts/check-no-orphaned-js.sh — fails if any .js file in src/ or shared/ shadows a .ts/.tsx source — and wires it as a CI gate in ci.yml. Also fixes broken @storybook/test import in CommandPalette.stories.tsx (package moved to storybook/test in v10), restoring tsc --noEmit clean pass. * fix(OMN-12171): include command topic in server typecheck * refactor(OMN-12187): Consolidate DRY violations in omnidash * refactor(OMN-12187): Consolidate DRY violations in omnidash Extract shared pure-data helpers (timestampValue, mergeDelegationSessions, buildCostSavingsOverviewResult) into server/projection-reader-shared.ts. Both sqlite-projection-reader.ts and postgres-projection-reader.ts delegate to the shared module, eliminating ~120 lines of duplicated logic. Add 13 unit tests for the shared module covering edge cases in timestamp parsing, session merge semantics, and cost overview aggregation. The integration test files (part2/part3/part4) are distinct suites with different test content — they are not duplicates and are left intact. * fix(OMN-12187): remove unused cost overview input * feat(OMN-12072): Swarm Control Plane widget — decomposition tree, wave view, live counters, savings bar (#125) * feat(OMN-12072): add Swarm Control Plane widget (B2-T1 through B2-T5) Implements the full swarm progression UI against the swarm_runs projection: - B2-T1: SwarmDecompositionTree — parent→subtask tree with aggregate-derived per-subtask statuses and model hints; graceful empty state when subtask_count=0 - B2-T2: SwarmSubtaskCard — colored status badge, 3×3 metrics grid, models-used pill tags, registry hash footer - B2-T3: SwarmWaveVisualization — heuristic 60/40 wave split with dependency connector; Wave 1 dimmed until run complete; empty state when subtask_count=0 - B2-T4: SwarmLiveCounters — 6 aggregate cards (total runs, subtasks, avg speedup, wall time, cost, savings) derived from swarm_runs projection - B2-T5: SwarmSavingsBar — stacked local+savings bar vs cloud equivalent baseline, per-run mini bars (up to 10), zero-cost graceful empty state - SwarmRunTable: 8-column run list with row selection, speedup/savings coloring - SwarmControlPlane: tabbed widget (Overview, Decomposition, Run Detail, Wave View, Savings) wired to swarm.runs.v1 projection topic via useProjectionQuery - Adds TOPICS.swarmRuns, swarm-runs.v1 postgres case, GET /api/swarm-runs route - 10 tests passing * feat(OMN-12072): add SwarmSubtaskDetailPanel — per-subtask model/tokens/cost/latency table (B5-T3 gap) Adds SwarmSubtaskDetailPanel showing per-subtask rows with: - subtask_id (derived: run_id prefix + padded index) - description (Subtask 01, 02, ...) - model assigned (cycled from run.models_used) - status (succeeded/failed/skipped/pending, derived from aggregate counts) - tokens_input / tokens_output (null — requires node_swarm_subtask_state_reducer) - cost_usd (evenly distributed from total_cost_usd) - latency_ms (estimated from dispatch_wall_latency_ms / parallelism) Panel is clearly marked as aggregate-derived with a fixture notice. Shows "No subtask data" with reducer name when subtask_count=0. Wired as new "Subtask Detail" tab in SwarmControlPlane alongside existing "Run Detail" aggregate card. Uses List icon from lucide-react. Schema confirmed: swarm_runs has no jsonb subtask column and no separate swarm_subtasks table exists yet in stability-test. All per-subtask data requires node_swarm_subtask_state_reducer projection. 15 new tests + 10 existing passing (25 total). * refactor(OMN-11578/OMN-11596): reduce cyclomatic complexity in ThreeCanvas and parseDashboardDefinition (#137) OMN-11578: Extract advanceFocusProgress, applyFocusedOpacities, applyIdleOpacities, and projectAxisLabels from the ThreeCanvas useEffect god-function (CCN=83). The tick loop is now a thin orchestrator calling four named helpers via a TickRefs context struct. OMN-11596: Extract validateTopLevelFields and validateLayoutItem from parseDashboardDefinition (CCN=31). The parse function is reduced to an 8-line orchestrator; all conditional branches live in the helpers. * fix(OMN-12245): silence unused swarm route request (#139) * feat(OMN-11658): wire SEA demo dashboard projections (#140) * feat(OMN-11658): wire SEA demo dashboard projections * fix(OMN-11658): make trace rows toggle independently * fix(OMN-11658): polish SEA dashboard rendering * feat(OMN-11658): expose delegation trigger in SEA control plane (#141) * fix(OMN-11658): improve SEA prompt feedback (#142) * fix(OMN-11658): let dashboard fill wide viewports (#143) * fix(OMN-11658): balance dashboard gutters (#144) * fix(OMN-11658): keep SEA stream newest first (#145) * fix(OMN-11658): refresh SEA traces after prompt submit (#146) * fix(OMN-11658): center dashboard shell (#147) * fix(OMN-11658): extend dashboard body to bottom (#148) * fix(OMN-11658): repair empty SEA dashboard layout (#149) * chore(DELEG-002): annotate pricing fallbacks, align fixture model names with bifrost config (#150) * feat(OMN-12070): wire delegation trigger through omnimarket runtime (#151) - Add COMMAND_TOPICS.delegateSkill ('onex.cmd.omnimarket.delegate-skill.v1') to shared/types/command-topics.ts so the topic is contract-declared rather than hardcoded in server/routes.ts - Declare the delegation trigger command topic in contract.yaml with handler node, ingress path, payload shape, and OMNIDASH_RUNTIME_EFFECTS_URL env note - server/routes.ts uses COMMAND_TOPICS.delegateSkill constant; runtime targets stability-test :18085/v1/effects via OMNIDASH_RUNTIME_EFFECTS_URL env var - DelegationRunContext exposes pendingCorrelationId + setPendingCorrelationId so a freshly-dispatched correlation_id propagates to all child panels before projection rows materialize - DelegationRunHeader wires onCorrelationId callback to DelegationTriggerPanel - DelegationEvidenceTabs auto-navigates to correlation-trace tab on dispatch and displays a pending indicator with the correlation_id - DelegationCorrelationTracePanel falls back to pendingCorrelationId when no run is selected and shows appropriate pending vs awaiting-events messages - Test: setPendingCorrelationId updates context value (DelegationRunContext.test) * fix(OMN-12243): add 'delegation' short-topic alias to postgres + sqlite projection readers (#152) GET /projection/delegation was returning rows:[] because the query() switch had no 'delegation' case — only the full versioned topic name. Adds a fall-through alias in both PostgresProjectionReader and SqliteProjectionReader so the short topic name routes to the same decisions.v1 query logic. Test: new routes.test.ts case seeds a SQLite DB and asserts the short alias returns the same rows as the full topic name. * fix(OMN-12287): close fixture gaps and add file-mode banners to dashboard widgets (#153) - Add fileMode prop to ComponentWrapper: renders a [File Mode] badge in the widget header when active, analogous to the [Live] badge, using Text component to comply with the no-typography-inline ESLint rule - Wire fileMode={!isLiveDataSource(mode)} on all 13 widgets that use useDataSourceMode: TraceExplorer, LiveEventStream, ContextHeatmap, DelegationControlPlane, EventStream, ReadinessGate, DelegationMetrics2D, DelegationSavingsWidget, DelegationTokenUsageWidget, DelegationModelRoutingWidget, DelegationQualityGateWidget, AbCompareWidget - Improve emptyHint messages on all updated widgets to include file-mode fixture directory paths so developers know exactly where to add test data - 1124/1124 tests pass * feat(OMN-12288): deep-link delegation runs to trace explorer (#154) - Add traceFilter + setTraceFilter to UISlice so a correlation_id can be passed to TraceExplorer before navigating there (no URL routing required) - DelegationRunTable adds a GitCommitHorizontal icon button per row; clicking it calls setTraceFilter(correlationId) + setActivePage('trace') and stops event propagation so the row-select handler is not also triggered - TraceExplorerWidget initializes its query state from traceFilter on mount and immediately clears the store value so subsequent navigations start fresh - Tests: DelegationRunTable.test.tsx (7 cases), TraceExplorerWidget deep-link test confirms filter pre-fill + store clear (1132 total tests pass) * feat(T22): add DelegationCostComparisonWidget — per-run cost proof surface (#155) - New widget DelegationCostComparisonWidget in delegation/ directory reads from TOPICS.delegationSavings (same source as DelegationSavingsWidget) and surfaces a per-session cost comparison table with columns: Session, Task, Model, Tokens, Latency, Local $, Cloud $, Saved, Provenance - Provenance badges (measured/estimated) on each row per the § 1.3 spec — green for measured (llm_call_metrics), amber for estimated (routing-rule inferred) - Summary KPI bar shows cumulative savings, local/cloud cost, session count, total tokens, measured vs estimated breakdown - Sort controls: date (default), savings descending, tokens descending - maxRows config cap with overflow notice; showProvenance=false hides source column - Registered as 'delegation-cost-comparison' in generate-registry.ts and index.ts - Integration tests updated for count 29 → 30 - 1132/1132 tests pass * fix(OMN-12402): unify CommandPalette node-type badges and Dispatch button contrast (#156) Issue 1: kindColor() mixed design tokens (var(--accent), var(--text-tertiary)) with hardcoded hex (#d97706, #7c3aed), so ORCHESTRATOR/COMPUTE badges rendered as weak plain text while EFFECT/REDUCER read as vivid chips. Drive all four badges from the design-system node-type role tokens (--<kind>{,-soft,-ink}), which exist for both themes, so every badge is a consistent filled chip. Issue 2: the Dispatch button used var(--accent) as its fill (which resolves to --brand-soft, a pale hover tint) plus literal color:'white' text — invisible in light mode. Use the brand fill + on-brand text token (--brand / --primary-foreground) so the CTA stays legible in both themes. Adds CommandPalette.test.tsx asserting both fixes. Visual proof: docs/evidence/OMN-12402-{badges,dispatch-light}.png * fix(OMN-12367): render Correlation Trace tab gracefully in file mode (#159) In VITE_DATA_SOURCE=file mode the /api/delegation/* proxy is not registered (it is wired only when VITE_PROJECTION_API_URL is set), so fetchCorrelationTrace hit the Vite SPA fallback, received index.html, and surfaced a raw 'JSON.parse: unexpected character at line 1 column 1' error. Detect file mode up front in fetchCorrelationTrace and read a per-correlation fixture under fixtures/onex.snapshot.projection.delegation.correlation-trace.v1/ so the surface renders a representative event chain without postgres. In live mode, parse defensively (res.json().catch) so any non-JSON body degrades to an empty trace and the panel shows its graceful no-events message rather than a raw parse error. Adds the delegationCorrelationTrace topic, a representative 2-event fixture for the file-mode decisions correlation (069e55b5...), and tests covering file-mode fixture fetch and non-JSON hardening. * fix(OMN-12399): register Context Effectiveness Heatmap widget in palette (#158) The widget source (component, hook, fixtures, types, test) existed but was registered in zero of the three Track A surfaces, so it never appeared in the widget palette and was unreachable through the supported user flow. - Add lazy import to componentImports in src/components/dashboard/index.ts - Add context-effectiveness-heatmap manifest to MVP_COMPONENTS in scripts/generate-registry.ts (quality category, contextExperimentScores projection, OMN-11241 fixture fallback) - Regenerate src/registry/component-registry.json (30 -> 31 components) - Update integration.part2/part3 component-count + name-list assertions Visual proof (OMN-7093): palette entry + widget rendered on dashboard in file mode using the OMN-11241 research fixture. * fix(OMN-12366): give Delegation Control Plane active tab a clear visual indicator (#157) The 10-tab evidence strip distinguished active from inactive tabs only by background (var(--panel-2) vs transparent); border, text color, and weight were identical, so the strip did not read as tabs. Active tab now uses the --brand accent token: brand border, a 2px brand bottom-border underline, --brand-soft background, and brand-colored semibold label text. All via existing design tokens (no hardcoded hex), verified to read clearly in both light and dark themes. Visual evidence (OMN-7093): - docs/evidence/OMN-12366-tab-strip-dark.png - docs/evidence/OMN-12366-tab-strip-light.png * feat(OMN-12139): add Trace Explorer fixture for onex.snapshot.projection.traces.v1 (#161) Wires a _trace_groups() factory into scripts/generate_fixtures.py and emits five representative TraceGroup records under fixtures/onex.snapshot.projection.traces.v1/, covering every TraceExplorer UI state: multi-node completed, single-node completed, running, error, and a longer multi-node trace. The widget reads each file as one TraceGroup via the legacy useProjectionQuery readAll path; the file-source middleware auto-generates index.json. Shapes mirror the widget's TraceGroup interface exactly (no entity_id); each record is keyed by its correlation_id so the fixture filename matches. DoD: VITE_DATA_SOURCE=file dev server renders 5 traces (1 running, 1 error) in the Trace Explorer in File Mode; Playwright screenshot under docs/evidence/OMN-12139-trace-explorer-file-mode.png proves populated/running/ error states (OMN-7093). npm run build, lint, and full vitest suite (1172 tests) green. * fix(OMN-12397): make Savings Proof Pack source line dynamic by data mode (#160) The printable proof pack hardcoded "Source: OmniNode delegation_events projection" even in file mode, so a PDF exported from fixture data was misleading once removed from the parent Fixture banner. Derive the "Source:" line and reproducibility sentence from useDataSourceMode(): file -> Local fixture file (fixtures/.../demo-delegation-snapshot.json) sqlite -> Local SQLite projection http -> OmniNode projection API via Express bridge postgres -> OmniNode delegation_events projection Non-live modes (file/sqlite) state the report is from a local development data source and is not a live projection. Renamed fixture live-delegation-demo.json -> demo-delegation-snapshot.json; the word 'live' inside fixtures/ was second-order misleading. The file is discovered by directory glob (vite fixturesMiddleware readdirSync), so no code references the old name. dod_evidence: rendered_output screenshot docs/evidence/OMN-12397-proof-pack-file-mode-source.png shows the file-mode footer with the fixture source string and 15 fixture rows. * fix(OMN-12392): repoint run-generate-fixtures.sh to local script and correct CLAUDE.md fixture claim (#162) OMN-12392: `run-generate-fixtures.sh` was looking for `generate_fixtures.py` at `$OMNIBASE_INFRA_PATH/scripts/generate_fixtures.py`, but that path does not exist in omnibase_infra. The script lives in this repo at `scripts/generate_fixtures.py`. Fix: set SCRIPT_PATH to `$REPO/scripts/generate_fixtures.py` while preserving `--project "$OMNIBASE_INFRA_PATH"` for uv dependency resolution (the script imports from omnibase_infra). `npm run generate:fixtures` now works without a non-standard OMNIBASE_INFRA_PATH. Also fix CLAUDE.md hard rule #6 which claimed "Fixtures and layouts are not committed" — fixtures/ IS tracked via the OMN-10305 gitignore carve-out (!fixtures/, !fixtures/**). Correct the claim and add the carve-out explanation. OMN-12393: add a lineage classification table to CLAUDE.md documenting that omnidash/ (dev) is CANONICAL_ACTIVE, omnidash-v2/ is SUPERSEDED, and omnidash-v2-new/ is a TEMP_STAGING clone. Cite verification evidence. * test(OMN-10947): pin dashboard e2e fixture proof (#163) * test(OMN-10947): scope dashboard e2e locators (#164) * fix(OMN-12394): add .env to omnidash .gitignore to prevent credential leak (#165) The .env file holds OMNIDASH_ANALYTICS_DB_URL with a live Postgres password. The existing rule only covered .env.local* — .env itself was unprotected. Adds an explicit .env entry so git never tracks the credential file. * fix: move omnidash event bus config to contract overlay (#166) * chore(OMN-12459): add universal managed gitignore block to omnidash (#168) Appends the onex-managed universal block to .gitignore, adding .env.*, !.env.example, *.log, .idea/, .vscode/, test-results/, and playwright-report/ coverage. .env and .DS_Store were already present individually; the block makes the policy explicit and machine-readable. No tracked files required untracking. * ci(OMN-12477): add dev-only main-target-guard (forward-port from main) (#167) Forward-ports the main-target-guard workflow to dev (the file existed on main but was absent on dev — main<->dev drift). Adds the dev-only variant WITHOUT the hotfix/* bypass: main accepts only dev promotion PRs carrying promotion-receipt: OCC-N. The guard only triggers on base==main, so it is inert on dev-targeting PRs and takes effect at the next dev->main promotion. OMN-12477 * feat(OMN-12674): remove reviewdog workflows and configs (#170) Removes the reviewdog caller workflow from omnidash. No reviewdog required-status-check bindings existed on main protection, dev protection, or any repo ruleset, so this is the only active reviewdog surface in the repo. dod_evidence: OMN-12674 * fix(OMN-12739): tighten delegation dashboard formatting (#171) * fix(OMN-12740): add browser-safe dashboard id fallback (#172) * feat(OMN-12745): add DelegationModelOutputWidget rendering inference generated_text (#177) Implements the demo-blocking deliverable for OMN-12745: DelegationModelOutputWidget renders ModelLlmInferenceResponse.generated_text sourced from bus topic onex.evt.omnibase-infra.inference-response.v1 in fixture mode (offline, no .201 dependency). Live surfacing is gated on the OMN-12746 demo redeploy. Changes: - src/components/dashboard/delegation/DelegationModelOutputWidget.tsx — new widget rendering latest generated_text, model identity, task type, token count, latency, and a recent response history list - src/components/dashboard/delegation/DelegationModelOutputWidget.test.tsx — 8 unit tests (TDD-first: loading, empty, generated_text, model identity, topic label, upstream notice, provisioned, token count) - src/components/dashboard/delegation/DelegationModelOutputWidget.stories.tsx — 6 stories: Empty, Loading, Populated, FixtureMode, Provisioned, SingleResponse - shared/types/topics.ts — adds inferenceResponseText topic key - src/storybook/fixtures/delegation-routing.ts — adds buildInferenceResponseProjection - src/storybook/fixtures/index.ts — exports new fixture builder - scripts/generate-registry.ts — adds delegation-model-output MVP manifest entry - src/registry/component-registry.json — regenerated (32 components) - src/components/dashboard/index.ts — lazy-import registration - src/templates/sea-demo.ts — delegation-model-output added to SEA Demo template - src/templates/templates.test.ts — updated SEA Demo widget order assertions - src/integration.part2.test.tsx — component count 31 to 32 and name list - src/integration.part3.test.tsx — component count 31 to 32 - src/storybook-coverage-compliance.test.ts — adds story file to compliance list - fixtures/onex.snapshot.projection.delegation.inference-response-text.v1/snapshot.json — fixture data for file mode (offline demo) Evidence-Source OCC SHA: c872f8edbabda4306e3dd290dbb8995031da3d7c * feat(OMN-12748): render delegated prompt/response from contract-declared projection (#178) The Correlation Trace panel already renders prompt_text/response_text per run, but fetchCorrelationTrace called a bespoke /api/delegation/correlation-trace/<id> REST route that the deployed contract-driven projection API does not implement (404), so the panel always showed 'No events found' and the dashboard could not surface the actual delegated work. Read the per-correlation trace from the contract-declared projection instead: /projection/onex.snapshot.projection.delegation.correlation-trace.v1?correlation_id=<id> (a read path the projection API already supports). Selecting a run now renders its prompt + response. File mode still reads the per-correlation fixture. Pairs with omnimarket OMN-12748, which exposes that projection over delegation_events. Canonical: client renders a contract-declared projection; no bespoke REST route. * chore(release)(OMN-12245): backmerge omnidash v1.1.1 to dev (#176) * fix(OMN-12756): publish delegation trigger through command bus (#179) * feat(OMN-12748): render delegated prompt/response from contract-declared projection The Correlation Trace panel already renders prompt_text/response_text per run, but fetchCorrelationTrace called a bespoke /api/delegation/correlation-trace/<id> REST route that the deployed contract-driven projection API does not implement (404), so the panel always showed 'No events found' and the dashboard could not surface the actual delegated work. Read the per-correlation trace from the contract-declared projection instead: /projection/onex.snapshot.projection.delegation.correlation-trace.v1?correlation_id=<id> (a read path the projection API already supports). Selecting a run now renders its prompt + response. File mode still reads the per-correlation fixture. Pairs with omnimarket OMN-12748, which exposes that projection over delegation_events. Canonical: client renders a contract-declared projection; no bespoke REST route. * fix(OMN-12756): route delegation API to dashboard server * chore(OMN-12765): backmerge omnidash main to dev (#181) * feat(OMN-11614, OMN-11617): deduplicate projection readers and integration test utilities (#120) * feat(OMN-11979): redesign delegation dashboard — dense metrics, narrative labels, stale .js cleanup (#119) - Replace donut chart with terminal-style KPI tiles + stacked model bar + task breakdown - Rename Quality Gate → Escalation Detail, humanize failure categories - Fix postgres-projection-reader: correct column names, JSONB string coercion - Add /projection Vite proxy for Express bridge - Remove 8 stale compiled .js files that shadowed .tsx source via Vite resolution - "Quality Gate" → "Local Success", "pass/fail" → "local/escalated" throughout * feat(OMN-11614, OMN-11617): deduplicate projection readers and integration test utilities OMN-11614: Extract shared session-merge and cost-savings-overview aggregation logic from postgres-projection-reader.ts and sqlite-projection-reader.ts into server/projection-utils.ts. Removes ~160 lines o…
OMN-12072 — Swarm Progression UI (B2-T1 through B2-T5)
Adds the Swarm Control Plane dashboard widget that renders swarm progression data from the
swarm_runsprojection.Components
SwarmDecompositionTreemodels_used, graceful empty state whensubtask_count=0SwarmSubtaskCardSwarmWaveVisualizationsubtask_count=0SwarmLiveCountersSwarmSavingsBarSupporting infrastructure
SwarmRunTable— 8-column run list (Status, Run ID, Subtasks, Speedup, Wall time, Cost, Savings, Created) with row selectionSwarmControlPlane— tabbed host widget (Overview, Decomposition, Run Detail, Wave View, Savings) withComponentWrappershared/types/topics.ts— addedswarmRuns: 'onex.snapshot.projection.swarm.runs.v1'server/postgres-projection-reader.ts— addedswarm_runsSELECT caseserver/routes.ts— addedGET /api/swarm-runssrc/services/swarm-api.ts— typed fetch clientGraceful degradation
The
swarm_runstable currently has 12 rows withsubtask_count=0, zero costs, and emptymodels_used—node_swarm_subtask_state_reducerisn't deployed yet. All components degrade with informative placeholder text explaining which projection is needed. Live counters and the savings bar will populate automatically once the projection materializes.Tests
10 tests passing — all tabs, empty states, edge cases (zero subtasks, zero costs), and run selection.
dod_evidence
npx tsc --noEmit)local/no-typography-inlineandlocal/no-projection-fallbackissues fixed)Summary by CodeRabbit