Skip to content

Release History

Changelog

⊕RSS FEED

Every release documented — new components, token additions, A2UI templates, and documentation changes.

The `./ag-ui` protocol layer: reducer, hook, selectors, crosswalk (#192)

ComponentsDocs
  • —./ag-ui subpath export (src/ag-ui.ts), carrying a pure reducer, a React hook, seven prop selectors, and a state crosswalk — Phase 1a of the CopilotKit/AG-UI integration spec (#144, Layer 1).
  • —reduceAgentEvent(state, event): RunViewModel and emptyRunViewModel (lib/ag-ui/reduce-agent-event.ts): a pure, throw-free fold from AG-UI BaseEvents to one RunViewModel (lib/ag-ui/run-view-model.ts). Discriminates on the event's type string only — never instanceof — because the Phase 0 spike found four coexisting @ag-ui/client module instances in this repo's resolved tree, three reporting the same version string while being physically separate files, which breaks identity checks silently. Unknown or malformed events are kept in unhandled rather than dropped or thrown on; raw is the separate bucket for the protocol's own EventType.RAW passthrough event.
  • —useAgentRun(agent) (lib/ag-ui/use-agent-run.ts): subscribes an AbstractAgent, folds its events, and exposes { vm, send, resume, stop }. Guards against updating state after unmount with two independent mechanisms, each verified against the installed @ag-ui/client 1.0.2 build and each catching a failure mode the other does not.
  • —Seven selectors (lib/ag-ui/selectors.ts) mapping RunViewModel to the props of RunLifecycleRail, MCPToolCall, StateDeltaInspector, HumanInputRequest, ReasoningTree, ChatMessage and ArtifactViewer — no component changed to consume them — and the InteractionState/A2ATaskState crosswalk (lib/ag-ui/state-crosswalk.ts).
  • —lib/ag-ui/README.md: what the reducer is for, the raw/unhandled split, why discrimination is on the type string, why interrupts are read from RUN_FINISHED's outcome rather than derived from a pending tool call (the success outcome's pendingToolCallIds makes an unresolved tool call a normal way to succeed), why nothing maps THINKING_* (no such event type in the installed @ag-ui/core 1.0.2), and the two import-boundary tests (__tests__/import-boundaries.test.ts).

`--ds-border-separator` stops doing a fill's job (#197, stages 1–3)

TemplatesTokensComponents
  • —--ds-state-inactive — a new semantic token for the off/pending/unfilled half of something that also has an on half: step dots and connectors, progress/strength tracks, priority/selection stripes, markers (~60 measured sites). Declared once in tokens/semantic.css as var(--bh-neutral-600), which auto-swaps per theme via the primitive. Measured against the worst of --ds-surface-{ground,raised,overlay}: 7.41:1 light (worst = ground), 7.60:1 dark (worst = overlay) — deliberately well above the 3:1 floor because several consuming sites composite it at 0.3–0.45 opacity, which eats most of that headroom: at 0.3 the composited result still reads ~1.6:1 (light) / ~1.9:1 (dark) against its own background, above --ds-border-structure's resting 1.22–1.37:1. neutral-600 is the same primitive --ds-text-secondary and --ds-text-disabled already reference — an earlier draft of this entry called it "unclaimed", which is false, and used that as the reason for rejecting neutral-500, so the rationale argued against its own choice. Sharing a *primitive* is not the defect this split fixes: that defect was one semantic token serving two roles, where no single value could satisfy both. Two semantic tokens on one primitive is ordinary, and each can be retuned independently.
  • —--ds-control-track — an alias for --ds-border-control (var(--ds-border-control), not a new value): the switch track IS "the visible boundary of an interactive control," the role --ds-border-control already names. components/form/Switch.tsx's off-state track now points here instead of at --ds-border-separator, so Switch no longer disappears (1.19:1) if the dark-theme responsive.css override is ever removed (stage 4, not done here).
  • —29 token references across 23 components migrated from --ds-border-separator to --ds-state-inactive wherever the token was consumed as a background/backgroundColor/fill/stroke. Three of those sites route through a shared colour variable (nodeColor in ApprovalPipeline/ApprovalStepIndicator, strengthColor in LearningMultiplierGraph) that ALSO feeds a border or text sink, so a few borders did change pixels — an earlier draft said "never a border", which was inaccurate: ApprovalStepIndicator, ApprovalPipeline, PaymentLifecycleTracker, PartialSuccessPanel, MultiAgentOrchestrationView, AgentRecoveryFlow, CeremonyGate, CounterpartyRiskCard, ScreeningResultCard, ComplianceChecklist, AgencyStack, LearningLedger, OnboardingChecklist, RetrievalRanking, PasswordStrength, ModelSelector, IterationDiff, SpeechStateIndicator, LearningMultiplierGraph (the last also fixes two SVG stroke sinks on zero-strength graph edges/nodes), HeroVisual, PaymentTracker, SettlementTimeline, RunLifecycleRail. Measured with the guard's own scanner: 113 fill/stroke sinks resolved to --ds-border-separator before this change and 82 after, i.e. 31 sinks eliminated.
  • —11 of the 34 flagged bucket-B components (AgentDecisionTree, ValidationChain, ScheduleGridView, PaymentQueueTable, AgentConflictPanel, ThinkingState, AgentThinkingIndicator, AgentActivityFeed, AgentPauseControl, ConflictResolutionBanner, AgentIdentityBadge) were traced to their sinks and found to consume --ds-border-separator only through border/text properties, never a fill — left untouched, because migrating them would not change any rendered pixel and the task is "fix the fill sink," not "touch every listed file."
  • —tests/border-separator-no-fill.test.ts — the precondition stage 4 (deleting the responsive.css override) will need: no background/backgroundColor/fill/stroke sink in the 35 managed files resolves to --ds-border-separator (1 bucket-A Switch.tsx plus 34 bucket-B, each asserted to exist on disk). Parses across newlines (a line-based grep misses ternaries and status-colour map lookups the same way the Badge scan in tests/solid-fill-borders.test.ts missed 74 of 74 elements) and resolves one hop through local const/let bindings, so a map read by key several lines from its consumer is still caught. Two known, manually-verified analyzer-precision gaps and 8 real, intentional bucket-C decorative sites inside otherwise-bucket-B files (static hairline dividers, a loading skeleton) are allow-listed by exact count, so any new site joining either list fails loudly. Mutation-tested: reintroducing the token at a real migrated site (CeremonyGate.tsx) reddens the guard, verified applied by exact match count before trusting the red.

Badge is a label, not a control (#199)

Components
  • —The 3:1 boundary floor added in #198 now binds controls only. Badge renders a bare <span> — no handler, no tabIndex, no interactive role, no href — and WCAG 1.4.11 governs the boundary of a user interface *component*, something a person operates. A label has no boundary for the criterion to govern, so darkening every outlined badge to satisfy it would have been a design change bought with a misreading. No badge colour changes.
  • —The four badge edges previously parked in a BELOW_MIN_CONTRAST set are no longer failures to park, so the set and its companion "still failing" test are gone.
  • —The exemption is pinned where it is actually decided: at the consumer. Badge spreads {...props} onto its span, so whether a badge is operable is a call-site property the component's own source cannot show. A review proved the gap — adding onClick/tabIndex/role="button" at a call site produced an operable badge with a 1.19:1 boundary while every test stayed green.
  • —The scan parses <Badge …> across newlines. A line-based grep finds 0 of this repo's 74 badge elements, because nearly every one spans several lines — a guard that looks thorough and covers nothing. The suite asserts the scan still reaches them, so it cannot quietly go vacuous.
  • —role= alone does not count. components/finance/SanctionsCheck.tsx passes role="alert" — a live-region announcement, not an affordance — and a guard that flagged it would have been wrong on the day it shipped. Only roles a person operates are flagged.

The palettes declare their own control boundary, and the portal tokens stop overstating (#201, #204)

TokensDocs
  • —#201 — the control boundary was the one edge that ignored the active palette. #198 added --ds-border-control and pointed --ds-btn-secondary-border at it. The five alternate palettes redeclare --ds-border-structure in their own hues but declared no control boundary, so a data-theme="ash-grey" page inherited the default light neutral — including in the dark blocks. All 9 blocks (four palettes have a dark and a light block, iris has one) now declare it, each value taken from that block's own family steps and measured against the worst of its ground, raised and overlay:
  • —#204 — the portal --color-* tokens repeated the #202 defect. Three text values sat below WCAG AA against the surfaces portal declares in these same files, and all 32 of the family's comments carried a hand-written "~x:1" approximation rather than a measured one — 21 of the 32 overstating the real figure against the worst-surface basis the new comments use (17 against the void-only basis the old comments stated), by as much as 1.58x:
  • —tests/palette-contrast.test.ts gains two families, keeping the shape of the existing one: --ds-border-control at a 3:1 floor (a UI boundary, not text — WCAG 1.4.11) against the worst of ground/raised/overlay, and the portal --color-* text tokens at 4.5:1 against the worst of void/surface. Both pin an exact entry count (9 and 32), both require every entry to carry its full surface set, both carry a control case proving the measurement can return a failure, and both assert that no comment claims a ratio the value does not have — the half of #201 and #204 that is a documentation defect rather than a contrast one.
  • —Deliberately not covered: --ds-border-{structure,separator,subtle} are hairlines at 1.0–1.4:1 by design, so asserting 3:1 on them would assert a defect; --color-border likewise. iris declares no portal block at all, which is why the portal count is 8 blocks and not 9 — asserted explicitly rather than left to the arithmetic.

Alternate palette text clears WCAG AA on every surface (#202)

Tokens
  • —Text values across the five alternate palettes sat below WCAG AA (4.5:1) against the surfaces they render on, while their comments claimed AA. The worst, colonial-cobalt light --ds-text-muted, measured 1.68:1 against a comment asserting "~3.0:1 (large)" — overstated by 1.8x.
  • —--ds-text-secondary and --ds-text-muted in all nine palette blocks are now taken from each palette's own family steps (--ag-slate-300, --vt-midnight-200, and so on) rather than from invented hexes, so the values stay on the palette's scale and keep its identity.
  • —Every comment on a palette text token states the measured ratio against the worst surface, and what the value changed from. Colour names are preserved where the value did not move.
  • —tests/palette-contrast.test.ts: every --ds-text-{primary,secondary,muted} in all 9 blocks clears 4.5:1 against the worst of its three surfaces; the tier gap holds in every block; no comment may claim a ratio the value does not have; and each entry must carry all three surfaces, so "worst" cannot quietly mean "the only one found". Surfaces reset at each theme block, so a token can never be paired against the previous block's ground.
  • —--ds-text-inverse sits on an inverse surface, not these three. Measured separately against --ds-surface-inverse it is 15.15–17.56:1 across all nine blocks — no defect. An early survey compared it to the ground instead and produced five 1.00:1 "failures" that were artefacts of the question.

The secondary button gets an edge you can see (#198)

Tokens
  • —secondary was drawn with --ds-border-structure, which measures 1.22:1 against the light page ground and 1.37:1 against the dark one. The reported symptom was a dark button whose border looked unclean; measuring inverted the diagnosis — the edge was not loud, it was a faint grey smear, and a 1px smear is what reads as a blurred edge.
  • —New semantic step --ds-border-control: var(--bh-neutral-500), the first point on the ramp clearing 3:1 in both themes (4.63:1 light, 6.72:1 dark; neutral-400 misses at 2.46 and 2.74). The primitive is already theme-swapped, so one declaration serves both. --ds-btn-secondary-border now points at it. The fill is unchanged, and primary and danger are untouched.
  • —A 3:1 floor on the visible boundary of any control whose fill is a surface — exactly the variants exempt from "a solid fill borders itself", because for them the border IS the boundary. tests/solid-fill-borders.test.ts enforces it, and its control case asserts the pre-#198 value still fails, so the check is shown able to go red.
  • —On the 3:1 figure: it is borrowed from WCAG 1.4.11 but stated as this system's own rule, not a conformance claim. 1.4.11 does not apply cleanly here — a button carrying a text label is already identified by its label, and a Badge is usually not interactive at all. Two deliberate things in this repo show the difference: ghost ships with no boundary whatsoever, and a disabled secondary renders at opacity: 0.4 (~1.6:1). So a failure means "this edge is too faint to be the deliberate thing it is meant to be" — which is the defect actually fixed — rather than an accessibility violation.
  • —The four Badge variants that also fail it (1.19–1.37:1, two tokens) are carried in a BELOW_3_TO_1 set as data rather than silence, with a second test requiring every entry to still be failing. Mutation-tested three ways: reverting secondary reddens the rule, fixing a pinned token forces it off the set, and padding the set with an already-passing token fails. It shrinks under pressure instead of rotting into an allowlist. Raising those four is #199 — a visible change to every outlined badge, and a question of whether a badge is interactive at all.

Solid-filled badges no longer draw a contrasting border (#195)

TokensComponents
  • —components/ui/Badge.tsx hardcoded --ds-border-structure on its black and validation colours, the same layer bypass #193 fixed in Button.tsx. The black badge is --ds-surface-inverse: in light theme an #E4E4E7 hairline around a #09090B badge, and in dark a #302D2A ring around a #FAF9F7 one — the halo of #193, mirrored. validation wore a grey ring over its own gold while agency and temporal already bordered themselves.
  • —All ten of Badge's compound variants now take their BORDER from a component token. Six new tokens in tokens/semantic.css: --ds-badge-{agency,temporal,validation,black}-border reference their own -bg, --ds-badge-gray-border stays --ds-border-separator, and --ds-badge-structural-border carries --ds-border-structure for the outlined variants whose colour family has no ring of its own. Only two variants change appearance — black filled and validation filled; the other eight keep the value they already resolved to.
  • —The agency, temporal and validation border tokens each serve two roles: the self-border of the filled badge and the contrasting ring of the outlined one, which fills with the matching -subtle tint. The token comment says so, because reducing one of them to transparent would delete the outlined badge's only edge.
  • —tests/dark-button-borders.test.ts is replaced by tests/solid-fill-borders.test.ts, which checks one property for both components instead of one component's instance of it: a control whose fill is SOLID borders itself, while a control filled with a surface (the page ground, a raised panel, a -subtle tint) keeps the border that is its only edge. Both halves are derived — solid means "the resolved fill is not one of this condition's surface values" — so no variant is named and no exemption is hand-maintained. It classifies all 14 variants of the two components correctly across eight conditions (light and dark, sRGB and wide-gamut, normal and high contrast).
  • —The published token reference gains the six badge border tokens, in both the component table and the usage map, and the existing agreement check now covers --ds-badge-*-border as well as --ds-btn-*-border.

Dark buttons no longer draw a contrasting border (#193)

TokensComponentsDocs
  • —components/ui/Button.tsx: the primary variant consumed --ds-border-structure directly instead of its own --ds-btn-primary-border token. In light theme that painted an #E4E4E7 hairline around an #09090B button, which read as a halo and blurred the edge; the dark theme block redefines --ds-border-structure to #302D2A, so a seam showed there too. It now consumes --ds-btn-primary-border, like every other variant.
  • —tokens/semantic.css: --ds-btn-primary-border is var(--ds-btn-primary-bg) — the approach danger already used. .ds-btn still sets border-width and border-style for every variant, so the border box is reserved and primary stays dimensionally identical to secondary and danger.
  • —tests/dark-button-borders.test.ts: a filled button's border is its own background, checked in light and dark, on sRGB and wide-gamut displays, at normal and high contrast — eight conditions. The property is deliberately not gated on the background being dark: a luminance gate holds today but would let a token-level revert back in through dark theme, where primary's fill is #FAF9F7. Measured — the ungated property fails all eight conditions on that revert, the gated one failed only the two light ones. Variants and the filled/outline split are derived from the token names, so a new --ds-btn-<v>-bg is covered the day it is declared. A control case puts the old declaration back on the token map and requires the detector to flag it.
  • —tests/helpers/token-cascade.ts: the cascade-aware token resolver, extracted unchanged from tests/case-study-contrast.test.ts (#158, #170, #172) so both tests read the tokens the same way. That test still passes, 15/15.
  • —A guard that the published token reference states what the CSS declares: app/(site)/foundation/tokens/page.tsx had --ds-btn-primary-border resolving to --ds-border-structure, which this fix made false. The table now also lists --ds-btn-secondary-border and --ds-btn-danger-border, which it had omitted.

Design spec for CopilotKit and AG-UI integration (draft, #144)

TemplatesComponentsDocs
  • —docs/superpowers/specs/2026-10-12-copilotkit-ag-ui-integration-design.md: the design for making the AG-UI protocol and CopilotKit first-class in the design system. It records the two owner decisions of 2026-10-12 (build it and let it be the probe for #101; AG-UI core with CopilotKit as an adapter), the four layers (dependency contract, an ./ag-ui protocol layer, a ./copilotkit adapter, the docs site), template adoption in three tiers, verification, phases, and risks.
  • —Third owner decision recorded: the local example runs through the signed-in Claude CLI with no API key, and the deployed site replays recorded runs and never calls a model. The spec gains a "Model access" section with the measured CLI invocation (three one-shot runs, CLI 2.1.294: login works with user settings excluded, tools and MCP servers off, and structured render actions stream as tool-use deltas), the guards on the local route, and three gates that keep a key and a deployed model call out of the repository.
  • —The route-guard work of #156 is re-scoped by that decision: its size caps carry into the local route, its rate limit and kill switch are dropped with the public model route they protected.

The three expensive CI suites no longer run on every PR (#139)

Docs
  • —E2E Critical User Flows, Visual Regression (.github/workflows/ci.yml) and Lighthouse CI (.github/workflows/lighthouse.yml) now run on pushes to main, or on a pull request carrying ci:e2e, ci:visual or ci:lighthouse. Those three labels were created with this change; none existed before.
  • —pull_request in both workflows now declares types: [opened, synchronize, reopened, labeled]. Without labeled, GitHub's default event set means adding a label to an open PR starts no run, so a label-based opt-in would silently never fire.
  • —CONTRIBUTING.md step 5 said "Visual regression runs automatically on every PR". It now documents the opt-in labels and the local commands.
  • —The always-on jobs (typecheck, test, package, coauthor-check) skip labeled events, and a suite's gate fires on a label event only when the label just added is its own. Without both, enabling labeled would make every label change — including adding type:infra — cost a full CI run, and labelling an already-opted-in PR would duplicate a 35-minute suite.
  • —build is the exception: it still runs for ci:e2e and ci:visual label events, because both suites declare needs: [build] and download its nextjs-build artifact, and a job whose needs was skipped is itself skipped. Guarding it like the others would have made the opt-in silently do nothing.

A Playwright spec that emulates a wide-gamut display (#180)

TokensComponentsDocs
  • —tests/e2e/12-wide-gamut.spec.ts and the npm run test:wide-gamut script. Four defects fixed earlier today (#170, #172, #174, #178) shared one root cause: code that assumed every colour is sRGB, while the light theme's three canonical primaries (--bh-red, --bh-blue, --bh-yellow) compute as color(display-p3 r g b) on a wide-gamut display. Every browser check before this spec was blind to the whole class, because headless Chromium reports (color-gamut: p3) as false by default.
  • —Emulation is a CDP session (Emulation.setEmulatedMedia, { name: "color-gamut", value: "p3" }), factored into one emulateP3() helper in the spec. page.emulateMedia({ colorGamut: "p3" }) was tried first and did not make the media query match in this setup.
  • —Every test asserts the emulation guard first — matchMedia('(color-gamut: p3)').matches — so the suite cannot pass by silently not emulating.
  • —Four behaviours asserted: /foundation/tokens/ shows the three P3 r g b labels and no hex for the primaries; a WebGL chart (/charts/depth-chart-gl/) has both red-dominant and blue-dominant canvas pixels, not merely "not blank"; dark theme and data-contrast="high" both keep the primaries in rgb(), never Display P3.
  • —The WebGL assertion forces preserveDrawingBuffer: true on every WebGL context the page creates, via addInitScript (test-only): components/finance/WebGLChartProvider.tsx creates contexts with it false, so by the time an external script samples the canvas, the browser has already cleared the buffer after compositing and both drawImage() and gl.readPixels() read back all-transparent.

Token page no longer restyles the whole document to read its dark colours (#169)

TemplatesTokensComponentsDocs
  • —app/(site)/foundation/tokens/page.tsx: the mount effect resolves every colour token in light and in dark by flipping data-theme on the root and reading getComputedStyle. Each flip changes inherited custom properties for all ~4,000 elements, and the read forces that restyle to run synchronously inside the hydration task. The effect now sets content-visibility: hidden on <body> for the flip (restyle skips a locked subtree; layout objects are kept) and restores it in a finally with the theme, and resolves against the root element, where every token is declared. Same tokens, same two reads, same state, same rows.
  • —Tried first and rejected: display: none on the body. It skipped the restyle but discarded and rebuilt the layout tree on restore, and measured far worse (median 478 ms at the repo setting).

KanbanBoard: columns of cards that move between stages (#166)

TemplatesComponentsDocs
  • —components/patterns/KanbanBoard.tsx, exported from components/patterns with applyKanbanMove and the types KanbanBoardProps, KanbanColumn, KanbanCard, KanbanMove. Props: columns (each id, title, optional limit), cards (each id, columnId, title, optional description and meta; order within a column is array order), onMove({ cardId, toColumnId, toIndex }) and label (accessible name, default "Board"). Controlled only: the board renders the cards it is given and reports a move; it keeps no copy of them.
  • —applyKanbanMove(cards, move) returns the cards after a move as a new array, so a consumer writes setCards((c) => applyKanbanMove(c, move)). toIndex is the position in the target column with the moved card taken out; beyond the end it clamps. An unknown card, or a card already at that position, returns the input unchanged. Pass the columns as an optional third argument to have a move to an unknown column refused too: from the cards alone an empty column and a missing one look the same.
  • —Three ways to move a card, all ending in the same onMove call. Drag it by its handle with mouse, pen or touch (Pointer Events with pointer capture; touch-action: none on the handle only, so a touch on the rest of the card still scrolls). Use the Move control on the card, the existing Select, which lists the columns and sends the card to the end of the chosen one. Or focus the card and press Alt with an arrow key: left and right go to the end of the previous or next column, up and down move one position. At an edge the key does nothing.
  • —While dragging, the card is dimmed where it is, a temporal line marks the insertion point and the column under the pointer is outlined in the temporal colour. The index comes from the pointer's height against the midpoints of that column's other cards. Escape, a cancelled pointer, a release outside the board and a drop where the card already is all call nothing.
  • —After a move, focus is on the moved card and a polite live region says where it went ("Reconcile ledger moved to Review, position 2 of 3"). Both answer what the cards prop holds in the update onMove causes, not the event: a move the consumer applies differently is announced as it actually applied it, and a move it refuses is silent. The request does not outlive that update, so a card that is shifted later by something else (another reader's card arriving above it) is never announced as this reader's move and focus is not pulled back to it. Apply the move in onMove itself; one applied in a later update is not announced.

WebGL charts draw wide-gamut primaries in colour, not white (#178)

TokensComponents
  • —components/finance/WebGLChartProvider.tsx: parseCSSColor now reads color(display-p3 r g b [/ a]) (numbers or percentages) and converts it to sRGB for the WebGL buffer, and reads rgb() in the space / slash form (rgb(255 128 0 / 50%)) as well as the comma form. Before, anything but hex and comma rgb() fell through to [1, 1, 1, 1], so on a wide-gamut display in light theme --ds-color-agency and --ds-color-temporal were drawn white by CandlestickGL, DepthChartGL, OrderFlowGL and VolumeProfileGL.
  • —A string the parser cannot read is no longer white. It returns UNPARSED_COLOR_FALLBACK (mid grey, visible on both surfaces) and, outside production, logs one console.warn per token and string naming both. It never throws inside the render loop.
  • —The token cache outlived a theme switch (clearTokenCache is called by nothing). The provider now watches data-theme and class on the root, clears the cache and changes resolveToken so the charts re-read their colours.
  • —components/finance/shared/display-p3.ts (shipped with the components; lib/color-contrast.ts now imports its transfer function and pattern from it, so one place still knows Display P3): displayP3ToSrgb(r, g, b) (Display P3 to linear, to XYZ D65, to linear sRGB with the CSS Color 4 matrices, re-encoded and clamped to 0..1) and parseDisplayP3Alpha(). One place knows the Display P3 transfer function; the provider imports it.
  • —tests/webgl-color-parse.test.ts and components/__tests__/webgl-provider-theme-cache.test.tsx. Red case on the previous parser: color(display-p3 1 1 1), 0 0 0 and 0.78 0.12 0.10 all gave [1, 1, 1, 1] (expected 1 to be close to 0.8520225931862091), and after a theme switch the cache returned [1, 0, 0] where [0, 0, 1] was expected.

Token page measures and labels Display P3 colours as Display P3 (#174)

TokensComponentsDocs
  • —lib/color-contrast.ts: the one colour-contrast implementation. luminance() reads #rgb, #rrggbb, rgb() / rgba() (comma form, opaque alpha) and color(display-p3 r g b) (numbers or percentages, opaque alpha); contrastRatio() returns the unrounded WCAG ratio. A translucent colour throws. Moved out of tests/case-study-contrast.test.ts, with the same maths.
  • —lib/token-color.ts: how the token page holds and shows a computed colour. computedColorToValue() keeps sRGB as #RRGGBB and Display P3 as color(display-p3 r g b); formatColorLabel() prints the hex for sRGB and P3 0.78 0.12 0.10 for Display P3; isColorValue() tells a colour from "".
  • —tests/color-contrast.test.ts and tests/token-color.test.ts: every accepted input form, P3 white 1 and black 0, 21:1 for black on white, translucent and unparseable input throwing, and the display formatter. Red case: on the previous converter, color(display-p3 0.78 0.12 0.1) gave #C71F1A and the test failed with expected '#C71F1A' not to be '#C71F1A'.
  • —/foundation/tokens: on a wide-gamut display in light theme the primaries are shown as P3 0.78 0.12 0.10 in the mono face instead of a hex. The kept value is valid CSS, so swatches, borders and the hover preview are painted with the real colour; the hover preview no longer drops its colour block for a non-# value. Every contrast badge comes from lib/color-contrast.ts; the page's own luminance and contrastRatio are gone. The primary palette grid is top-aligned so a two-line P3 label at 360px does not push neighbouring swatches down.
  • —tests/case-study-contrast.test.ts imports the shared functions; its cascade model stays in the test and its 15 tests pass unchanged.

High-contrast mode keeps its -aaa primaries on wide-gamut displays (#172)

Tokens
  • —tokens/bauhaus.css: the @media (color-gamut: p3) block is now scoped to :root:where(:not([data-theme="dark"]):not([data-contrast="high"])). With data-contrast="high" on the root in light theme, [data-contrast="high"] and the P3 block both had the specificity of :root and the P3 block came later, so a wide-gamut display got color(display-p3 0.78 0.12 0.10) and color(display-p3 0.16 0.25 0.78) in place of the -aaa primaries. It now gets #A01818 and #1A2FA0. :where() still keeps the rule at the specificity of :root, so a later consumer rule still wins.
  • —No sRGB value and no P3 value changed. Light with normal contrast still gets the P3 primaries; dark is unchanged.
  • —tests/case-study-contrast.test.ts models data-contrast on the root (its selector matcher knew only data-theme). New tests: in light and dark, sRGB and P3, with data-contrast="high", the three primaries resolve to their -aaa values and never to a display-p3 colour; every pairing the file checks reaches 7:1 in those four conditions (lowest: text-secondary on surface-raised, 7.73:1 light, and inverse text on agency, 7.96:1 light); and a fixture of the pre-fix block reproduces the failure while the fixed selector clears it. Red case: on the previous tokens the first two fail with light p3 high, --bh-red: expected 'color(display-p3 0.78 0.12 0.10)' to be '#A01818' and text-inverse on color-agency, light p3 high: 5.51:1.

CycleRail: a loop of stages that does not end (#167)

TemplatesComponentsDocs
  • —components/patterns/CycleRail.tsx, exported from components/patterns with CycleRailProps and CycleStage. Props: stages (2 to 8, each id, label, optional description), currentStageId, cycle (1-based), label (accessible name, default "Stage cycle") and orientation ("horizontal" default, or "vertical"). Display only: no handlers, no per-stage status, no size variants.
  • —Horizontal: the stages are equal cells in one row. Under the row the return path leaves the last cell, runs back and re-enters the first with an arrowhead, and the cycle count ("CYCLE 4", mono, tabular) sits on it. Vertical: the stages stack and the return runs up the left side with the count beside it.
  • —The live stage carries a temporal rule on its cell edge, the label in primary text and weight, and the word NOW, so it is never colour alone. Passed stages read secondary, stages ahead muted. A summary line above states it in words ("Cycle 4 · Stage 2 of 4 · Review"); with an unknown currentStageId nothing is marked and the summary shows the cycle only.
  • —A labelled group around an ordered list, aria-current="step" on the live item, the return-path drawing aria-hidden.
  • —Registered in registry-extended.ts (beta; related RunLifecycleRail, PaymentLifecycleTracker, OnboardingChecklist), a component page demo (mid-cycle, last stage, two stages, vertical), stories/patterns/CycleRail.stories.tsx, and components/__tests__/cycle-rail.test.tsx.

Dark theme keeps its own primaries on wide-gamut displays (#170)

TemplatesTokens
  • —tokens/bauhaus.css: the @media (color-gamut: p3) block that gives the three primaries richer Display P3 values is now scoped to :root:where(:not([data-theme="dark"])). It was a bare :root, which has the same specificity as [data-theme="dark"] and comes later in the file, so on a wide-gamut display it replaced the dark theme's --bh-red, --bh-blue and --bh-yellow with the light theme's P3 values. --ds-color-agency, --ds-color-temporal and --ds-color-validation are var() references to those three and are not redefined in dark, so they followed.
  • —In dark theme on a wide-gamut display, inverse text on temporal was 2.54:1 and on agency 3.57:1. Both are now the dark sRGB figures, 6.28:1 and 6.50:1. No sRGB value and no light P3 value changed.
  • —:where() keeps the rule at the specificity of :root. A plain :not() would have raised it above a later :root or [data-theme] rule, so a consumer theme that redefines a primary would have lost to this block on wide-gamut displays only.
  • —tests/case-study-contrast.test.ts evaluates every pairing in four conditions: light and dark, on an sRGB display and on a Display P3 one. It now applies each rule in tokens/bauhaus.css and tokens/semantic.css by specificity and source order, including rules inside @media (color-gamut: p3), and reads color(display-p3 r g b) with the Display P3 luminance row (0.22897, 0.69174, 0.07929). Percentage channels and an opaque alpha are read too; a translucent color throws, because its contrast depends on what is behind it. The transfer-function breakpoint is 0.04045 (it was 0.03928, which is the same for every 8-bit channel but not for a decimal P3 one). The earlier version read only :root and [data-theme="dark"], so it could not see this defect.
  • —Controls in that file: P3 white is 1 and black is 0; a neutral and sRGB red, each written in both encodings, give one luminance; a fixture with the old unscoped block reproduces 2.54 and 3.57 and the scoped one gives 6.28 and 6.50; no rule the model skips declares a token the pairings read. Red case: on the unfixed tokens the dark P3 assertions fail with 2.54:1 and 3.57:1; restoring :root, or using a selector the model cannot evaluate, or dropping :where(), each fail a test.

ApprovalCard: an importable component for nodus:approval-card (#165)

TemplatesComponentsDocs
  • —components/patterns/ApprovalCard.tsx, exported from components/patterns (and so from the package entry and @danagraph/design-system/patterns) with ApprovalCardProps, ApprovalCardConfidence and ApprovalDecision. Props are the spec fields (title, reasoning, consequences, confidence, agentSources) plus decision (controlled), onDecision('approve' | 'reject'), approveLabel, rejectLabel and disabled.
  • —Layout, top to bottom: agent sources (Tag) and confidence (ConfidenceMeter) in an overline row, the title as the heading, a Reasoning list, a Consequences list, then Reject (secondary) and Approve (primary). Sections without data are not rendered. A decided card shows "Approved" (outcome-positive) or "Rejected" (outcome-negative) as text in a polite live region at the left of the action row and disables both actions. The card is a role="group" named by its title.
  • —Registered in registry-extended.ts (beta; related ApprovalGate, ApprovalQueue, HumanInputRequest), a component page demo, and stories/patterns/ApprovalCard.stories.tsx (Full, Minimal, Approved, Rejected). nodus-ds add ApprovalCard installs it with ConfidenceMeter.
  • —components/__tests__/approval-card.test.tsx: every field present, the decided state (controlled and uncontrolled), title-only renders no Reasoning or Consequences, accessible name and real lists, and A2UISurface reporting (id, 'approve' | 'reject') including the title fallback.
  • —A2UISurface renders nodus:approval-card through ApprovalCard. onAction(id, 'approve' | 'reject') keeps its signature and its spec.id ?? spec.title fallback.

The generator page no longer shows CopilotKit's inspector or announcement (#157)

TemplatesTokensComponentsDocs
  • —/a2ui/generate on a production build showed an announcement popup ("AG-UI 1.0 is here, plus OpenBot and OpenMuse!") and a round inspector button at the top right. Both are one element: CopilotKit's <cpk-web-inspector> (@copilotkit/web-inspector), which fetches cdn.copilotkit.ai/announcements.json and renders the popup itself. The CopilotKit provider mounts it when enableInspector is true, and when that prop is left unset it falls back to "the hostname is localhost" (@copilotkit/react-core 1.66.4, dist/copilotkit-nRjRp2_5.mjs, shouldShowDevConsole and CopilotKit()). A production build served from localhost therefore got it; a deployed host would not have.
  • —generator-client.tsx now sets enableInspector to process.env.NODE_ENV === "development" and showDevConsole={false} explicitly. The inspector stays available under next dev by that choice, not by default.
  • —showDevConsole={false} also switches off CopilotKit's dev-mode error toasts and usage banner under next dev; errors remain in the console.
  • —tests/e2e/07-template-generator.spec.ts asserts cpk-web-inspector is absent from the page. Against the unfixed production build it fails (Expected: 0, Received: 1); after the change it passes.

Case study page describes what the Agent Chat page does (#158)

TokensComponentsDocs
  • —app/(site)/community/claude-api-case-study/page.tsx: the four headline stats ("<50ms first token render", "0 layout shifts", "4.5:1 contrast", "5 Claude API primitives") are replaced by four counts taken from the reference page and the repo (0 model calls, 6 scripted messages, 3 spec card types, 4 components); each label names what it counts. "5 Claude API primitives" is also removed from the site-wide metadata description in app/layout.tsx. Nothing in the repo measures render time or CLS, and lighthouserc.json does not cover the Agent Chat route.
  • —The architecture section is split in two. "What the reference page does" describes the scripted thread on timers; "Integration pattern (illustrative)" keeps the five-layer description, rewritten against the real props of StreamingArtifact, FunctionCallVisualizer and ContextWindowMeter and the real ModelSelector options list. Removed: claude-opus-4-6, tool_use blocks and stream event names, "reads usage.input_tokens", the 200k Opus figure and localStorage persistence, none of which the code does. Model names now match app/api/sandbox/route.ts (claude-haiku-4-5-20251001) and app/api/copilotkit/route.ts (claude-sonnet-4-6).
  • —The code sample imports from @danagraph/design-system (it named a package that does not exist) and uses the components' actual props; it is labelled illustrative. "Three steps to production" is now "Three steps".
  • —The token example renders the real ContextWindowMeter with labelled sample values instead of a hand-built bar.
  • —The accessibility table shows contrast ratios computed from the tokens in light and dark (17.72 / 14.79, 7.73 / 8.41, 7.29 / 6.28, 5.21 / 6.50) instead of "14:1" and "7:1". The rows are labelled sRGB, and a GAP row records that the @media (color-gamut: p3) override in tokens/bauhaus.css also applies in dark theme, where inverse text on the user bubble and the Approve button computes to about 2.5:1 and 3.6:1 on wide-gamut displays (the token itself is not changed here). The behavior rows now cite what the code does: aria-live=polite while streaming, native keyboard controls, the global reduced-motion rule, and one gap (the reference page's input sets outline: none).

Documentation is no longer a Tailwind source (#161)

TokensComponentsDocs
  • —next dev answered 500 on every page from 36cbc2f onward. Tailwind v4 scans every file in the project for class-like strings, and the #154 entry below, together with a note in docs/release-readiness/ledger.json, quotes a class pattern with a wildcard in it as documentation. Tailwind generated a rule for that pattern whose value is not valid CSS, and the dev server's CSS parser rejected the whole stylesheet. The production build tolerated the rule, so npm run build and the deployed site were unaffected and no gate noticed.
  • —app/globals.css now excludes docs/ and the root Markdown files from Tailwind's sources with @source not. Nothing the site renders takes its classes from those files.
  • —tests/tailwind-source-scan.test.ts compiles app/globals.css the way the site does and asserts the result parses, using a strict CSS parser with no error recovery; that is the condition the dev server failed on, so it covers any invalid generated value, not only this one. A second assertion checks for a var() with a wildcard in its name, for a readable failure. A third is a known positive: compiling a stylesheet whose only source is the offending class must produce CSS that does not parse, so the guard cannot pass because nothing is being generated. Against the unfixed stylesheet the first two fail (Unexpected token Delim('*'); expected [ 'var(--ds-radius-*)' ] to deeply equal []) and the known positive still passes.

All 20 composed templates install and compile; the 6 listed as installable never did (#109)

TemplatesComponentsDocs
  • —nodus-ds add, swizzle and add-template now copy a component to the path it has in the package (components/finance/RiskGauge.tsx lands in components/finance/), which is the path every @/components/... import of it is rewritten to. The destination used to come from the registry's category and name. Every finance/ and ai/ component is category ui, so it was written to components/ui/, and a module that exports several components (ChartInteractionLayer.tsx) was written under whichever name was asked for. 233 of the 373 component entries landed somewhere other than where their own import path points.
  • —nodus-ds add-template now installs the components a template's components import, dependencies first, not only the ones the template page imports. A component reached only through another component is left as it is when the consumer already has it. The template page and the components it imports directly are re-copied on every run, as before, so a local edit to one of those is overwritten. --dry lists the same set.
  • —scripts/generate-template-sources.ts and tests/add-template-eligibility.test.ts decide "installable" over that whole set. A dependency that is copyable itself but imports a component that is not now defers the template.
  • —Import-only edits to make the seven blocking components copyable; no rendered output changes. AgentIdentityBadge, ProvenanceChain, ConfidenceMeter, ConsentFlow and ApprovalQueue import the components they compose as @/components/ui/... (and @/components/finance/RiskGauge) instead of ../ui/..., the one cross-directory form the CLI can verify and rewrite. AgentConflictPanel needed no edit: it was blocked only through those siblings.
  • —lib/confidence.ts and lib/glossary.ts moved to components/patterns/, and lib/create-theme.ts to components/ui/create-theme.ts, beside the components that read them, because the package ships components/ and not lib/. All eight importing components follow (EvalRadar, ConfidenceSpectrum, AgentConfidenceBar, TrustScoreDisplay, MCPToolCall and MCPServerStatus as well as the blockers). lib/create-theme.ts stays as a one-line re-export, so the site, the stories and the package's createTheme export are untouched.

Token audit reports radius literals and Tailwind rounded classes (#103, supersedes #63)

TokensComponentsDocs
  • —scripts/audit-tokens.ts: a hardcoded-radius rule (P1) covering borderRadius/border-radius literals (px strings, unitless React numbers such as borderRadius: 2, and ternary values) and plain Tailwind rounded* classes (anything but rounded-ds-* and rounded-[var(--ds-radius-*)], which already consume the token). The literal check has no px exemption, which is why 2px and 3px radii were never reported.
  • —Flags --rule <name>, --scope literal|tailwind|all, --expect <n> and --expect-min <n>. --rule prints each hit with a suggested token, skips the registry write and sets the exit code from the count. The radius rule walks components/ instead of the registry list, so site chrome and shared chart tokens are counted.
  • —CLAUDE.md anti-pattern row and PR checklist line for radius; docs/quality-bar.md radius row now names the command instead of "GATE PENDING: B-03".
  • —compliance-registry.json and public/compliance-registry.json regenerated: the default audit now includes radius hits in each component's violations (passing 372 to 366, warning 3 to 9, failing unchanged at 1).
  • —Ledger: B-03 and R-003 are both done, acceptance is --expect-min 113.

The template generator can reach its runtime again (#148)

TemplatesTokensComponents
  • —app/api/copilotkit/route.ts: every POST to /api/copilotkit returned 500 {"error":"internal_error"}, including the runtime-info request /a2ui/generate sends on mount, so the page showed "Runtime info request failed with status 500" and the copilot never became available. The route now gives the runtime one BuiltInAgent (anthropic/claude-sonnet-4-6) whose prompt is the Nodus system prompt, and no longer passes a v1 middleware.onBeforeRequest or an AnthropicAdapter. The info request returns 200 and lists the default agent.
  • —tests/copilotkit-route.test.ts: calls the route handler directly against a stubbed provider. One test asserts the runtime-info request returns 200 with the default agent and costs no model call; the other runs the agent and asserts that the request reaching the provider carries exactly buildA2UISystemPrompt(). Both were red on the old route (500). With the prompt removed from the fixed route, the second alone fails.
  • —tests/e2e/07-template-generator.spec.ts: new test "template generator connects to its runtime". It waits for the page's own runtime-info response and asserts 200, a default agent, and no "Runtime info request failed" text. Red against the old route in both projects (expected 200, received 500) while the other six tests in the flow still passed; 8 of 8 pass after the fix, on next dev and on a production build served by next start.

Storybook access runbook (#111)

Docs
  • —docs/storybook-access.md: operator runbook for the /storybook licensed-access gate, derived from middleware.ts, lib/storybook-access.ts and its tests. It records how the gate decides, what the env var does when unset in production versus development, how to add, rotate and revoke a key, the exact requests and responses that verify a change, what the matcher does not cover, and a clearly marked proposal section on per-licensee keys.
  • —Links to the runbook from docs/consuming.md and docs/agent-integration.md.

Supply Chain Finance template shows its activation rate and ICP personas (#95)

TemplatesTokensComponentsDocs
  • —app/(site)/a2ui/templates/supply-chain-finance/page.tsx: the supplier activation rate (37.4%) is shown under the Enrolled Suppliers KPI and on the Enrollment tab. It is computed in code from ENROLLED_SUPPLIERS / APPROVED_SUPPLIERS (142 / 380 = 37.37%, rounded to one decimal), and the "not yet enrolled" count (238) is now derived from the same two constants instead of typed in.
  • —An "ICP personas (CPG) · illustrative" card at the foot of Program Overview with the three personas the issue named (Marcus Webb, David Osei, Sarah Okonkwo): role, company, focus (all from the issue), and the tab that serves each (an editorial mapping, not sourced from the issue). The names live in lib/demo-names.ts (icpTreasuryVp, icpFinanceVp, icpCfo) like every other demo person, and the page carries the "all names fictional" header.

Multi-Entity Consolidation: catalog entry now describes the shipped page (#96)

TemplatesTokensComponentsDocs
  • —lib/a2ui-templates.ts (multi-entity-consolidation only): the description, tab names and specs described a different, never-built design (5 entities, Meridian Corp, $3,850M revenue, Entity Overview / IC Eliminations / Consolidated P&L, entity-consolidation and minority-interest specs). It now describes the page: Summit Foods Group, 8 entities, $284.6M assets, 5 currencies, the four real tabs, and the issue's four spec labels (multi-entity-consolidation, intercompany-elimination, fx-translation, consolidation-adjustments).
  • —app/(site)/a2ui/templates/multi-entity-consolidation/page.tsx: the FX tab did not reconcile. The per-entity CTA rows summed to -$3.0M while the header card, the GBP/USD OCI card and the issue say -$2.4M, and the EMEA row translated 61.4 GBP to $77.8M although the entity table (and the 1.2682 spot rate) give $61.4M. EMEA is now 48.4 GBP to $61.4M with CTA -$1.8M, so GBP CTA is -$2.4M and the net CTA the page computes is -$2.4M.
  • —Persona David Osei, VP Finance, in the page header (named in the issue).

Regulatory Reporting template: page, catalog and search now agree (#97)

TemplatesTokensComponentsDocs
  • —app/(site)/a2ui/templates/regulatory-reporting/page.tsx: every derivative position count now derives from the one DERIVATIVES table (15 positions, 13 confirmed, 2 pending). The page previously said "142 open positions" in two places and "16 / 18 confirmed" elsewhere while the table showed 15 rows. The EMIR and Dodd-Frank (9 FX positions, 7 confirmed) tracker rows, the Filing Calendar summary cards (due this month, active rolling, due in 8 days, overdue) and the "fully compliant" count are computed from their rows instead of hard-coded.
  • —lib/a2ui-templates.ts: the regulatory-reporting catalog entry described Basel III COREP, EMIR UTI and DFAST content that is not on the page; its description and specs (nodus:regulatory-filing, compliance-tracker, derivatives-reporting, audit-trail) now match the page.
  • —lib/search-index.ts: the search description for the template is rewritten for the same reason.
  • —A "Viewing as Alex Rivera, Group Treasurer" line in the page header (the persona the issue asked for, owner of the on-time filing rate).

Storybook package family aligned on one version (#123)

TokensComponentsDocs
  • —package.json: storybook, @storybook/nextjs, @storybook/react, @storybook/addon-docs, @storybook/addon-a11y and @storybook/addon-themes are all declared ^10.6.2 (were ^10.3.6, ^10.4.6 and ^10.4.3 mixed). 10.6.2 is the newest 10.x at which every one of these packages is published (npm view <pkg> versions).
  • —package-lock.json: re-resolved with npm install --package-lock-only, then npm install. The diff is the Storybook family plus what it necessarily drags (a nested magic-string and semver under the webpack presets, probe-image-size/needle replacing image-size, a hoisted fs-extra). Other lock moves are forced or re-resolved by Storybook 10.6.2: oxc-resolver 11.24.2 to 11.21.2 (a downgrade; storybook pins it exactly), @testing-library/user-event 14.6.1 to 14.6.7, @jridgewell/sourcemap-codec 1.5.5 to 1.6.0, react-docgen 8.0.3 to 8.0.4, jsonfile 6.2.0 to 6.2.1 and css-loader 7.1.4 to 7.1.5.

Strict installs resolve with no peer-silencing overrides (#121)

TokensComponentsDocs
  • —package.json: @anthropic-ai/sdk ^0.55.0 to ^0.57.0 and typedoc ^0.27.9 to ^0.28.20. The three overrides that only silenced peer violations are removed: @copilotkit/runtime -> @anthropic-ai/sdk, typedoc -> typescript, and typedoc-plugin-markdown -> typedoc/typescript. The security overrides are untouched.
  • —package.json: one new override, typedoc -> brace-expansion ^5.0.8. typedoc 0.28.20 pulls minimatch@10, which imports the named export expand that only brace-expansion 5 has; the repo-wide brace-expansion ^2.0.3 security pin made npm run generate:docs crash at load. This is a functional scope, not a peer silencer.
  • —.npmrc: removed (it held only legacy-peer-deps=true), so a default install is strict and no longer rewrites the lockfile.
  • —package-lock.json: re-resolved with npm install --package-lock-only --legacy-peer-deps=false; the diff is the sdk, typedoc and its transitive moves only.

Template honesty, second pass: the residual stale copy (#104)

TemplatesTokensComponentsDocs
  • —public/press/media-kit.md, product-hunt-post.md, social-assets.md: these are the v1.2.0 launch kit as written on 2026-03-27 and are still served from the site. Their bodies are left as the record of what was said (233 components, 142 "production templates"). Each now opens with a dated correction note giving the current counts (376 components, 167 templates), saying the pages are reference implementations, that 20 of the 167 are composed and that only some of those are installable with nodus-ds add-template (the 77 patterns claim is corrected to 101 too). The installable count is deliberately not written into the press pages because it is not gated; the README states 6 as of 2026-10-12.
  • —docs/portfolio/NARRATIVE.md: "production-ready starting point" is now "reference implementation"; the unverified "most complete library ... in existence" line is replaced; the stale "77 patterns" is now the tagged count (101).
  • —docs/portfolio/AWWWARDS-SUBMISSION.md: "Each one is production-ready" is now "a reference implementation"; the "77-pattern grid" is now the tagged count (101).
  • —app/(site)/community/claude-api-case-study/page.tsx: the Agent Chat page is a standalone page (it imports no design-system components), so the claims that it was built from five DS components and "using only Nodus DS components" are replaced with what is true; "template" becomes "reference implementation" in the copy and links.
  • —README.md: the A2UI Template System section now says 20 of the 167 are composed, 147 standalone, and that 6 of the composed 20 are installable today (the other 14 depend on package-only components).

The generative-UI peer floor moves to @copilotkit/react-core 1.66 (#120)

TokensComponentsDocs
  • —package.json: the optional peer @copilotkit/react-core is now >=1.66 (was >=1.56). The repo's own @copilotkit/react-core and @copilotkit/runtime-client-gql devDependencies move from ^1.56.5 to ~1.66.0 (installed 1.66.4), so the package is built and tested on the 1.66.x line it declares as its floor (the repo overrides for katex and @a2ui/web_core stay in place). package-lock.json was re-resolved with strict peers, as on main.
  • —docs/consuming.md: new subsection stating the floor, why, and the katex override consumers must add.

The props table header stays in view under the site header (#91)

TemplatesTokensComponentsDocs
  • —components/site/ComponentPageTemplate.tsx: the desktop props table header row is now sticky (top-12, z-10) with an opaque var(--ds-surface-raised) background and a bottom rule drawn as an inset shadow. Body rows pass beneath it and never show through.
  • —The table wrapper changed from overflow-x-auto to overflow-x-clip, and type cells wrap (overflow-wrap: anywhere).

E2E checkpoints are behavioural by default; the screenshot assertion is parked (#131)

TokensComponents
  • —checkpoint() in tests/e2e/helpers.ts now waits for fonts and network idle and asserts the main content region is visible. It asserts toHaveScreenshot only when E2E_VISUAL=1. All 12 call sites are kept.
  • —.gitignore now ignores tests/e2e/results/ and tests/e2e/report/.
  • —playwright.e2e.config.ts header no longer claims snapshots are captured at every checkpoint.

The i18n E2E suite is skipped on purpose, not left timing out (#132)

TokensComponents
  • —tests/e2e/11-i18n-locale-switching.spec.ts now opens with a suite-wide test.skip(true, ...) whose reason names #132. The spec is kept, locators untouched. npm run test:i18n now reports 62 skipped in about 3s. Before, all 62 tests (31 per project) failed on origin/main; the per-test 15s timeout cost in #132 was taken from the issue and not re-measured here.
  • —components/ui/LocaleSwitcher.tsx is a button with aria-haspopup="listbox", labelled "Switch locale" by default, opening a popup ul[role=listbox]. It is not a <select>, so selectOption() cannot drive it even if the name matched; renaming it would only move the failure.
  • —lib/i18n/LocaleContext.tsx has no nodus-locale localStorage key, which the persistence tests read.
  • —/i18n renders the switcher with a no-op onChange.

A pre-push gate, because CI here does not gate

TokensComponentsDocs
  • —tools/git-hooks/pre-push + tools/git-hooks/install-hooks.sh + a README. The hook runs npm run gates:code (type-check, count sync, changelog sync, release idempotency guard, unit suite, Storybook theme check) before a push leaves the machine. Install with bash tools/git-hooks/install-hooks.sh.
  • —Branch protection is unavailable on the organisation's plan — GET /branches/main/protection returns 403 "Upgrade to GitHub Pro". There are no required status checks, so a red check has never blocked a merge.
  • —On 2026-10-08 the organisation's Actions minutes ran out (1,971 of 2,000) and every job was *refused* rather than run — zero steps, three seconds each. CI reported red on work that was correct, and would have reported nothing at all had anything been wrong.
  • —core.hooksPath is set to a RELATIVE path. The first implementation used git rev-parse --show-toplevel, which pinned the config — shared across every worktree of the repo — to one .claude/worktrees/<name> directory, and would have broken the main checkout outright once that worktree was removed. Git resolves a relative core.hooksPath against whichever working tree runs the hook, so one relative value is correct everywhere.
  • —Missing node_modules fails the push rather than skipping it. A gate that passes when it could not run reports success it did not earn. This repo has already been bitten by that exact shape: a build step that uploaded nothing stayed green for weeks while every job downstream of it failed.

Bound the E2E artifact upload so storage cannot refill

TokensComponents
  • —playwright.e2e.config.ts: video: "off" (was retain-on-failure) and trace: "on-first-retry" (was retain-on-failure). .github/workflows/ci.yml: the e2e-videos upload step is removed, since there is now nothing to collect, and a step reporting the results-directory size on failure is added.
  • —A conditional upload fires on every run when the suite fails on every run. The E2E suite is red on all 124 tests' worth of runs (62 i18n timeouts, every screenshot checkpoint), so if: failure() is effectively unconditional.
  • —A one-day retention cannot absorb fourteen runs in one day. 2026-10-08 saw 14 CI runs; at ~274MB per e2e-report that is 3.84GB, plus 0.81GB of videos — 5.06GB total, which exhausted the quota and then failed unrelated jobs at their own upload step *after* their real work had passed.

Show the visual-regression gap instead of hiding it

TokensComponentsDocs
  • —/foundation/agent-process now carries a failing Visual Regression — not measured — Chromatic retired; replacement under review row, rather than omitting the metric entirely as the Chromatic retirement did. Faramarz's call, and the better one: the quality table is a claim about what is verified, and silently dropping a row makes the table read as complete when a gate is missing. A visible red row is honest about the gap and creates pressure to close it. The renderer already colours a failing value with --ds-color-outcome-negative, so no component change was needed.
  • —For the record, the row previously read 153 snapshots — Chromatic baseline locked — passing, which was false on both counts: Chromatic never ran, and nothing in the repository successfully compares visual baselines. It stays failing until a replacement produces an actual green run, not merely a configuration.
  • —Actions artifact storage reclaimed: 5.06 GiB freed, 43 artifacts deleted, 0 remaining. e2e-report alone accounted for 3.84 GiB across 14 copies — 75% of the total — with e2e-videos at 0.81 GiB and nextjs-build at 0.41 GiB. The exhausted quota was failing the Build job at its Upload build artifact step *after* the build and dist-validation steps had passed, so unrelated green work reported red. Every deleted artifact belonged to a completed run; nothing from an in-flight run was touched, since nextjs-build is consumed by downstream jobs.
  • —This will refill unless the upload is bounded. The E2E suite still fails 62 i18n tests and every screenshot checkpoint, and each failing run retains a video and trace — which is how 376MB per run became 5 GiB. Reclaiming buys time, not a fix.

Retire Chromatic

TokensComponentsDocs
  • —.github/workflows/chromatic.yml, the chromatic devDependency and the chromatic npm script. There is no Chromatic subscription, and the workflow had never succeeded: 60 failures out of 60 runs, ending 2026-09-23. It could not have succeeded — gh secret list returns empty for both the repo and the danagraph org, so secrets.CHROMATIC_PROJECT_TOKEN resolved to nothing. It had already been reduced to workflow_dispatch-only on 2026-09-22 for a separate and still-valid reason: Chromatic publishes Storybook to an unauthenticated *.chromatic.com URL, which is exactly the content the /storybook access gate in middleware.ts protects. The README build badge goes with it.
  • —The repo had already flagged this as pending — docs/superpowers/specs/2026-09-05-private-package-release-design.md records "Chromatic, which has no token and has never run; retiring that workflow is a separate call."
  • —/foundation/agent-process asserted things that are no longer true, and one that never was. It listed "Chromatic" among the DevOps agent's domains, described a "Storybook/Chromatic integration", and claimed the ship phase "Runs CI, Chromatic snapshot review" producing a "Chromatic baseline updated". Those are corrected. It also published a quality metric reading "Visual Regression — 153 snapshots — Chromatic baseline locked — passing", which was false on two counts: Chromatic never ran, and nothing in the repository successfully compares visual baselines. That row is removed rather than restated, because a metrics table should not list a gate that is not measured.

Stop Vercel telemetry 404ing on every page, and drop Speed Insights (#127)

TokensComponents
  • —app/layout.tsx mounted <Analytics /> and <SpeedInsights /> unconditionally. Both load their script from /_vercel/{insights,speed-insights}/script.js, a path Vercel's edge serves ONLY on a Vercel deployment — so under next start, locally and in CI, each 404'd and put two console errors on every page. Every E2E spec asserts expect(criticalErrors).toHaveLength(0) after filtering favicon and net::ERR, so all ten flows failed on Received length: 2. <Analytics /> is now gated on process.env.VERCEL (read at render — layout.tsx is a server component, and the variable is set for preview and production builds alike). Off-Vercel the script cannot report anyway, so the gate costs nothing.
  • —Diagnosis note for the next person: the assertion collects msg.text(), which for a failed subresource is the bare string "Failed to load resource: … 404" with no URL. That is why a month of red CI never identified the resource. A page.on("response") listener names it immediately; an HTML-level sweep does not, because both scripts are JS-injected (0 bad refs across 640 HTML-referenced assets per page).
  • —@vercel/speed-insights and its <SpeedInsights /> mount. The service is no longer subscribed, so the component had nothing to report to; it was removed rather than gated. This repo was its only consumer. One package left the install tree.
  • —No visual baselines are committed at all. tests/e2e/snapshots/ is absent from the repository, so every checkpoint() fails with A snapshot doesn't exist … writing actual — roughly one per flow. Baselines cannot simply be generated locally: macOS font rendering will not match CI's Linux, so committing locally-produced PNGs trades one permanent failure for another. They need generating in a Linux environment.
  • —The i18n suite cannot find its control. tests/e2e/11-i18n-locale-switching.spec.ts (added in dad7bf5, Sprint 25, DAN-151) fails 48 tests with TimeoutError: locator.selectOption … waiting for getByRole('combobox', { name: /select language/i }). The language switcher it targets is either absent or not labelled that way. At 15s per timeout this cluster alone dominates the suite's runtime.

Drop eight dependencies the manifest declared but never imported (#122)

TokensComponentsDocs
  • —Root dependencies: @radix-ui/react-collapsible, d3-color, d3-format, d3-scale-chromatic, d3-time, d3-time-format. Zero import sites for any of them — the only references were the tsup.config.ts external list and historical changelog prose. README.md advertises a Collapsible component, but no such source file exists: the sidebar's CollapsibleGroup is a local function in components/site/SidebarNav.tsx and uses no Radix primitive. The six matching external entries are pruned with them.
  • —Root devDependencies: @types/d3 (every d3 module we import already has its own @types/d3-*, so the umbrella was redundant) and conventional-changelog-conventionalcommits (unreferenced; @commitlint/config-conventional carries its own).
  • —It does not shrink what consumers install. All six removed runtime deps still arrive transitively and were measured doing so: @radix-ui/react-collapsible via @radix-ui/react-accordion; d3-color via d3-interpolate; d3-format, d3-time, d3-time-format via d3-scale; d3-scale-chromatic via @copilotkit/react-core → streamdown → mermaid → d3. Exactly one package left the install tree: conventional-changelog-conventionalcommits. The issue's "5 shipped to consumers" framing described the manifest, not the resolved graph.
  • —What it does buy is a manifest that claims only what the source actually imports, so dependency review, upgrade pressure and version ranges stop being anchored to six packages no code touches.
  • —The packages/nodus-ds-native candidates from #122 (@babel/preset-env, @babel/preset-react, @babel/preset-typescript, @babel/plugin-transform-flow-strip-types, babel-preset-react-native, @react-native/jest-preset, @types/react-native, react-test-renderer) are untouched. That package is not an npm workspace and has no lockfile, so a root install leaves it without node_modules — npm test there exits 127, jest: command not found. Its removals cannot be proven safe until the package has a reproducible install, and its rn-ci workflow is failing independently. Left on #122.

Registry schema conformance and the attestation path (DANA-2298)

TokensComponents
  • —design.dana.xyz/registry.json did not conform to the registry-schema.json served beside it, and had not since the tiering landed: 1335 validation errors over 376 entries — path, tokens and source required on every entry and present on none, plus 207 stale-enum violations. The missing fields were deliberate (PR #64 moved them to the gated agent-catalog tier; registry-tiering.test.ts forbids them on the projection), so the schema was what was wrong, not the data.
  • —public/registry-schema.json is now GENERATED from registry-extended.ts and branches on the document's tier: public-projection requires the discovery fields and FORBIDS the gated ones; agent-catalog/demo-catalog additionally require path/source/tokens/props. The published schema now carries the licensing boundary instead of contradicting it. Stale enums (19 subcategories published against 28 real; 11 token families against 21) cannot recur — they come from the SSOT.
  • —Ten registry entries referenced design principles 8 and 10. There are exactly seven Nodus principles (01-07). The unresolvable values are dropped; in-range ones kept. Public projection unaffected — principles is a gated field.
  • —The attestation scripts resolved their state as <repo>/../../tools/data/ds-attestation at six independent call sites, which escapes the repository — the reason attestation:gate had NEVER passed and its CI job was retired. One resolution point now honours DS_ATTESTATION_DIR (default unchanged), writers mkdir their target, and the not-found message names the path and the env var instead of suggesting commands that could not have helped. The gate completes for the first time (218/218 passing). The CI job stays retired: nothing in CI runs attestation:run yet.
  • —lib/__tests__/registry-schema.test.ts — ajv-validates all four emitted catalogs against the published schema on every npm test, and asserts the gate is not inert in both directions (a projection entry carrying path must be rejected; an agent-catalog entry missing path must be rejected). ajv/ajv-formats become explicit devDependencies.

v1.27.0 — add-template: composed templates become installable (DANA-2276)

TemplatesComponents
  • —npx nodus-ds add-template <slug> (+ --list, --dry) — installs a composed template: page source with site-only imports rewritten (next/link → absolute anchor; site-styles/use-staged-reveal copied to templates/_shared/), then every component dependency driven through the existing add machinery. Standalone templates get an honest refusal pointing at the live gallery.
  • —Honesty gate outcome: 6 of the 20 composed templates are installable this release; 14 deferred with recorded reasons — each depends on at least one component add itself classifies package-only. Making those copyable unlocks the rest.

v1.26.0 — nodus-ds-mcp: MCP server over the licensed catalogs (DANA-2279)

TemplatesTokensComponentsDocs
  • —nodus-ds-mcp — a stdio MCP server shipped in the package (bin + dist/mcp-server.mjs): hand-rolled JSON-RPC 2.0 against spec 2025-06-18 (2024-11-05 fallback), zero new dependencies. Six read-only tools over the licensed catalogs: search_components, get_component (typed props, optional embedded source), list_templates, get_template, get_tokens, get_shadcn_item. The credentialed package IS the gate — no network surface, nothing anonymous. Wiring guide: docs/mcp-server.md; docs/agent-integration.md updated.

Agent integration guide (DANA-2280)

Docs
  • —docs/agent-integration.md — consumer guide for the machine contract: discovery vs licensed tier model, the four in-package catalogs with verified shapes, CLI usage, Storybook key flow, and three programmatic examples with executed outputs. Includes the importPath repo-specifier gotcha and an honest "not in the contract yet" list.

v1.25.1 — Public demo no longer bundles the licensed props catalog (DANA-2282)

Components
  • —The public /a2ui/generate demo's client chunks carried the full 376-component typed props catalog (licensed tier) via a2ui-prop-validate's static import. The site build now aliases that import to a generated registry/demo-catalog.json — the agent catalog filtered to the demo's actual 123-component menu — while the npm package keeps the full catalog (dist verified). scripts/check-demo-bundle.ts asserts the licensed tier marker is absent and the demo marker present in built chunks, and was falsified (alias disabled → check fails) before landing.

shadcn-compatible registry items, packaged (DANA-2279 rail scaffold)

Components
  • —registry/shadcn/ — a shadcn-CLI-compatible registry emitted from registry-extended.ts: committed root manifest (376 items) + gitignored, build-time-generated per-item payloads with embedded component source, derived npm dependencies and sibling registryDependencies. Validated structurally against schemas vendored byte-identical from ui.shadcn.com (dated 2026-09-23). Ships only inside the credential-gated npm package — no public route, nothing under public/. When the free-tier boundary is approved, serving a subset onto the npx shadcn add @nodus/... rail is a one-line change.

Templates catalog: component join keys for the composed set (DANA-2276)

TemplatesComponents
  • —registry/templates-catalog.json — generated from lib/a2ui-templates.ts + static import parsing of every template page, resolved against the agent catalog. 20 of 167 templates are composed (real DS-component compositions, per-template {name, importPath} join keys); 147 are standalone. Ships in the package at the licensed tier; owned by the idempotence gate; 7 tests enforce the contract.

v1.25.0 — Storybook becomes a licensed surface (GTM-HARDEN)

Docs
  • —middleware.ts gates /storybook and /storybook/* behind STORYBOOK_ACCESS_KEYS (comma-separated capability keys, Vercel env). A valid ?key= mints an httpOnly cookie scoped to /storybook (30 days) and strips the key from the URL; no/invalid key redirects to /license?from=storybook. Fail-closed in production when the env is unset; explicit dev-mode bypass so local workflows keep working. Helpers in lib/storybook-access.ts with 12 unit tests.
  • —Authorized bare /storybook and /storybook/ land on index.html (the static handler has no directory-index resolution).
  • —Chromatic auto-publish (push/PR triggers) DISABLED, workflow_dispatch only: Chromatic hosted the same Storybook at an unauthenticated *.chromatic.com URL — found by the adversarial review of this gate; it defeated the gate entirely. Re-enable checklist in the workflow file: set the Chromatic project private and audit old build URLs first.
  • —robots.txt disallows /storybook/; llms.txt and /license name Storybook as a licensed surface.

Fix Vercel production build: grade.ts catalog-names typing (GTM-HARDEN)

Components
  • —eval/grade.ts catalogNames() — the 1.24.1 array-shape handling produced TS2322 (string[] | null not assignable to string[]) under Vercel's build type check, erroring the production deploys for PRs #65/#66 while the site kept serving the older #64 deploy. Assignment now flows through a typed local; tsc --noEmit and the grade test suite verified before merge.

Changelog-drift gate (DANA-2278)

TokensComponents
  • —scripts/check-changelog-sync.ts (npm run check:changelog, in gates:code and the CI lint job): fails when lib/changelog-data.ts CURRENT_VERSION differs from package.json, when CHANGELOG.md lacks a heading for the current version, or — in --range mode — when a diff touches components//tokens/ without touching both changelog files. Falsified in both directions before landing (broken version → exit 1; component-touching probe commit without changelog → exit 1).

Template honesty: reference implementations, not production templates (DANA-2276)

TemplatesComponentsDocs
  • —All commercial copy (README, docs/portfolio submissions and case studies, social copy) now calls the 167 A2UI pages "reference implementations" — measured ground truth: 147 of 167 import zero design-system components; they are standalone demonstration pages, not installable compositions. README states this explicitly.
  • —Stale portfolio-doc counts (233 components / 77 patterns / 134 templates, v0.3.0) corrected to current marked counts; check:counts coverage grew from 22 to 46 tagged counts.
  • —Unverified market superlatives removed ("world's first", "most complete library available anywhere") per the fact-verification protocol.

v1.24.1 — Public-surface hardening (GTM): a2ui projection, tarball trim, robots/llms.txt

ComponentsDocs
  • —public/a2ui-catalog.json is now generated by scripts/generate-registry-json.ts at projection tier — 376 components, discovery fields only. Previously a hand-edited 25-component stub with no owning generator (flagged in the v1.23.0 entry) that exposed typed props schemas anonymously; that was the last public props surface.
  • —npm tarball no longer ships the docs-site chrome (components/site/), components/__tests__/, or *.test/*.stories files — files negation globs; 456 → 425 files, 909 → 798 KB. Guarded by the updated tests/subpath-exports.test.ts exact-array assertion.
  • —public/robots.txt and public/llms.txt — llms.txt names the discovery-tier surfaces (registry, a2ui catalog, compliance registry), the license terms, and where the full typed catalog lives (inside the credentialed npm package).
  • —lib/__tests__/registry-tiering.test.ts now asserts the a2ui projection covers every component and leaks no implementation fields.

v1.24.0 — Tiered registry: public projection + full agent catalog (GTM Phase A)

TokensComponents
  • —registry/agent-catalog.json — the full typed catalog (every component's props schema, source path, a11y notes, token bindings) plus a new derived importPath field (@/components/...), generated from registry-extended.ts by the same generator that owns public/registry.json. Ships inside the npm package (package.json files), so the GitHub Packages credential gates it — no new auth surface.
  • —lib/__tests__/registry-tiering.test.ts — enforces the tiering contract in both directions: the public projection carries no implementation fields (props, path, importPath, tokens, accessibilityNotes, mobile, principles, source), and the agent catalog is a strict superset of the projection (same component set, every public field equal, every path resolving on disk). The gated surface can never drift thinner than the free one.
  • —public/registry.json (anonymously served at design.dana.xyz/registry.json) is now a trimmed discovery projection — name, slug, category, subcategory, description, tags, variants, sizes, status, since, version, interactive, related — with a tier: "public-projection" marker and a pointer to the full catalog. 665KB → 191KB; the typed props schema and source paths are no longer anonymously fetchable.
  • —Full-field consumers repointed to registry/agent-catalog.json: lib/a2ui-prop-validate.ts, the nodus-ds CLI manifest loader (keeps a fallback to the public path so older package layouts never crash), scripts/generate-render-registry.ts, scripts/audit-tokens.ts. smoke-test.ts and check-count-sync.ts keep reading the projection (slug/category/counts only).
  • —scripts/check-generated-idempotent.ts: the registry generator now declares both artifacts in writes.

v1.23.0 — License-gated artifacts, powered-by badge, ask-AI entry points (DANA-2273)

TokensComponents
  • —license/terms fields on every public machine-readable artifact — public/registry.json, compliance-registry.json, public/compliance-registry.json (via their generators, single-sourced from the new lib/license.ts) and public/a2ui-catalog.json (direct edit; no generator owns it). registry-schema.json documents both fields as optional. Terms URL points at the new /license page.
  • —/license — evaluation-grant terms aligned to the root LICENSE file; commercial licensing by written agreement; no commercial terms published.
  • —/community/badge — "Built with Nodus Design System" attribution badge (PoweredByBadge, site-only) with live preview, plain-HTML embed snippet with token fallbacks, and a copy button (CopyEmbedButton, clipboard-safe with visible-snippet fallback).
  • —Homepage "Ask your AI about Nodus DS" section — ChatGPT / Claude / Perplexity buttons opening a short, neutral, URL-encoded prompt.

Idempotence gate can now prove an artifact was written (GH-41)

TokensComponentsDocs
  • —ARTIFACTS and GENERATORS were unlinked lists, and the only evidence the gate collected was git status --porcelain. A never-written artifact and a correctly-reproduced one give that command the identical output — empty — so a generator that silently stopped writing a tracked file still counted toward generators reproduce HEAD exactly. The gate passed having verified nothing about that artifact. The reproducible instance is scripts/audit-tokens.ts:299-301, which mirrors public/compliance-registry.json only when run without --output.
  • —Each generator now declares the artifacts it owns in a writes field, and ARTIFACTS is derived from those declarations rather than maintained as a second list free to drift out of correspondence.
  • —The check runs in two passes. [1/2] idempotence is the previous behaviour unchanged — generators run against the committed tree and the result is diffed against HEAD byte-for-byte. [2/2] coverage deletes each artifact immediately before the generator that owns it runs and requires it to exist again afterwards; a missing one fails loudly, naming the artifact and the generator command expected to produce it.
  • —Deletion rather than mtime, because four of the six generators write through scripts/write-if-changed.ts, which by design does not touch the file when content is unchanged. An mtime check would report those four as "not written" on almost every healthy run. write-if-changed always writes when the previous read fails, so deleting first makes existence afterwards proof of a write instead of an inference from one.
  • —Two passes rather than one, because forcing a write is what makes the two claims incompatible: four artifacts embed a generation timestamp that write-if-changed normalises away when comparing but still emits on a real write, so a forced rewrite re-stamps them and they then differ from HEAD by the timestamp alone. Running coverage separately against a restored tree keeps pass 1 byte-exact instead of teaching the gate every generator's volatile-value patterns. Cost is one extra generator run — locally 13s to 20s.

Release-readiness loop (DANA-2256): font parity between site and Storybook (B-02)

TokensDocs
  • —R-002: next/font in app/layout.tsx defines --font-inter/--font-jetbrains-mono/--font-dm-serif on the site root only; Storybook never renders the layout, so tokens/bauhaus.css fell back to a locally installed Inter or system-ui and stories did not look like the site. .storybook/preview-head.html now bridges the same three variables via a Google Fonts stylesheet (Inter's opsz axis mirrors layout.tsx) until an @fontsource self-hosted install is affordable.
  • —app/globals.css code, pre used a hardcoded 'JetBrains Mono', 'Fira Code', monospace literal instead of the token, so site code blocks could drift from every other MONO-styled element. It now reads var(--ds-type-mono-font).
  • —Added scripts/probe-font-parity.ts (npm run probe:fonts), which opens one site route and one story and asserts the three families actually resolve — checking the --font-* custom property and document.fonts registration rather than the rendered font-family string, since tokens/bauhaus.css deliberately names the same family as its own fallback text, which would otherwise pass whether or not the bridge exists.

Release-readiness loop (DANA-2256): Storybook defaults to light (B-01)

TokensDocs
  • —R-001: Storybook opened in dark theme while the site defaults to light (app/layout.tsx only sets data-theme=dark from localStorage), so the dark-only defects tracked as GH-7 and P-0040 showed first for anyone opening Storybook cold. .storybook/preview.ts defaultTheme is now "light" and .storybook/nodus-theme.ts is rebuilt on base: "light" with the light block of tokens/semantic.css (surface, text, border, and toolbar colours) instead of the dark hex it had baked in.
  • —Added scripts/check-storybook-theme.ts, wired into gates:code, which fails with the mismatched file and value whenever either the preview decorator or the manager theme drifts back to dark.

Release-readiness loop: ledger, quality bar, daily program (DANA-2256)

TokensDocs
  • —docs/release-readiness/ledger.json: the single backlog for the private release — the 54 findings of the 2026-09-05 polish audit (the five closed by DS #21 marked done, the four on polish/C-02-1 marked in-review), open issue #7, and the three defects verified on 2026-09-09: Storybook defaults to dark while the site defaults to light (.storybook/preview.ts, .storybook/nodus-theme.ts); Storybook and site code blocks never receive the next/font families (app/layout.tsx scopes the variables to the site root; app/globals.css names 'JetBrains Mono' literally); and 26 borderRadius: "2px" literals plus 35 Tailwind rounded* classes bypass the radius tokens, unreported because scripts/audit-tokens.ts exempts px values up to 3. Batches B-01 to B-18 in priority order, each with the files it may touch and an acceptance command. Schema in docs/release-readiness/README.md.
  • —docs/quality-bar.md: every rule the system holds itself to, paired with the command that proves it; rules without a gate yet are marked with the batch that adds one.
  • —docs/superpowers/specs/2026-09-09-release-readiness-loop-design.md and docs/superpowers/plans/2026-09-09-release-readiness-loop.md: the design and the task-by-task plan the daily loop executes. The loop itself (/ds-release-daily, launchd com.nodus.ds-release-daily) lives in the lab monorepo; it takes one batch per day, ships it as a PR with the local review recorded, never merges, never bumps the version.

Idempotence gate now compares against HEAD (GH-36 follow-up)

ComponentsDocs
  • —The check introduced in #38 compared two consecutive generator runs to each other. Both runs share one clock, so volatility coarser than a single run — a date-only stamp, an hour, a git SHA — was invisible to it. The pre-#35 stress-test report stamped a date-only Generated: line and would have passed that check every time while still dirtying the tree once a day. It also could not see a stale committed artifact at all, which is exactly what instance 1 (#28) was.
  • —It now runs the generators once and compares the result against what is committed, which catches both non-determinism and staleness.
  • —The check mutates the working tree and restores the artifacts before exiting; a restore that fails is a hard failure, since an artifact that cannot be restored was also not verified.
  • —There is deliberately no dirty-tree precondition, so any uncommitted edit to any of the seven artifacts is lost — whether present when the check starts or made while it runs. write-if-changed does not protect them: a hand-edited file differs from the generated content and is therefore rewritten. Do not hand-edit these files and expect the edit to survive. Recorded on #36.
  • —An artifact not tracked in HEAD now fails before any generator runs. Such a path yields an empty git status --porcelain, indistinguishable from clean, so it was previously counted toward the success line while being checked against nothing.

Idempotence gate no longer fails under NODE_ENV=production (GH-36)

Components
  • —scripts/check-generated-idempotent.ts pins NODE_ENV=test for its vitest generator. The gate added in #38 ran clean locally but failed in CI: both callers of gates:package set NODE_ENV: production (.github/workflows/ci.yml Package job, .github/workflows/release.yml Gates step), under which Vite externalizes node: builtins for the browser and scripts/stress-test-props.test.tsx dies on load with No such built-in module: node:. As merged, #38 would have failed the Package job on every PR and blocked every release.
  • —The override is scoped to that one generator rather than applied globally, so the other five keep producing output under the environment they would normally see — a global override could change what they generate and quietly invalidate the comparison.
  • —execFileSync now sets an explicit maxBuffer. Node's 1 MB default surfaces as ENOBUFS, which this script reported as ✗ generator failed — blaming a generator for the volume of its output rather than for anything wrong with it.

Generated artifacts are now checked for idempotence (GH-36)

Components
  • —scripts/check-generated-idempotent.ts runs every generator twice and fails if any tracked artifact it produces differs between the two passes. Wired into gates:package, and therefore into release:gates, so it runs on every release.
  • —The invariant is idempotence, not correctness — the check does not care what a generator produces, only that producing it twice changes nothing. That needs no fixtures, costs ~19s, and catches the whole defect class rather than one instance of it.

Compliance audit no longer rewrites 377 lines of timestamps (GH-36)

TokensComponents
  • —scripts/audit-tokens.ts stamped both a top-level generatedAt and a per-component lastAuditDate on every run, so npm run audit:tokens rewrote 377 lines of pure timestamp churn even when no score changed. A genuine compliance regression would have been one meaningful line among them — the worst of the four instances catalogued in #36, since the previous three produced a one-line diff.
  • —Both copies now write through scripts/write-if-changed.ts, normalising only the timestamp _values_. The dates consequently record when the audit result last changed rather than when the audit last ran, which is the more useful reading. The --output scratch-run behaviour introduced earlier is unaffected: a scratch audit still writes only where it was told and leaves the served copy alone.

Stress-test report no longer dirties the tree on every test run (GH-30 follow-up)

Components
  • —scripts/stress-test-props.test.tsx rewrote scripts/stress-test-report.md on every run with a fresh Generated: date, so npm test left a tracked file modified with a one-line, semantically empty diff. This is the same defect GH-30 fixed for the two prebuild generators; that change was scoped to prebuild and correctly described, but this third generator sits in the test suite and was not covered.
  • —The report now writes through the same scripts/write-if-changed.ts helper, with a pattern matching only the date value so an edit to the rest of that line is still detected. Verified in all three directions: two consecutive runs leave the tree clean, a real content change is still written, and a date-only difference is left alone.

LICENSE grant bounded to evaluation

TokensComponents
  • —The LICENSE made the software "available only to parties granted access by Dana, for evaluation and use within their own products", with no term, no fee obligation and no seat or application limit. On a plain reading anyone holding a package credential had a perpetual, free, unlimited production licence — the opposite of the intended commercial posture, and the first published artifact would have carried it.
  • —The grant is now bounded to evaluation in a non-production environment. Production use requires a separate written agreement, and the text says explicitly that no such agreement arises from the notice itself, from access to the software, or from possession of registry credentials — closing the reading under which issuing a token conveyed production rights.
  • —Evaluation and production are separated by _environment and purpose_, not by who the end users are. An earlier draft defined evaluation as having "no end users other than the organisation's own personnel" while defining production use as any application "made available to end users, whether internally or externally" — which placed the same internal-staff use simultaneously inside the grant and inside the clause requiring an agreement. The grant also now names contractors and continuous-integration environments explicitly, since an evaluation carried out by an agency or on third-party CI would otherwise have fallen outside it.
  • —Added a term (evaluation ends on revocation of access, with no right of renewal and no entitlement to a commercial licence), and extended the warranty disclaimer to state that accessibility and compliance information shipped with the software is informational rather than a certification.
  • —The copyright holder now reads "Faramarz Hashemi, trading as Dana". "Dana" alone is an unregistered trade name, which is not a legal person and cannot hold copyright or be a party to an agreement.

Release workflow no longer guesses when the registry is unreachable (GH-29)

TokensComponents
  • —release.yml decided whether to publish by pre-checking with npm view, discarding its output with >/dev/null 2>&1. Any failure — a 5xx, an auth blip, a timeout — was therefore indistinguishable from "not published yet" and fell through to npm publish, which hard-failed with EPUBLISHCONFLICT on a version that was already there. A recoverable error became a red release, in the step whose whole purpose is re-run safety.
  • —The pre-check is gone rather than repaired. A pre-check cannot answer the question reliably: GitHub Packages returns 404 for an unauthorised read of a private package, so a token problem looks exactly like "not published yet". The workflow now attempts the publish and tolerates only the specific "this version already exists" rejection, which the registry answers unambiguously.
  • —The "GitHub Release with tag" step had the same gh release view pre-check and now uses the same helper.
  • —The logic lives in scripts/release/idempotent.sh, called by both steps, with scripts/release/idempotent.test.sh covering fifteen cases — success, three flavours of already-exists, and five failures that must surface (401, 403, 404, 500, timeout). The tests run the real script against fake commands, so what is exercised is exactly what the workflow executes. Wired into gates:code, since GitHub Actions is paused on this org and unrunnable release shell is otherwise checked by nothing. The tolerated-rejection patterns are anchored to the registry's own error identifiers rather than the phrase "already exists": the step's combined output includes everything npm publish's lifecycle scripts emit, so build noise such as EEXIST: file already exists would otherwise have marked the step green _without publishing_ — a silent failure worse than the bug being fixed, since the next step tags the version anyway and version-check.ts then reports is_new_version=false forever.

README and portfolio counts corrected, and put under the guard

TemplatesComponentsDocs
  • —README.md claimed 142 production-ready templates in one place and 134 in another while the real figure is 167. docs/portfolio/AWWWARDS-SUBMISSION.md claimed 233 components against a real 376 — understating the library by 143 — and its own layer breakdown did not add up to its stated total (73 + 111 + 77 = 261, not 233). All are now derived figures carrying ds-count markers.
  • —The source-tree listing in README.md carried four stale directory counts (45 / 42 / 109 / 134). These are file counts, deliberately distinct from the registry-derived headline stats, so they are corrected to the real 52 / 94 / 175 / 167 and relabelled as source files and template directories rather than silently conflated with the component counts above.
  • —README.md said "Licensing terms have not been finalized" and "not published to npm" although a proprietary LICENSE is present and distribution is via GitHub Packages. The banner now states what is true: proprietary and licensed, with commercial pricing not yet published.
  • —The copyright line said "Copyright Dana (Apadana Holdings)", an entity deprecated in the brand architecture and contradicting LICENSE. Both now read "Copyright (c) 2026 Faramarz Hashemi, trading as Dana".
  • —scripts/check-count-sync.ts now scans docs/portfolio/ wholesale in addition to README, GUIDE and the design-system log. Those buyer-facing documents had drifted precisely because nothing checked them, and new files added there are picked up automatically rather than needing to be registered by hand. 22 tagged counts now verify against lib/ds-stats.ts.

Generated artifacts no longer dirty the tree on every build (GH-30)

TokensComponents
  • —scripts/generate-registry-json.ts and scripts/build-tokens.ts embed a wall-clock timestamp in their output and rewrote it on every run, so any npm run build left public/registry.json and public/tokens/semantic.v2.css modified with a one-line, semantically empty diff. Real uncommitted work hid in that noise, and since public/registry.json ships in package.json files, identical source produced a different published tarball on every build.
  • —Both now write through scripts/write-if-changed.ts, which compares against the file already on disk with the timestamp normalised away and skips the write when nothing else differs. The timestamp now records when the artifact last actually changed rather than when a build last ran. The other two prebuild generators were already byte-stable and are untouched.
  • —The normalisation patterns match only the timestamp _value_, never its surrounding line. A pattern that swallowed the whole line would also mask any future edit to that line, so the artifact would silently stop updating — verified by adding text to the generated header and confirming the write still happens.

Compliance registry regenerated and wired to the site

DAN-139TokensComponentsDocs
  • —public/compliance-registry.json — the copy the token-compliance dashboard actually fetches — was generated 2026-04-06 and described 335 components while the library had grown to 376. It reported overallCoverage: 100, which read as a full-coverage audit result but was 335 of 376, or 89%. The repo-root copy had separately been regenerated on 2026-07-04 at 360 components and never propagated, so three different component counts were in play at once (335 served, 360 generated, 376 real).
  • —scripts/audit-tokens.ts writes to the repo root, but app/(site)/foundation/token-compliance/page.tsx:324 fetches /compliance-registry.json, which Next.js resolves to public/. The two were never wired together, so every regeneration since April stayed invisible to the site. scripts/audit-tokens.ts now writes both copies itself, which is what the page's own doc comment already claimed happened. The sync lives in the script rather than as a shell cp in the npm script, so --output still works and a scratch audit (npm run audit:tokens -- --output ./tmp/reg.json) writes only where it was told and leaves the site copy alone.
  • —Both copies regenerated against the current 376-component registry. Honest numbers replace the round one: 372 passing, 3 warning, 1 failing, 99% coverage. The four components below the passing threshold are PaymentQueueTable (56), ProvenanceFrame (75), UnknownComponentFallback (75) and RunLifecycleRail (85); they were previously absent from the published artifact rather than passing it.

Production polish program (DANA-2256)

TemplatesComponentsDocs
  • —The site header rendered both the desktop nav and the mobile hamburger between 768px and 1023px, forcing horizontal page scroll on every route at 768px; the desktop nav now switches in at the same lg breakpoint the mobile menu switches out (components/site/SiteShell.tsx). (P-0007, P-0014, P-0026, P-0005)
  • —The header's GitHub link, theme toggle and search control were below the 44px touch-target minimum on mobile; they now measure 44×44. (P-0030)
  • —The component-page feedback widget wrapped a 28px button in a decorative 44px box, so the tappable area did not match the visual one; the button itself is now the 44px control (components/site/ComponentPageTemplate.tsx). (P-0031)
  • —The homepage component showcase, the homepage "Templates ready to ship" grid, and the why-nodus "Not for every team" grid used repeat(auto-fill, minmax(...)) on a grid container whose background paints the 1px gap lines; at 1280px width the item counts didn't fill every auto-fill track, so the leftover tracks exposed the container background as a solid gray/black box next to the real content. Switched to auto-fit, which collapses unused tracks instead of reserving them (app/(site)/_components/component-showcase.tsx, app/(site)/page.tsx, app/(site)/why-nodus/page.tsx). (P-0001, P-0002, P-0006, P-0019)

v1.22.2: Fix dark-mode contrast failure on outcome -fg tokens (DANA-2051)

TokensComponents
  • —--ds-color-outcome-positive-fg / -caution-fg / -negative-fg were pinned to white unconditionally by the dark-mode token deriver's generic fg-on-color rule ("keep as-is"). That rule assumes the paired accent stays dark enough for white text — it doesn't for these three: their OKLCH-derived dark-mode values (#75b975 / #ff7f4e / #ff796e) are pastel-light, so white text measured ~2.4-2.6:1 contrast, failing WCAG 1.4.3 outright.
  • —scripts/build-tokens.ts: added a MANUAL_DARK override for all three tokens to #18181B in dark mode — matches the existing --ds-color-validation-fg precedent (dark text on a light accent). Light mode is untouched (white still passes 5:1+ there). Dark-mode contrast now ~6.9-7.6:1 across all three tiers.
  • —Two real consumers inherited the bug and are now fixed for free: the ledger /reporting urgency chip (which was also referencing a phantom --ds-color-text-on-accent token that doesn't exist anywhere in the DS — fixed in apps/ledger, tracked under DANA-2051) and components/ui/APIKeyDisplay.tsx's copied-state text color.
  • —Surfaced during a DANA-2031 dieter re-review of the ledger /reporting route.

v1.22.1: Fix dist/cli.mjs Dynamic require crash (DANA-2145)

Tokens
  • —dist/cli.mjs threw Dynamic require of "events" is not supported from commander/lib/command.js on load — the bundled CJS dep's internal require('events') hit esbuild's ESM-output require shim, which throws for anything it can't statically resolve. bin/nodus-ds's import(...).catch(() => tsx fallback) masked this since publication; the compiled bundle had never actually run.
  • —tsup.cli.config.ts: added a banner that sets globalThis.require = createRequire(import.meta.url) so the shim's typeof require !== "undefined" check delegates to a real require instead of throwing. (A local const require was tried first — esbuild renamed it to avoid an internal name collision, which defeated the check; globalThis.require isn't a local binding esbuild can rename.)
  • —tsup.cli.config.ts: added explicit target: "node22" — tsconfig's ES2017 target was leaking into esbuild and silently emptying import.meta.url (needed by both scripts/cli.ts and the new banner).
  • —bin/nodus-ds: now spawns node dist/cli.mjs as a child process instead of import()-ing it in-process. Importing made bin/nodus-ds the file in process.argv[1], which fails cli.ts's direct-invocation guard (import.meta.url === pathToFileURL(process.argv[1]).href, added to keep the CLI side-effect-free when imported by tests) and silently no-op'd instead of running.
  • —Verified with the ticket's repro command (node dist/cli.mjs add --list) plus bin/nodus-ds add --list (compiled path) and a bundle-removed run of bin/nodus-ds tokens --dry (tsx fallback still works).

v1.22.0: Evaluator page + IA nav overhaul + homepage truth repairs (DANA-2168)

TemplatesTokensDocs
  • —New page /why-nodus — 8-section evaluator journey: positioning header with opening argument, agent-ops evidence grid (Sovereignty/Protocol/Provenance/Generative-UI), stats bar sourced from ds-stats constants, build/buy economics table with semantic left-border accents and [AI ESTIMATE] footer, honest-scope disqualifier grid (3 cells), semantic color law swatch grid (text-labeled, token-referenced), three living-proof template cards linking to verified routes, and early-access CTA terminal block on --ds-surface-inverse.
  • —{ title: "Why Nodus", href: "/why-nodus" } added to Getting Started sidebar after Overview.
  • —Top nav (SiteShell.tsx NAV_LINKS): added "Agentic" → /foundation/agentic-design-language at slot 3; removed "Charts" (/charts) and "Changelog" (/changelog). Net: 8 → 7 items.
  • —Homepage (app/(site)/page.tsx): Executive door href changed from /awards → /why-nodus; Executive door description rewritten to "The case for adoption — what Nodus DS is, what it is not, and how to get access."; Developer door description rewritten to set accurate install-step expectations.
  • —Homepage DIFFERENTIATORS const: added href field to all 3 entries (/foundation/agentic-design-language, /foundation/tokens, /finance); JSX render now shows "See the evidence →" arrowLink per entry.

v1.21.0: ComponentPageTemplate five-tier spacing hierarchy overhaul (DANA-1837)

TemplatesTokensComponents
  • —Outer container: space-y-10 (40px uniform) removed; replaced with flex flex-col and explicit marginTop per section. The uniform 40px gap between every section child erased all information hierarchy — Header-to-Import had the same gap as Props-to-Metadata. The spacing itself now encodes tier.
  • —Spacing map: three rhythm zones — 16px (--ds-space-stack-sm, same-tier adjacency), 32px (--ds-space-stack-md, tier transition), and 0px (fused sub-sections within a tier).
  • —Props section: marginTop: var(--ds-space-stack-md) (32px — the largest gap on the page, creating gravitational pull into the Tier 1 apex); padding 20px → var(--ds-space-inset-md) (token compliance).
  • —Accessibility section: marginTop: 0 explicit (border-top handles the internal division); padding 20px → var(--ds-space-inset-md). Props and Accessibility now fuse into one Tier 1 "implementation contract" block — isolated A11y notes read as optional; fused they read as mandatory.
  • —Metadata strip: marginTop: var(--ds-fin-gap-xl) (16px — annotation-close to the content above); Metadata overline label removed (tautological — the three pill visual treatments already carry category encoding); paddingBottom 20px → var(--ds-space-inset-md).

v1.20.1: Live demos for the 8 finance components registered in v1.20.0 (DANA-2164)

ComponentsDocs
  • —The 8 finance components registered in v1.20.0 (BarChart, ClearingNetworkIndicator, ForecastRow, HeatmapChart, OrderBookRow, PaymentQueueTable, PaymentStatusBadge, ScatterChart) had registry entries and Storybook stories but no lib/demo-registry.tsx entries, so their site component pages fell back to "Demo coming soon". Each now has a live demo, following the existing inline finance-demo convention (finance demos live directly in demo-registry.tsx; the lib/demos/ modules cover dataviz/sovereignty/interaction/protocol batches only).
  • —Demo data mirrors each component's stories/finance/*.stories.tsx scenarios but is hydration-safe and mock-only: fixed values in place of the stories' Math.random() (the HeatmapChart FX correlation matrix is a fixed symmetric 5×5), fixed value dates, and demo entities (Meridian Bank, Northwind Capital, Eastern Corp, Atlas Bank, Kaito Securities, Harbor Global) in place of the stories' real bank names.
  • —ForecastRow and OrderBookRow render as <tr> sub-components, so their demos wrap them in the same <table> chrome their Storybook decorators use — valid markup on the site page.

v1.20.0: 8 orphan finance components registered + Storybook docs-gap policies (DANA-2144/DANA-2124)

TemplatesComponentsDocs
  • —Registry entries for the 8 previously-orphaned finance components (BarChart, ClearingNetworkIndicator, ForecastRow, HeatmapChart, OrderBookRow, PaymentQueueTable, PaymentStatusBadge, ScatterChart) — category ui / subcategory finance, status stable, with props transcribed from the actual prop types, accessibility notes from real ARIA behavior, variants/sizes where real, and MOBILE_USAGE classifications. The catalog count moves 368 → 376 (finance 172 → 180, stable 354 → 362); this is why the release is a feature/minor bump despite shipping no new component source. SYNC_PENDING_REGISTRATION in registry-extended.test.ts is now empty and the "allowlists stay honest" test enforces it stays that way — every future component file needs a registry entry at birth (DANA-2144).
  • —Dedicated stories/patterns/ChartTooltip.stories.tsx — renamed from ChartInteractionLayer.stories.tsx (which only held ChartTooltip stories) so the file basename is the authoritative lib/storybook-ids.json key, matching its ChartAnnotation/ChartComparisonBar siblings. Adds StaleAgentAnalysis (validation-gold stale warning via staleAnalysisAge) and NegativeDelta (outcome-negative delta + primary field emphasis) to the existing user/agent states (DANA-2124).
  • —docs/storybook-conventions.md — story file/title layout, mock-data rule, the all-states convention (REQUIRED for state-driven components — badges/status/indicators — optional elsewhere; the export must be named AllStates because generate-storybook-ids prefers the "All States" story as the preferred story ID), and the hooks exclusion policy (DANA-2124).
  • —Hooks get API docs, not Storybook: useAgentStream, usePositionFeed, useAgentHeartbeat are deliberately excluded via a documented HOOK_EXCLUSIONS list in scripts/generate-storybook-ids.ts (also enforced — excluded hooks can never acquire a stray mapping), and ComponentPageTemplate now renders "API reference — no visual stories" for kind: "hook" entries instead of the misleading "Storybook (coming soon)" link. The typed props table + JSDoc'd source on the page are the reference (DANA-2124).
  • —PaymentStatusBadge AllVariants and ClearingNetworkIndicator AllNetworks stories renamed to AllStates per the all-states convention — both are state-driven, and the name is load-bearing for the preferred-story ID.

Storybook stories + docs for the 8 S28/generative-UI components (DANA-2163)

ComponentsDocs
  • —One autodocs-tagged story file per component shipped in v1.15.0–v1.18.0, in stories/patterns/: TaskStateBadge (Default, All States, per-lifecycle-group rows), RunLifecycleRail (in-flight, finished, interrupt terminal, nested child run with error terminal), HumanInputRequest (open with custom options, open with default APPROVE/DENY, answered, expired), StateDeltaInspector (default, snapshot toggle, history scrub, malformed-patch warnings), A2ATaskPanel (completed with history/artifact/metadata, input-required, working-empty, failed), ArtifactViewer (text/data/file part routing, streaming appending, unknown part kind, empty), ProvenanceFrame (default, chips + warnings, inProgress skeleton), UnknownComponentFallback. Story scenarios mirror lib/demos/protocol-demos.tsx mock data (demo-vendor, run_demo_*, fixed ISO timestamps) so site demos and Storybook stay consistent.
  • —Each component's JSDoc header extended with a concise Usage section (when to use / when not to, key props) alongside the existing protocol-concept citations — JSDoc headers + Storybook autodocs pages are the component docs convention (no per-component guides in docs/).
  • —Storybook rebuilt; lib/storybook-ids.json regenerated so all eight components resolve real docs/story IDs and their /patterns/<slug> pages replace the "Storybook (coming soon)" fallback (DANA-2123 mechanism).
  • —Docs hub (monorepo, separate commit surface): the 8 components appended to NODUS_COMPONENTS in apps/nodus-agent-api/app/api/routes/docs/components.py per the nodus-apps contribution rule.

v1.19.2: Live demos for the 8 S28/generative-UI components (DANA-2159)

Components
  • —The eight components shipped today in v1.15.0–v1.18.0 (TaskStateBadge, RunLifecycleRail, HumanInputRequest, StateDeltaInspector, A2ATaskPanel, ArtifactViewer, ProvenanceFrame, UnknownComponentFallback) had registry-extended.ts entries but no demo-registry entries, so their /patterns/<slug> pages rendered the "Demo coming soon" placeholder. New lib/demos/protocol-demos.tsx (sovereignty-demos module conventions — fixed ISO timestamps, mock-only data: demo-vendor, run_demo_*, task_demo_*) adds one live demo per component: full TaskStateBadge state row across the lifecycle groups, RunLifecycleRail with a nested child run and error terminal, HumanInputRequest open (3 options, live response ack) + answered pair, StateDeltaInspector with add/replace/remove patches and the BEFORE/AFTER snapshot toggle, a complete A2ATaskPanel (status, history, artifact, metadata), ArtifactViewer with text/data/file parts plus a streaming appending instance, ProvenanceFrame wrapping a StateBadge with a [SOURCE:] chip and a dropped-prop warning, and UnknownComponentFallback. Wired into lib/demo-registry.tsx via ...protocolDemos.

v1.19.1: Header version badge derived from package.json (DANA-2159)

Components
  • —SITE_VERSION was hand-maintained and sat at 1.4.1 through fifteen releases while the nav badge displayed it; lib/site-meta.ts now derives it from package.json at build time — drift is impossible.

Generative UI Phase 3: public package exports via ./generative-ui subpath + docs (DANA-2161) — v1.19.0

TokensComponentsDocs
  • —@nodus/design-system/generative-ui subpath export (src/generative-ui.ts, new tsup entry) — the public generative-UI surface: useNodusGenerativeUI, renderNodusComponentInline, renderA2UILayoutInline, RENDER_ACTION_NAME, LAYOUT_ACTION_NAME, validateProps + ValidatePropsResult, plus re-exports of ProvenanceFrame, UnknownComponentFallback, and the layout validator. Shipped as a subpath (the tokens/*.css precedent) rather than from the root barrel so @copilotkit/react-core and the 358-entry lazy render registry never load for consumers who don't use generative UI.
  • —Root barrel (src/index.ts) now exports the dependency-light pieces: ProvenanceFrame (+ ProvenanceFrameProps), UnknownComponentFallback (+ UnknownComponentFallbackProps), validateLayoutSpec, A2UI_SPEC_TYPES, and ValidateLayoutSpecResult.
  • —@copilotkit/react-core added as an OPTIONAL peer dependency (>=1.56, peerDependenciesMeta.optional: true) and to the tsup external list — required only when importing ./generative-ui.
  • —Foundation docs: /foundation/a2ui-architecture gains a "Generative UI" section documenting the render-action model (renderNodusComponent / renderA2UILayout), the data-props-only safety property (JSON parsed once, never-throw validation), ProvenanceFrame semantics (generated output is always visibly framed), and /a2ui/generate as the living demo.
  • —/a2ui/generate chat panel gains a two-sentence "what am I looking at" explainer strip above the chat: components render live from the approved catalog, the LLM supplies data only, provenance chrome marks generated output. Keeps the public demo self-explanatory for the DANA-2162 demand probe. GENERATOR_CATALOG untouched.

S28 protocol operations: StateDeltaInspector, A2ATaskPanel, ArtifactViewer + Nodus A2UI Profile naming (DANA-2163) — v1.18.0

ComponentsDocs
  • —StateDeltaInspector (components/patterns/StateDeltaInspector.tsx) — AG-UI StateSnapshot/StateDelta inspector: renders shared agent state as a collapsible tree with RFC 6902 JSON Patch ops (add/replace/remove, RFC 6901 pointers) highlighted in place — temporal tint on patched values, agency accent on agent-written path labels, strikethrough ghost rows for removed keys (IterationDiff deletion treatment). Optional snapshotToggle (BEFORE/AFTER) and historyScrub (PREV/NEXT stepper indexing into the patches array). Never-throw: malformed patch entries (unknown op, invalid pointer, missing value, unresolvable path) render as caution warning rows.
  • —A2ATaskPanel (components/patterns/A2ATaskPanel.tsx) — single A2A Task detail surface mirroring the spec's Task object ({id, contextId, status, history, artifacts, metadata}): identity header with the TaskStateBadge sibling, role-turned message history (user/agent) in ChatMessage border conventions, artifact list delegating each artifact to ArtifactViewer, and a mono key/value metadata block. Temporal spine.
  • —ArtifactViewer (components/patterns/ArtifactViewer.tsx) — Part-type-aware renderer for the A2A Artifact model ({artifactId, name?, parts: Part[]}, Part = {kind: "text"|"data"|"file", ...}): text parts as a typographic block, data parts as DataList-convention key/value rows, file parts as a mediaType + uri chip (reference only, never fetched), unknown kinds as a warning row. appending: boolean shows the StreamingDot tail while streaming artifact-update appends arrive. Validation-gold accent — an artifact is enriched output.
  • —All three registered in registry-extended.ts (status stable, full props) and exported from the patterns barrel. 10 new vitest render tests in components/__tests__/protocol-operations.test.tsx (patches applied/malformed/toggle/scrub, part-type routing, task header/history/artifacts). The A2UI GENERATOR_CATALOG is deliberately untouched.
  • —app/(site)/foundation/a2ui-architecture/page.tsx — the page now introduces the protocol as "A2UI — the Nodus A2UI Profile" and adds a short naming note: Google's A2UI (a2ui.org) is an open protocol in the same agent-to-UI space with different payload semantics; the Nodus dialect is called the Nodus A2UI Profile to disambiguate. Descriptive naming only — no compatibility or endorsement claims.

S28 protocol operations: TaskStateBadge, RunLifecycleRail, HumanInputRequest (DANA-2163) — v1.17.0

TokensComponents
  • —TaskStateBadge (components/patterns/TaskStateBadge.tsx) — badge for all nine A2A protocol TaskStates (submitted, working, input-required, auth-required, completed, failed, canceled, rejected, unknown) on the existing StateBadge anatomy. Colors follow the research doc's protocol lifecycle grouping: active=temporal, interrupted=validation, failed/canceled/rejected=agency, completed=outcome-positive, unknown=muted. Exports a taskStateGroup() helper so consumers can branch on active / interrupted / terminal.
  • —RunLifecycleRail (components/patterns/RunLifecycleRail.tsx) — vertical rail rendering one AG-UI run as lifecycle phases: RunStarted, StepStarted/StepFinished segments, and a terminal cap (finished=outcome-positive / interrupt=validation / error=agency). Supports one level of nested child runs (AG-UI parentRunId concept), rendered indented. Composes the AgentTimeline track/dot visual language with TaskStateBadge-consistent segment colors.
  • —HumanInputRequest (components/patterns/HumanInputRequest.tsx) — the canonical "agent needs a human" surface unifying the AG-UI interrupt model and the A2A input-required TaskState behind a source adapter prop ("ag-ui" | "a2a"). Validation-gold frame treatment consistent with ReviewRequestCard (S27 sovereignty styling); response buttons meet the 44px touch-target floor (--ds-touch-target-min-h); answered/expired statuses collapse to a resolution row.
  • —All three registered in registry-extended.ts (status stable, full props) and exported from the patterns barrel. 9 vitest render tests in components/__tests__/protocol-operations.test.tsx. The A2UI GENERATOR_CATALOG is deliberately untouched — catalog inclusion is a separate curation decision.

Generative UI Phase 2: composite layouts + full render registry (DANA-2160) — v1.16.0

Components
  • —renderA2UILayout CopilotKit frontend action (lib/use-nodus-generative-ui.tsx): the generator chat can now render composite multi-component A2UI layouts live through the existing A2UISurface renderer, wrapped in ProvenanceFrame. The spec travels as a JSON string (specJson) parsed once on completion — the same deterministic pattern Phase 1 chose for props.
  • —lib/a2ui-layout-validate.ts — never-throw minimal validation of layout specs against the 19 nodus:* spec types A2UISurface can render (7 unit tests in lib/__tests__/a2ui-layout-validate.test.ts). Unknown spec types, non-objects, and invalid JSON degrade to visible warnings in the frame; a recognized-but-malformed spec is caught by an error boundary — the chat never crashes.
  • —The gated "Live Component Rendering" system-prompt section now covers both tools and explains the choice: one catalog component → renderNodusComponent; a composed flow of nodus:* spec types → renderA2UILayout. Inline chat rendering wired for both action names in generator-client.tsx, still behind ENABLE_RENDER_ACTIONS.
  • —scripts/generate-render-registry.ts now sources public/registry.json (path field) instead of the GENERATOR_CATALOG: the render registry expands 123 → 358 components. Unresolvable entries (non-.tsx paths, duplicate names, missing files, statically unresolvable exports) are skipped with a logged codegen warning — one bad entry never breaks the registry — and catalog coverage is hard-asserted (all 123 catalog names must be renderable). Intentional distinction: the catalog stays the LLM's allowed set in the system prompt; the registry is a superset so renderA2UILayout specs may reference any registered component.
  • —lib/a2ui-prop-validate.ts enum-checks pure string-literal union types (e.g. "primary" | "secondary", 120 of 1,727 registry props) — invalid variant/size strings are now dropped with a warning instead of reaching React (3 new unit tests). Phase 2 spike verdict recorded in the module header: registry props are a curated data-prop subset of the real TS types (acceptable — unknown keys warn and pass through); a full ts-morph/react-docgen JSON-schema pass is documented and deferred.

Generative UI Phase 1: live DS components inline in chat (DANA-2158) — v1.15.0

Components
  • —The /a2ui/generate chat now renders approved catalog components LIVE inline via a CopilotKit renderNodusComponent frontend action (lib/use-nodus-generative-ui.tsx), in addition to — not instead of — the generated code panel. Props travel as a JSON string parsed once on completion; streaming tool calls show a Skeleton + StreamingDot frame.
  • —scripts/generate-render-registry.ts (npm run generate:render-registry, chained into prebuild after generate-registry-json) codegens lib/a2ui-render-registry.generated.tsx — a React.lazy registry over all 123 GENERATOR_CATALOG components, resolving both named and default exports (m[Name] ?? m.default).
  • —lib/a2ui-prop-validate.ts — never-throw prop validation against the registry.json prop schemas: lossless primitive coercion, function/undefined stripping, unknown-key and missing-required warnings surfaced in the UI (6 unit tests in lib/__tests__/a2ui-prop-validate.test.ts).
  • —New trust-provenance patterns (registered): ProvenanceFrame — provenance chip rail (default "ILLUSTRATIVE — generated output, no live data", [SOURCE:] chips in agency, [AI ESTIMATE]/confidence chips in validation), intentional 3px agency-red left agent-output marker, warnings list — and UnknownComponentFallback for non-catalog names.
  • —System prompt gains a gated "Live Component Rendering" section instructing one renderNodusComponent call per component before the code block. Single toggle point: ENABLE_RENDER_ACTIONS in lib/a2ui-flags.ts (gates client action registration, inline message rendering, and the prompt section).

Vibe-tests: A2UI eval harness (DANA-2118)

ComponentsDocs
  • —eval/ harness that measures whether the constrained A2UI generator produces correct, compliant UI vs baselines. Four arms (a2ui / registry-only / tailwind-baseline / plain-html), a hybrid grader (pure deterministic hard gates — scaffold, no-hex-in-JSX, catalog fidelity, must_use — plus a separate blind LLM-judge soft score), a runner with per-arm aggregation and the a2ui-vs-baseline delta, and an 8-task domain battery.
  • —Live path uses the claude -p CLI (no API key); EVAL_MOCK=1 runs the whole pipeline offline. Pure grading logic unit-tested (7 cases). npm run eval:vibe / eval:vibe:mock.
  • —Full live run, multi-turn degradation curve, and CI regression gating vs a committed baseline (see docs/spec-vibe-tests-eval-harness.md).

Figma Code Connect preflight: one-command unblock (DANA-2125)

TokensDocs
  • —scripts/figma-preflight.ts (npm run figma:preflight) distinguishes missing vs expired/403 vs valid FIGMA_ACCESS_TOKEN against the live Figma API, and computes Code Connect mapping readiness live (14 ready / 55 pending of 69) instead of a hand-maintained table. docs/figma-code-connect.md documents the one-command unblock flow.
  • —The 403 itself is resolved only by rotating the Figma credential (a manual step); the preflight makes that fix one command and self-verifying, but the valid-token success path is not exercised here.

nodus-ds upgrade: versioned codemods (DANA-2121)

TokensComponentsDocs
  • —nodus-ds upgrade [--apply] [--from <version>] [--dir <path>] runs frozen, version-pinned codemods to migrate consumer code off deprecated APIs. Dry by default; value-preserving replacements are auto-applied, ambiguous sites are flagged for manual review.
  • —First codemod — v1.3.0 bh-to-ds-tokens: 38 value-preserving --bh-* → --ds-* replacements (each --ds-* is defined as an exact alias of the primitive, so output is identical) plus 32 ambiguous primitives flagged for intent-based migration. Upgrades the warn-only deprecation-check CI into an autofixer.
  • —scripts/codemods/ registry + Codemod interface; pure transform with 6 unit tests; docs/cli-upgrade.md.

nodus-ds swizzle: eject a component to own it (DANA-2122)

ComponentsDocs
  • —nodus-ds swizzle <component> ejects a component you already consume into your own source tree with a loud ownership warning (it no longer tracks upstream). --rewrite-imports scans your project and repoints just that named import from @nodus/design-system to the local copy, preserving aliases and other named imports.
  • —repointBarrelImport() — a pure, unit-tested import transform (6 cases). The CLI now guards program.parse() behind an is-main check so it can be imported by tests.
  • —docs/cli-swizzle.md documents usage and the add vs swizzle distinction.

Framework-agnostic tokenVar() escape hatch (DANA-2120)

TokensDocs
  • —tokenVar(name, fallback?) and a frozen tokenVars map, re-exported from the package entry, let non-Tailwind consumers (Panda, Emotion, MUI, vanilla-extract, inline styles) bind --ds-* tokens with a typed helper instead of hand-writing var() strings.
  • —scripts/generate-token-names.ts (wired into prebuild) derives the typed DsTokenName union of 476 tokens from semantic.css/dataviz.css/responsive.css, so the typed surface can never drift from the CSS. Output: lib/token-names.ts.
  • —docs/token-var.md documents usage; lib/token-var.test.ts guards behavior and generation freshness.

SYNC contract: registry <-> component source drift fails CI (DANA-2119)

Components
  • —Three integrity cases in registry-extended.test.ts (existing Vitest CI job): every registry path/source exists on disk; no unregistered component source files; allowlists self-clean.
  • —Surfaced 8 finance components missing a registry entry (tracked in DANA-2144), temporarily allowlisted rather than silently ignored.

ds-stats.ts enforced as the single count SSOT (DANA-2126)

TemplatesComponentsDocs
  • —Prose counts had drifted from the derived lib/ds-stats.ts values (README 357, GUIDE 335 vs 360 components; 166/168 vs 167 templates). Corrected and annotated 16 live claims.
  • —scripts/check-count-sync.ts validates counts tagged with an opt-in <!-- ds-count:STAT --> marker against ds-stats, ignoring untagged/historical numbers, and cross-checks registry.json totalComponents. Wired into the CI type-check job (check:counts).

v1.14.0: Storybook autodocs enabled + real ID map ends broken site links (DANA-2123)

TemplatesComponentsDocs
  • —Every "Storybook" docs link on the site was broken — two root causes. (1) The deployed Storybook contained zero docs pages: docs: false in .storybook/main.ts and @storybook/addon-docs was never installed, though all 368 story files already carried tags: ["autodocs"]. (2) ComponentPageTemplate reconstructed story IDs from registry category (ui|patterns only) while real IDs derive from story titles across 11 groups — 232 of 360 component pages linked to non-existent IDs (e.g. ui-benchmarkcomparisonchart--docs instead of finance-charts-benchmarkcomparisonchart--docs).
  • —stories/foundation/Introduction.mdx now loads (import updated to @storybook/addon-docs/blocks; the MDX glob was previously excluded entirely).
  • —@storybook/addon-docs enabled — the built Storybook now ships 369 autodocs pages (1,842 index entries total).
  • —scripts/generate-storybook-ids.ts — runs via poststorybook:build (locally and on Vercel) and emits lib/storybook-ids.json mapping component name → real docs/story IDs from the built index.json. Templates look IDs up instead of guessing; 357/360 registry components resolve to live docs pages.
  • —Components without a Storybook entry (useAgentStream, usePositionFeed, useAgentHeartbeat — tracked in DANA-2124) fall back to the Storybook root labeled "Storybook (coming soon)". The interactive preview iframe is gated on a resolved story ID.

Foundation token-hygiene sweep: 33 pages moved onto `--ds-*` tokens (DANA-2106)

TemplatesTokensDocs
  • —Every hardcoded color/px/font literal across the 37 foundation pages is now a var(--ds-*) token or carries a line-level audit-exempt marker with a reason category. Real page chrome (headings, labels, cards, frame spacing) converted to --ds-type-*, --ds-space-*, and --ds-* color tokens — 4,354 token references now live across foundation pages.
  • —1,951 audit-exempt markers document deliberate literals by reason: code examples, reference/spec values, demo entity colors, layout geometry, fluid clamps, off-scale spacing, compound padding, optical tracking. Demo integrity preserved throughout.
  • —Left bare by convention: @media breakpoints inside CSS template strings, JSX prose, and code-snippet interiors, where a marker would alter rendered output.

v1.13.4: Declare @types for directly-imported d3/geojson/storybook packages (DANA-2106)

Docs
  • —Cold builds failed type-check (550 cascading errors) — the app imports 9 d3-* subpackages, geojson, and @storybook/react directly, but their type packages were never declared; resolution worked only through pnpm's private hoist into .pnpm/node_modules, which app source cannot see. A node_modules relink broke that leakage, and warm builds kept passing on stale incremental type-check state, masking it.
  • —Added 11 devDependencies — @types/d3-{array,chord,contour,force,geo,hierarchy,interpolate,scale,shape}, @types/geojson, @storybook/react@^10.3.6. tsc --noEmit: 550 errors → 0. No code changes.

v1.13.3: Apple-grade text rendering + Turbopack JSX-space fix (DANA-2106)

Docs
  • —font-synthesis: none globally — the browser can never render synthesized (blurry) bold/italic; every weight/style in use ships as a real font file (Inter variable with the opsz axis, JetBrains Mono 400–700, DM Serif 400 + italic). This is the single biggest crispness lever after the v1.13.2 font foundation.
  • —text-wrap: balance on headings and text-wrap: pretty on paragraphs — Apple-quality rag, no one-word orphans (progressive enhancement).
  • —Turbopack drops the implicit space after a JSX expression — the hero subhead rendered "93pre-built" on dev _and_ production despite a correct source space. All 31 {expression} text sites across the homepage, roadmap, getting-started, and gallery now use explicit {" "} string spaces (captured as learning-042).

v1.13.2: Sovereignty Console promoted to stable — harness 167/167 (DANA-2106)

Components
  • —Production render-verify harness: 167/167 clean (light + dark, zero errors). sovereignty-console promoted beta → stable per the QA gate. Gallery: 167 stable / 0 beta.

v1.13.2: High-resolution pass — 100% token compliance + crisp type rendering (DS-RESOLUTION)

TokensComponents
  • —Token compliance 98% → 100% — audit:tokens now reports 360/360 passing (was 342), 0 warning, 0 failing. The last 19 sub-90 components were hardened: ConfirmDialog 5 → 100, plus KeyboardShortcutsPanel, MCPToolCall, NumberInput, ProgressiveDisclosure, ActionLedger, AuthorityScope, and 12 finance charts. Chart type was converted only inside <text> style objects (where var() resolves); genuine SVG geometry, sr-only blocks, and responsive wrappers were annotated audit-exempt rather than force-tokenized.
  • —Synthetic faux-bold eliminated — JetBrains Mono now loads weights 600/700. ~252 components pair mono with --ds-type-label-weight (600) / --ds-type-hero-weight (700); without the real weight files the browser synthesized bold (blurry). Every mono label, timestamp, cost indicator, and button now paints crisp.
  • —EmptyState self-contradiction — default error label "SOMETHING WENT WRONG" → "ACTION FAILED"; the old string is the exact generic copy the a2ui interaction-states guidance lists as a Don't. GlossaryTooltip's "Learn more" link gains aria-label="Learn more about {term}" (WCAG 2.4.4).
  • —Type rendering fidelity (app/globals.css + app/layout.tsx): requested Inter's opsz axis + font-optical-sizing: auto (glyphs refine per size — sharper at the 10–11px data floor and at display sizes); font-variant-numeric: tabular-nums slashed-zero on mono (column-aligned digits for treasury data); text-size-adjust: 100% (stops mobile Safari inflating and breaking the tuned scale).
  • —audit:tokens 100% (360/360) · build:verify passed (validates the new font axis + weights) · 2665 tests pass.

v1.13.1: Sovereignty Console — the agent-ops flagship template (DANA-2106)

Templates
  • —/a2ui/templates/sovereignty-console — one operator supervising a four-agent fleet: the authority they granted (AuthorityScope, ReleaseBot's release-train grant with caps), the run log they can interrupt (ActionLedger — pause/stop/hand-back drives a FLEET PAUSED banner + header stat), and the reversal window on the last consequential action (ReversalWindow, "Released v1.13.0 to production"). The demo content is literally true — it renders the agent fleet that shipped v1.13.0. Domain: Agent Operations (first artifact of the agent-ops ontology exploration). Status: beta pending the production render-verify harness.

v1.13.0: S27 Sovereignty word-class ships — generator selects it 10/10 (DANA-2106)

TemplatesComponentsDocs
  • —3 sovereignty spec types — nodus:authority-grant, nodus:reversal-window, nodus:action-ledger (vocabulary now 22 types; the generator catalog gains a Sovereignty group with selection guidance).
  • —3 stable renderers in components/ai/: AuthorityScope (grant, permitted/denied, caps with usage, revoke), ReversalWindow (undo horizon, countdown, reverse-with-reason), ActionLedger (interruptible run log with Pause / Stop / Hand back). Stories + argTypes prop controls + storybookEmbed + live site demos. WCAG AA throughout (text status labels; AA-safe validation pairing instead of raw gold text; hydration-safe countdown per the S27 pattern).
  • —19 site demos via modular batches (lib/demos/): 12 dataviz charts, 3 sovereignty, 4 patterns — every storied component now has a live site demo (story-to-site gap: zero).
  • —Selection experiment: 10/10 PASS in an isolated harness using the generator's exact system prompt — 8/8 positive scenarios (incl. keyword-free paraphrases) selected the correct sovereignty spec unprompted; 2/2 negative controls stayed clean. Kill signal (<6/10) not approached. Verdict recorded in the brin decision log.
  • —SidebarNav duplicate-key error fixed at the root — the Templates nav group listed /a2ui/templates twice (Overview + a stale "View all 165+" link); duplicate removed. The long-standing console error (previously ignore-listed in the QA harness) is gone; console now clean.

v1.12.1: Roadmap re-aimed at the thesis — S27 Sovereignty leads (DANA-2106)

Components
  • —The roadmap now LEADS with the category arc: Sprint 27 — The Agent Interface Language: Sovereignty plus the Widening the Language track (A2UI spec-type expansion, cross-surface render parity, multi-agent orchestration surfaces, A2UI schema publication). Header reframed from feature-parity language to the-language-growing-word-classes. Answers the strategist-lens critique that a category-defining roadmap should widen the moat, not ship a maturity ledger. (Entry restored — an earlier CRLF-anchored insert was a silent no-op.)

v1.12.0: Role-selector, gallery color-law, A2UI proof strip (Gradient pt 2) (DANA-2106)

TokensComponentsDocs
  • —Homepage role-selector — five doors. A routing layer below the hero: Executive/Partner (→ /awards), Developer (→ /getting-started), Designer (→ /foundation/tokens), Strategist (→ /a2ui/generate), Adoption Lead (→ /community/contributing). Each door is a full-cell <Link> with a semantic accent left-border plus a text role label (identity never carried by color alone), ≥44px targets, hover lift + widened border, visible focus ring, <nav aria-label="Find your path">.
  • —Gallery shows the three-color law at the scan layer. New GalleryCard (3px role accent border + a 32px color-expression strip + a text role tag) and TriadFilterBar (All 264 / Agency 36 / Temporal 194 / Validation 10 — real buttons with aria-pressed, counts, 44px targets). Dominant role derives tokens-first with the design-brief's subcategory fallback (lib/semantic-role-map.ts) — only 14/264 entries declare a triad token, so tokens-only would have rendered 95% of cards neutral. Live mini-previews intentionally skipped: the demo registry previously OOM'd SSG workers; the spec's color-expression fallback is pure CSS.
  • —A2UI proof strip on the homepage. Two panels: what A2UI is (+ domain chips + CTA to the live generator) and a proof illustration — a frozen spec JSON → arrow → the real ApprovalGate component rendered from it, with an honest visible caption. The moat, witnessed instead of narrated. JSON uses --ds-color-temporal-subtle on the inverse surface (token-only, contrast-safe).
  • —All three surfaces render-verified on dev (5 doors with correct routes/accents; 263 gallery cards with exact accent distribution matching filter counts; strip renders spec + live component, 0 page errors). 2656 tests pass; build:verify green.

Homepage re-hero + Recognition (Gradient direction, part 1) (DANA-2106)

Components
  • —Hero leads with the claim, not a thesis. The abstract "Built to make AI action legible" → the category claim "No other design system / built for the agent era" (Gradient copy Option A — the inversion earns the assertion; red continuation). The subhead now leads with concrete, derived counts (93 patterns, 172 finance), and a semantic-color-law line explains red/blue/gold in plain text.
  • —Proof bar is honest and leads with proof. Now 357 Components · 172 Finance · 93 AI Patterns · WCAG AA (verified) — leads with the total and ends on the one _verified_ claim (gold), instead of the internal sub-breakdown.
  • —CTAs match reality. "Start building" (a promise an invite-only site can't keep) → "Explore the system" (/components) + "See A2UI in action" (/a2ui). The version badge → "Private preview · Evaluation by request · WCAG AA · render-verified."
  • —Awards → Recognition. The verified WCAG card now leads (gold VERIFIED tier); the two award entries are honestly labelled "Submitted · Mar 2026" (under review), and the heading is "One achievement. Two submissions in review." — separating earned proof from in-motion submissions (the highest-trust fix the critique surfaced).
  • —Single coherent <h1> (the color on the continuation is decorative — the sentence reads correctly monochrome and to a screen reader, WCAG 1.4.1); real text status labels (VERIFIED / Submitted), not color alone; 11px label floor on status badges; descriptive link text.

Last stale stat row → derived counts (DANA-2106)

Components
  • —The Gradient copy review caught a second homepage stat row still hardcoding 41 / 111 / 73 (a different inline array from the ARCHITECTURE one fixed in v1.11.11). Wired to PATTERN_COUNT / FINANCE_COUNT / CORE_UI_COUNT. The homepage now has zero hardcoded component counts — every number derives from lib/ds-stats.ts.

Fix concrete defects surfaced by the persona critique (DANA-2106)

TokensComponents
  • —Onboarding taught broken imports. getting-started used @nodus/ui/tokens.css and @nodus/patterns — neither package exists. Now the real specifier @nodus/design-system (+ /tokens/index.css).
  • —The CLI was broken three ways (npx nodus-ds exited 1):
  • —tsup.cli.config.ts added a shebang banner on top of the source's shebang → an invalid double-shebang bundle (syntax error). Removed the banner.
  • —The output is dist/cli.mjs, but bin/nodus-ds imported dist/cli.js. Pointed the bin at .mjs and added a npx tsx fallback so it works from source too.
  • —loadManifest() cast the object-shaped registry.json to an array and crashed on .reduce. Now reads .components (supports both shapes). npx nodus-ds add --list and add <c> --dry work.

Timezone-pin the last 2 #418 date renderers (DANA-2106)

TemplatesComponents
  • —The render-verify backstop after v1.11.12 caught 2 templates still failing #418 (morning-brief, payment-approval-workflow) — a root cause distinct from the Date/random freeze: toLocale*String without a timeZone formats in UTC on the SSR server but in the client's local zone on hydration → text mismatch. (quant-dashboard was fully fixed by the freeze.)
  • —morning-brief: AS_OF.toLocaleDateString/toLocaleTimeString → added timeZone: "UTC".
  • —components/finance/DualControlPanel: party.timestamp toLocaleString(undefined, …) → "en-US" + timeZone: "UTC" (fixes payment-approval-workflow and any future consumer).
  • —Render-verify harness now reports 166/166 clean. This iteration is why the empirical backstop matters — freezing the _instant_ to a deterministic value still left the timezone-dependent _formatting_ axis, which only a real cross-timezone render exposes.

A2UI gallery 100% stable — hydration residuals frozen (DANA-2106)

TemplatesComponents
  • —Ran verify-a2ui-templates.mjs against the live deploy after the component hydration hardening: 163/166 clean (the shared-component fix cleared 6 of the 9 originally-failing templates — portfolio-risk-overview, compliance-monitoring-center, money-market, swift-payments, cash-flow-forecasting, plus yield-curve via the SVG fix).
  • —Fixed the 3 residual #418 templates (their own render-time volatility, not covered by component hardening):
  • —morning-brief + quant-dashboard: Math.random() chart/surface data → seeded mulberry32 (deterministic; identical server + client).
  • —payment-approval-workflow + morning-brief: render-time new Date() → fixed module-level AS_OF constant. Event-handler timestamps (post-hydration) left untouched.
  • —verify-a2ui-templates.mjs now reads the extracted lib/a2ui-templates.ts registry (the template array moved out of the gallery page in v1.11.11).

Storybook + site alignment, hydration hardening, SVG fix (DANA-2106)

TemplatesComponentsDocs
  • —yield-curve-analytics SVG polyline bug — points used percentage strings ("0%,8.18…") inside a 0 0 100 100 viewBox, which is invalid; now plain numeric user-unit coordinates. Template passes the render-verify gate and is promoted to stable (gallery: 158 stable / 8 beta).
  • —SSG hydration (React #418) hardening — 11 shared relative-time components now carry suppressHydrationWarning on the 22 text elements whose first render derives from Date.now() / new Date() (RelativeTimestamp, Timestamp, AgentIdentityBadge, DeadlineCountdown, ConfidenceSpectrum, CitationCard, ActionProvenanceCard, ProvenanceChain, ThinkingState, CollaborationPresence, AmbientAgentStatus). Prevents server/client text mismatch when a statically-prerendered page hydrates later. 4 sibling components (DataFreshness, ReviewRequestCard, CeremonyGate, AuditTrail) were verified already-safe (volatile values computed only post-mount or from fixed props).
  • —lib/ds-stats.ts — single source of truth for headline counts, derived from the component registry + the A2UI template registry. Marketing pages import the named constants so counts can never drift from reality again.
  • —18 Storybook stories closing the registry↔Storybook coverage gap — every storyable component now has a story: 4 AI-agent (StreamingArtifact, ContextWindowMeter, FunctionCallVisualizer, GenerativeFormFill), 2 reconciliation (AgedItemsList, ReconciliationSummary), 12 dataviz charts (Sunburst, Chord, ForceGraph, BoxViolin, ParallelCoordinates, Choropleth, CirclePacking, ContourDensity, Horizon, StreamGraph, Bump, RadialBar).
  • —Extracted the A2UI template registry to lib/a2ui-templates.ts (was inline in the gallery page, 2,524→251 lines) so the gallery and the derived stats read from one source.

A2UI QA gate automated: 150 templates promoted to stable (DANA-2106)

Templates
  • —scripts/verify-a2ui-templates.mjs — automated render-verify harness. Loads every /a2ui/templates/<slug> in light AND dark via Playwright, captures uncaught exceptions + console errors, checks rendered content, and emits the list of beta templates that render clean in both themes (promotion candidates). This makes the A2UI QA gate a runnable command instead of a manual per-template review.
  • —150 beta templates promoted to "stable" after passing the automated light+dark render-verify gate (166 total tested: 157 clean, 9 flagged). Gallery is now 157 stable / 9 beta.
  • —morning-brief demoted stable to beta — it was marked stable but fails the gate (React #418 hydration mismatch). Honest gating: a template with a known render error is not stable.
  • —8x React error #418 (hydration text mismatch, likely time/locale-dependent SSR content): morning-brief, payment-approval-workflow, money-market, swift-payments, cash-flow-forecasting, portfolio-risk-overview, quant-dashboard, compliance-monitoring-center.
  • —1x SVG <polyline> bug (percentage strings in a numeric points attribute): yield-curve-analytics.

Dark-mode completeness: A2UI gallery chrome tokenized (DANA-2106)

TemplatesTokensComponents
  • —Dark-mode audit result: the 360-component library is fully dark-safe (0 hardcoded colors; token-compliance enforced; 62 --ds-* dark overrides). This change closes the only real dark-mode gaps found — in the A2UI gallery chrome:
  • —Template gallery: the 40 per-template accent colors (many very dark, e.g. #1A1A6E) were used as text/bar color and went near-invisible in dark mode. Now wrapped in color-mix(in srgb, <accent> 65%, var(--ds-text-primary)) — one adaptive pattern that stays readable in both themes while preserving hue.
  • —Generator UI: #059669 success color → var(--ds-color-outcome-positive); dropdown boxShadow → var(--ds-shadow-md).

A2UI generator: primitives added to the catalog (DANA-2106)

ComponentsDocs
  • —Expanded the A2UI generator catalog with 53 foundational primitives (Layout, Typography, Forms, Overlays, Navigation, Data, Feedback, Actions). Previously the generator's catalog was finance-only (72 entries) — it could not lay out a page, build a form, or add a dialog. It can now compose complete interfaces.
  • —All import paths are registry-derived and verified (119 unique paths, 0 missing).
  • —formatGeneratorCatalog() now groups UI/primitive components by subcategory in the system prompt (added optional group field).

Production-readiness: test suite green + token fixes + typecheck gate (DANA-2106)

TokensComponentsDocs
  • —Test suite was RED (12 failing) → now green (2655 pass, 0 fail).
  • —Real token-compliance violations fixed: ConfirmDialog boxShadow → var(--ds-shadow-lg); KeyboardShortcutsPanel gap → var(--ds-space-stack-2xs)
  • —Corrected 10 stale tests to match current (correct) behavior: ScrollArea (6 — component refactored to .ds-scroll-area + scroll-area.css), Tag a11y (2 — renders a native <button>), CardLoading skeleton count selector (1), sidebar-nav Foundation now uses groups (1)
  • —typecheck npm script (tsc --noEmit) — passes clean, closing the toolchain gap

Storybook stories: all primitives complete (DANA-2106)

Docs
  • —Added Storybook stories for the remaining 30 primitives — typography (8), forms (10), overlays (4), data (3), feedback (2), navigation (3) — in stories/ui/, grouped by subcategory title
  • —All 37 primitives now have both a live demo and a Storybook story — completes the primitive-surfacing effort (both goals of the spec)
  • —Verified via storybook:build (exit 0, all story bundles built)

Storybook stories: layout primitives (DANA-2106)

Docs
  • —Added Storybook stories for the 7 layout primitives — Box, Flex, Grid, Container, Section, Separator, ScrollArea (stories/ui/, titled Layout/*, CSF3 + autodocs + argType controls)
  • —Reference batch of the primitive Storybook coverage pass (37 total); verified via storybook:build (exit 0, all 7 bundled)

Complete primitive demo surface: 7 more demos (DANA-2106)

Components
  • —Added live demos for ProgressiveDisclosure, NumberInput, DatePicker, DateRangePicker, GlossaryTooltip, ConfirmDialog, and Command to lib/demo-registry.tsx
  • —With the navigation reference set, all 70 ui primitives now render a live demo (was 60/70) — no remaining "Demo coming soon" across layout/typography/data-entry/data-display/overlays/feedback/navigation
  • —Verified: build:verify passes; all 7 pages render live (dev server) with correct content, no demo-originated console errors

Surface navigation primitives + build-gate fix (DANA-2106)

ComponentsDocs
  • —Added live demos for Breadcrumbs, Stepper, and KeyboardShortcutsPanel to lib/demo-registry.tsx — their /components/<slug> pages now render interactive previews (were "Demo coming soon")
  • —Reference subcategory for the demos-only primitive-surfacing pass; spec at docs/superpowers/specs/2026-07-02-surface-primitives-demos-stories-design.md
  • —Verified: build:verify passes; all three pages render live (dev server), no demo-originated console errors
  • —scripts/verify-build.sh false-pass: the gate exited 0 when next was missing (fallthrough treated any unrecognized failure as the known SSG bug). Now a non-zero build is tolerated only with positive evidence — Compiled successfully present AND the failure is specifically the _global-error/_not-found prerender error; otherwise it exits 1. Three-case tested.

Security dependency scan + patch updates

DAN-782Docs
  • —Ran npm audit across 2,645 installed packages (78 prod, 2,568 dev)
  • —0 critical, 0 high, 0 moderate vulnerabilities
  • —6 low severity vulnerabilities identified — all in devDependency chain only
  • —Root: elliptic (GHSA-848j-6mx2-7j84, CVSS 5.6) via @storybook/nextjs → node-polyfill-webpack-plugin → crypto-browserify
  • —Fix requires breaking Storybook major downgrade (v10→v7) — accepted risk (devDep, no prod exposure)

Storybook decoupled + 12 missing stories + doc fixes

DAN-751TemplatesComponentsDocs
  • —build script now runs next build only — Storybook failures no longer block site deploy
  • —Storybook build remains available via dedicated storybook:build script
  • —ConfirmDialog — 5 stories: Destructive, Agency, Neutral, Loading, NoDescription
  • —Command — 3 stories: Default palette, EmptyState, AgentCommands
  • —KeyboardShortcutsPanel — 4 stories: Inline, ButtonTrigger, KbdTrigger, FilteredByGroup

A2UI template QA pipeline + 8 stable promotions

DAN-752TemplatesTokens
  • —QA script — Added scripts/qa-a2ui-templates.ts: static analysis gate enforcing CLAUDE.md QA rules. Checks hardcoded hex, wrong theme default, font size floor, missing useEffect cleanup, and gallery listing. Run: npx tsx scripts/qa-a2ui-templates.ts [slug...]
  • —approval-flow fix — const ACCENT changed from "#FF0000" to "var(--ds-color-agency)". Gallery card color synced.
  • —agent-chat fix — useEffect init reveal refactored to collect timer IDs and return cleanup, preventing message duplication in React 18 strict mode.
  • —agent-approval-gate fix — Approve button color: "#fff" replaced with "var(--ds-text-inverse)".
  • —8 stable promotions — Morning Brief, Agent Chat, Approval Flow, Observability, Cash Position Dashboard, FX Risk Monitor, Multi-Agent Orchestration, Agent Approval Gate promoted beta → stable after passing all QA gate checks.

Build pipeline, package.json, and CI validation fixes — DAN-750

TokensDocs
  • —B1 — Removed NPM_TOKEN from semantic-release step env in release.yml; private: true is intentional (sub-packages publish independently) and the token was contradictory and unused at that step.
  • —B2 — Moved @anthropic-ai/sdk, @copilotkit/*, @testing-library/dom, @vercel/analytics, @vercel/speed-insights from dependencies → devDependencies; all are doc-site-only and should not pollute consumer installs.
  • —B3 — Added build:cli step to release.yml release job, ensuring the CLI binary is built before semantic-release runs.
  • —B4 / C1 — Added build:lib validation step to ci.yml build job so dist/ is validated on every PR and push, not just at release time.
  • —Dep dedup — Removed framer-motion, lucide-react, next, react, react-dom, recharts from dependencies (they were duplicating peerDependencies).

Token layer fixes: T3, T4, warnings

DAN-748TokensDocs
  • —T3 — Added dark mode overrides for --ds-overlay-scrim/light/heavy in [data-theme="dark"]. Pins scrim/heavy to literal #000000 and light to literal #FFFFFF at low opacity. Eliminates the --bh-black/--bh-white inversion bug where these tokens rendered as white haze on dark surfaces.
  • —T4 — Created docs/token-migration-v1-v2.md documenting the v1→v2 spacing migration strategy. Covers --ds-space-inset-sm semantic change (16px uniform → 4px 8px shorthand), migration detection steps, and when to use each variant.
  • —Warning: --ds-color-info dark mode — Added --ds-color-info: var(--bh-blue-500) dark override (#6B8AFF, 5.67:1 on dark surfaces). Light mode --bh-blue-400 (#7080EC) is decorative-only contrast; dark surfaces needed a brighter step.
  • —Warning: --ds-space-inset-2xl — Added to :root inset scale as var(--bh-space-8) (64px). Completes the inset size ladder from 2xs through 2xl.
  • —Warning: responsive.css — Replaced @media (prefers-color-scheme: dark) with [data-theme="dark"] selector. Project theme is opt-in via DOM attribute, not OS preference.

Breaking dep upgrades (security follow-up) — DAN-426

Docs
  • —semantic-release v24→v25 — Major version upgrade from ^24.2.3 to ^25.0.3. Eliminates bundled npm@10.9.8 which contained vulnerable picomatch@4.0.3 (high, ReDoS) and brace-expansion@2.0.2 (moderate, process hang) inside its isolated node_modules/ tree.
  • —npm override ^11.13.0 — Forces @semantic-release/npm's transitive npm dependency to 11.13.0+, which ships patched picomatch@4.0.4 and brace-expansion@5.0.5. Resolves the remaining bundled-dep audit findings that overrides alone could not reach.
  • —CopilotKit uuid — Already resolved by uuid>=14.0.0 override from DAN-673. Confirmed all @ag-ui/client, @copilotkit/*, and @langchain/* instances resolve to uuid@14.0.0. No additional action needed.
  • —Vulnerability trajectory — 8 (DAN-673) → 6 (this scan). All remaining are elliptic (6 × low) in @storybook/nextjs → node-polyfill-webpack-plugin → crypto-browserify. Dev-only, no upstream fix, no production surface.
  • —Storybook/elliptic — No fix available. elliptic@6.6.1 is latest and remains in advisory range. Storybook 10.x uses node-polyfill-webpack-plugin@2.0.1 which transitively pulls it. Downgrading to Storybook 7.x is not viable. Accepted as residual dev-only risk.

Security dependency scan — DAN-673

Docs
  • —uuid override added — Added "uuid": ">=14.0.0" to package.json overrides. This closes GHSA-w5hq-g745-h8pq (missing buffer bounds check in uuid v3/v5/v6 when buf is provided) across all transitive consumers: @copilotkit/*, @ag-ui/*, @langchain/langgraph*, mermaid, and streamdown. 15 moderate vulnerabilities eliminated.
  • —DevDep patch sweep — Updated 12 devDependencies to latest patch versions: @storybook/addon-a11y@10.3.6, @storybook/addon-themes@10.3.6, @storybook/nextjs@10.3.6, storybook@10.3.6, @axe-core/playwright@4.11.3, @playwright/test@1.59.1, @tailwindcss/postcss@4.2.4, tailwindcss@4.2.4, chromatic@16.6.3, @figma/code-connect@1.4.4, @next/bundle-analyzer@16.2.4, vitest@4.1.5.
  • —Vulnerability trajectory — 27 (DAN-561) → 23 (DAN-561 patched) → 8 (this scan). Zero production-surface vulnerabilities remain.
  • —Remaining (dev-only, unfixable at time of scan):
  • —picomatch (high) + brace-expansion (moderate): bundled inside npm CLI's isolated tree. Resolved in DAN-426 via semantic-release v25 + npm override.

Component gaps + production hardening — DAN-661

TokensComponents
  • —5 new components — Breadcrumbs (navigation trail with overflow collapse), Stepper (multi-step progress indicator), NumberInput (finance-grade numeric input with Intl.NumberFormat currency/locale), DatePicker + DateRangePicker (native date inputs, zero deps), Command (cmdk wrapper with DS tokens).
  • —All Tier A — Every new component uses forwardRef + cva + cn() + displayName. Semantic tokens only, no raw hex.
  • —WCAG AA verified — Accessibility-lead reviewed all 5 components. Fixed: Stepper ARIA list structure + step count labels, NumberInput keyboard ArrowUp/ArrowDown increment.
  • —Barrel exports wired — All new components exported from domain barrels (form/, navigation/, ui/), main src/index.ts, and components/ui/index.ts (Command).
  • —Registry expanded — 357 components registered in registry-extended.ts / public/registry.json (up from 338 pre-DAN-661).

Token compliance audit — DAN-562

TemplatesTokensComponents
  • —Scope — Full P1 audit across all 372 component files for: raw hex values, --bh-* primitive token usage, --ds-border-* as text color, and font size below 10px floor.
  • —Result — Zero P1 violations found. All 367 component TSX files pass the token contract (no regression from DAN-311).
  • —compliance-registry.json — Added 37 previously-unregistered components to the registry, bringing total from 335 → 372 at 100% token compliance.
  • —New chart components: BarChart, BoxViolinPlot, BumpChart, ChordDiagram, ChoroplethMap, CirclePacking, ContourDensityChart, ForceGraph, HeatmapChart, HorizonChart, ParallelCoordinates, RadialBarChart, ScatterChart, StreamGraph, SunburstChart
  • —New finance components: ClearingNetworkIndicator, ForecastRow, OrderBookRow, PaymentQueueTable, PaymentStatusBadge

Security dependency scan — DAN-561

Docs
  • —package.json overrides — Added postcss: ^8.5.10 override to force Next.js 16's bundled PostCSS from 8.4.31 (XSS via unescaped </style> — GHSA-qx2v-qp2m-jg93) to 8.5.12. Verified all postcss resolutions now show overridden at 8.5.12.
  • —npm audit fix — Applied non-breaking fixes; patched 4 vulnerabilities total (27 → 23).
  • —Remaining (unfixable without breaking changes):
  • —picomatch (high) + brace-expansion (moderate): bundled inside the npm CLI tool (node_modules/npm/node_modules/...). Not app runtime code; cannot be patched via overrides. Upstream npm must release a patched bundle.
  • —elliptic chain (6 × low): transitive dev-only dependency via @storybook/nextjs. Fix requires downgrading to Storybook 7 — rejected. Not shipped to production.

Documentation freshness check — DAN-500

Docs
  • —lib/sidebar-nav.ts — Added /foundation/a2ui-architecture to the System group under Foundation. This 825-line page (A2UI protocol design, spec type taxonomy) was present but unreachable through navigation.
  • —lib/sidebar-nav.ts — Fixed i18n nav split: "Internationalization (i18n)" now correctly points to /foundation/i18n (the RTL/BiDi design spec, DAN-146) rather than the demo. Renamed /i18n link to "i18n Demo" and /i18n/demo to "i18n Interactive" for clarity.
  • —lib/search-index.ts — Added /foundation/i18n (i18n design spec) and /foundation/a2ui-architecture (A2UI foundation spec) so both pages appear in site search. Previously unreachable via both nav and search.

Charts nav, /charts/[slug] routes, sidebar links — DAN-433

TemplatesComponentsDocs
  • —app/(site)/layout.tsx — Added "Charts" to primary header nav between Patterns and Finance. Gives the Charts & Data Viz section equal standing with Components and Patterns.
  • —app/(site)/charts/[slug]/page.tsx — New dynamic route. Serves all chart components (both ui/finance and pattern/dataviz registry entries) through ComponentPageTemplate with prop table, source code, and related components. Static params generated from both categories at build time.
  • —app/(site)/charts/page.tsx — Gallery card links updated from /components/{slug} / /patterns/{slug} to /charts/{slug} for all chart types.
  • —lib/sidebar-nav.ts — Charts & Data Viz sidebar rebuilt with buildChartGroups(): replaces anchor-only "By Type" links with individual component entries under "Finance Charts" and "Visualization Patterns" groups, all linking to /charts/{slug}.

D3 chart accessibility hardening — DAN-432

Components
  • —components/finance/ChordDiagram.tsx — Enhanced ARIA: added role="img", aria-label with dynamic data summary, keyboard navigation between chords (Arrow keys), screen reader fallback <table> with flow amounts.
  • —components/finance/ForceGraph.tsx — Enhanced ARIA: added role="img", aria-label, keyboard navigation between nodes (Tab + Arrow keys), focus ring on active node, screen reader fallback <table> with node connections and edge weights.
  • —components/finance/SunburstChart.tsx — Enhanced ARIA: added aria-label with hierarchy summary, keyboard drill-down (Enter to expand, Escape to collapse), breadcrumb trail announced via aria-live.
  • —components/__tests__/d3-charts-a11y.test.tsx — New comprehensive a11y test suite (572 lines) covering all D3 chart components: ARIA roles/labels, keyboard navigation, screen reader fallback tables, focus management, prefers-reduced-motion support, high-contrast mode.
  • —All D3 charts now pass axe-core audit with zero violations.

Adopt D3.js + charts section + 3 initial D3 components — DAN-427

TokensComponentsDocs
  • —Architecture decision: D3.js modules adopted as math/geometry layer. React keeps DOM ownership. D3 provides scales, shapes, layouts, interpolation. No d3.select() — fully declarative React rendering.
  • —D3 modules installed: d3-scale, d3-shape, d3-hierarchy, d3-array, d3-format, d3-time, d3-time-format, d3-interpolate, d3-color, d3-scale-chromatic, d3-sankey, d3-force, d3-chord, d3-geo, d3-contour. Types: @types/d3, @types/d3-sankey.
  • —Charts & Data Visualization section added to sidebar nav with gallery page at /charts. Organizes 30+ chart/viz components by type (Standard, Hierarchical, Network, Heatmaps, Financial, Micro, Infrastructure).
  • —components/finance/SunburstChart.tsx — New D3-powered sunburst chart (d3-hierarchy.partition). Fund decomposition, portfolio allocation drill-down with arc transitions.
  • —components/finance/ChordDiagram.tsx — New D3-powered chord diagram (d3-chord). Cross-entity treasury flows, intercompany transfers with gradient ribbons.

HorizonChart + StreamGraph + BumpChart + RadialBarChart — DAN-430

TokensComponentsDocs
  • —components/finance/HorizonChart.tsx — Dense multi-series time-series using the horizon chart technique. Each series gets a compact lane; values fold into nBands overlapping opacity layers (band 0 = full, band 2 = lightest) using d3-area + curveMonotoneX with clipPath masking. Use case: monitoring 10+ FX rates or cash positions simultaneously.
  • —components/finance/StreamGraph.tsx — Stacked area with stackOffsetWiggle + stackOrderInsideOut for organic S-flow baseline. Uses d3-shape stack + area + curveCatmullRom. Categorical token palette. Use case: AUM composition over time.
  • —components/finance/BumpChart.tsx — Rank change chart connecting positions across time periods with curveBumpX (d3-shape 3.x). Dots at each rank with accessible aria-label per point. Right-side series labels at last rank position. Use case: fund performance rankings, counterparty ranking shifts.
  • —components/finance/RadialBarChart.tsx — Circular comparative bars using scaleBand (angle positions) + scaleLinear (radial length) + d3Arc. Background track arcs show limit. Labels with cos/sin(midAngle - π/2) offset for 12-o'clock orientation. Use case: KPI comparison, limit utilization.
  • —All 4 registered in registry-extended.ts with full prop definitions.

ChoroplethMap + CirclePacking + ContourDensityChart — DAN-429

TokensComponents
  • —components/finance/ChoroplethMap.tsx — Geographic exposure visualization using d3-geo (geoNaturalEarth1 / geoMercator). Regions colored by sequential token scale. Accepts real GeoJSON features; default demo uses major world-region rectangles. Legend strip + pattern overlay ensure color is not the sole encoding. Screen-reader fallback table.
  • —components/finance/CirclePacking.tsx — Hierarchical allocation chart using d3-hierarchy.pack(). Nested circles for portfolio → fund → position drill-down. Depth-based opacity conveys nesting hierarchy. Labels gated on radius ≥ 18px. Categorical token palette. Screen-reader fallback table with allocation %.
  • —components/finance/ContourDensityChart.tsx — 2D kernel density estimation using d3-contour. Auto-derived KDE bandwidth. geoPath() (no projection) converts pixel-space MultiPolygons to SVG paths. Bimodal demo data showing two return clusters. Zero reference lines, scatter point overlay, density legend. Screen-reader data summary table.
  • —All 3 registered in registry-extended.ts; added to charts catalog sections; mobile behaviors documented.

D3 chart math migration — DAN-428

TokensComponents
  • —components/finance/Treemap.tsx — Replaced custom squarified layout algorithm with d3-hierarchy.treemap(). React rendering layer and all accessibility (ARIA, keyboard nav) preserved. Removed ~60 lines of manual layout code.
  • —components/finance/SankeyDiagram.tsx — Replaced custom BFS column assignment + manual Bezier path generation with d3-sankey + sankeyLinkHorizontal(). Preserves token-based theming, gradient fills, and screen reader labels.
  • —components/finance/LineChart.tsx — Scale logic migrated to d3-scale (scaleLinear); path generation migrated to d3-shape (line, area, curveLinear). Confidence band switched from <polygon> to d3Area with y0/y1 bounds. Zero visual diff.
  • —components/finance/AreaChart.tsx — Same scale/shape migration as LineChart. Stacked area baselines computed identically; d3Area and d3Line replace manual M L Z path strings. Zero visual diff.
  • —components/finance/shared/chart-tokens.ts — getSequentialColor and getDivergingColor now use d3-interpolate (interpolateRgb) for smooth continuous gradients; replaces discrete 5-step palette. HeatmapChart inherits the improvement automatically.

Statistical charts — DAN-431

ComponentsDocs
  • —components/finance/BoxViolinPlot.tsx — New D3-powered box/violin plot for risk distribution visualization. Renders quartiles, median, whiskers, outliers, mean diamond. Optional violin KDE overlay with Gaussian kernel and Silverman bandwidth. Keyboard-focusable series with tooltip. Screen-reader fallback <table> with statistical summaries (min, Q1, median, mean, Q3, max, outlier count).
  • —components/finance/ParallelCoordinates.tsx — New D3-powered parallel coordinates chart for multi-factor instrument analysis. Vertical axes with drag-to-brush range filtering. Rows that fall outside any brushed axis are hidden. Live row count and "Clear filters" control. Screen-reader fallback table with aria-hidden on filtered rows. Keyboard navigation on each axis (Tab + Escape to clear brush).
  • —registry-extended.ts — Registered both components (box-violin-plot, parallel-coordinates) with full prop definitions and mobile usage notes.
  • —app/(site)/charts/page.tsx — Added "Statistical & Analytical" section to the chart catalog.
  • —lib/sidebar-nav.ts — Added "Statistical & Analytical" nav item with new badge.

Design review cycle — DAN-425

Components
  • —components/patterns/AvatarStack.tsx — Initials and overflow count (+N) font sizes now clamp to Math.max(10, ...) at avatar sizes below 25px. At the default size (32px) this is a no-op; at small sizes (e.g. 24px) the old formula produced 9px (violation). CollaborationPresence already applied this pattern correctly.

Security dependency scan — DAN-423

ComponentsDocs
  • —Next.js 16.2.1 → 16.2.4 — GHSA-q4gf-8mx6-v5v3 (Denial of Service via Server Components, CVSS 7.5, >=16.0.0-beta.0 <16.2.3)
  • —Vite 8.0.1 → 8.0.10 — three high-severity CVEs: path traversal in optimized deps .map handling (GHSA-4w7w-66w2-5vf9), server.fs.deny bypass with queries (GHSA-v2wj-q39q-566r), arbitrary file read via WebSocket (GHSA-p9ff-h696-f583)

Token compliance audit — DAN-311

TemplatesTokensComponentsDocs
  • —Font size floor (10px minimum): lib/site-styles.ts a2uiLabel raised from 9px → 10px (propagates to all 23+ A2UI templates). components/patterns/LiveChangeIndicator.tsx role label raised from 8px → 10px.
  • —Border tokens used as text color (20 violations fixed): var(--ds-border-separator), var(--ds-border-structure), and var(--ds-border-default) were misused as CSS color values — these are 2–3:1 contrast, failing WCAG AA. All replaced with var(--ds-text-muted) (4.83:1).
  • —components/ai/ModelSelector.tsx — separator |
  • —components/site/ComponentPageTemplate.tsx — breadcrumb /
  • —components/patterns/SpeechStateIndicator.tsx — idle + muted states

KeyboardShortcutsPanel + KeyboardBadge (H7 + H10) — DAN-132

TokensComponentsDocs
  • —components/ui/KeyboardShortcutsPanel.tsx — New component with three trigger modes: inline (dedicated pages), button (sidebar toggle), kbd (global ? key activation).
  • —KeyboardBadge — Exported sub-component rendering styled <kbd> elements using --ds-type-mono-font and --ds-surface-raised.
  • —context prop — Context-aware filtering: pass a context key to show only relevant shortcuts for the current view.
  • —Two-column grid layout: Action label | Key combination(s), grouped by category with group-filter tab strip.
  • —Full token compliance: all colors, borders, and type use --ds-* semantic tokens. No raw hex.

GlossaryTooltip + term definition system — DAN-133

Components
  • —components/ui/GlossaryTooltip.tsx — New <abbr>-anchored tooltip component using the temporal variant (blue = informational). Props: term, definition, learnMoreHref?.
  • —lib/glossary.ts — GLOSSARY map with 15 entries: MCP, MCP Server, MCP Tool Call, A2A, A2A Handoff, Provenance, Epistemic State, Confidence Score, Grounding, Orchestrator, Handoff, Delegation, Vault, Guardrail, Model Selector.
  • —components/patterns/MCPToolCall.tsx — tool and server labels wrapped with GlossaryTooltip.
  • —components/patterns/MCPServerStatus.tsx — Section title wrapped with GlossaryTooltip.
  • —components/patterns/ProvenanceChain.tsx — Chain title wrapped with GlossaryTooltip.

Token compliance audit — DAN-69

TemplatesTokensComponents
  • —ChartInteractionLayer — Swap A/B button used --ds-border-structure as text color (2-3:1, fails WCAG). Corrected to --ds-text-muted (4.83:1).
  • —PresenceAvatarStack — Separator · dot used --ds-border-structure as text color. Corrected to --ds-text-muted.
  • —ComponentPageTemplate — Check icon badge had fontSize: "9px" below the 10px floor. Bumped to 10px.

Security dependency scan and patch — DAN-68

TemplatesDocs
  • —lodash@4.18.1 — fixes GHSA-r5fr-rjxr-66jc (Code Injection via _.template, CVSS 8.1) and GHSA-f23m-r3pf-42rh (Prototype Pollution, CVSS 6.5)
  • —lodash-es@4.18.1 — same CVEs as lodash
  • —picomatch@4.0.3 (high) — bundled inside npm@11.12.1/tinyglobby — cannot be overridden; affects only npm CLI tooling, not runtime. Upstream fix required from npm team.
  • —brace-expansion (moderate) — bundled inside npm@10.9.8 in semantic-release — same constraint.
  • —elliptic and crypto-browserify chain (5 low) — in @storybook/nextjs@10.3.3; npm audit incorrectly suggests downgrade to 7.0.14. Dev-only, no runtime exposure.

Fix duplicate color TS errors in finance components — DAN-44

TokensComponentsDocs
  • —Fixed: ConfirmationCard, CurrencyAmount, ExceptionQueue, HedgeBook, JournalEntry, PaymentQueue, PaymentTracker, RegulatoryReport, RiskDisclosure, RiskHeatmap, SanctionsCheck, ScreeningResultCard, SurveillanceQueue, TCAReport, ThresholdAlert, WaterfallChart
  • —Storybook build: ✓ passing
  • —Next.js build: ✓ passing (576 pages, TypeScript clean)

Site header redesign — DAN-35

Components
  • —Version badge now sourced from lib/site-meta.ts — no more drift from package.json
  • —Nav links gain active state via usePathname() — bold text + agency-red underline pip
  • —Added Get Started and Changelog to top nav
  • —Improved ARIA labels, aria-current="page", role="banner", tabIndex={-1} on main
  • —Focus-visible outlines on all interactive header elements (temporal blue, 2px offset)

P2 inline-no-token violations cleared — DAN-24

TokensComponents
  • —Added audit-exempt directive support to scripts/audit-tokens.ts for layout-only, dynamic-color, and geometric style blocks
  • —Cleared P2 violations across ~80 pattern, UI, AI, form, overlay, and typography components
  • —No semantic changes — all visual output unchanged; compliance metadata only

P1 hardcoded token violations cleared — DAN-22

TokensComponents
  • —Added audit-ignore directive support to scripts/audit-tokens.ts for geometric/SVG/breakpoint exemptions
  • —Replaced hardcoded values with DS spacing tokens (--ds-space-inset-*, --ds-fin-gap-*, --ds-icon-size-*)
  • —Replaced one rgba(255,255,255,0.35) with color-mix(in oklch, var(--ds-text-inverse) 35%, transparent)
  • —Coverage: 97% → 100%

Roadmap accuracy audit — DAN-221

TemplatesTokensComponents
  • —S26: White-Label Theming System promoted inprogress → stable (RuntimeThemeProvider, theme builder, guide all shipped). Sprint phase → stable.
  • —P2 Finance Ontology: Liquidity & Cash, Compliance & Regulatory, Reconciliation, Portfolio, Funding all promoted planned/inprogress → stable with component references.
  • —P2 Finance Workflows: Daily Cash, FX Hedging, Payment Approval promoted planned → stable with A2UI template references.
  • —P3 Data Visualization: All 8 chart types → stable. DS Token Bridge and Responsive Container → stable. Tooltip System and Chart Accessibility → beta. Phase → stable.
  • —P4 Templates: All finance and AI interface templates → stable. Phase → stable.

Interaction State primitives extracted as reusable components — DAN-216

TokensComponents
  • —StateBadge (components/patterns/state-badge.tsx) — color-coded uppercase badge
  • —StateIndicator (components/patterns/state-indicator.tsx) — animated dot indicator.
  • —ScenarioScript (components/patterns/scenario-script.tsx) — step-sequence renderer
  • —globals.css — added four new keyframe + utility classes:
  • —interaction-states page refactored to import StateBadge and ScenarioScript;

Token compliance Wave 5: 6 components reach 97–100% score — DAN-212

TokensComponents
  • —MatchQueue 80→97% — 10 P2s fixed (layout props moved to Tailwind className)
  • —ProgressivePermissionFlow 88→100% — 6 P2s fixed (flex/margin layout props → className)
  • —ReconciliationDiff 90→100% — 2 P1s fixed (width: "140px" → var(--ds-fin-col-4xl))
  • —BreakAnalysisTable 94→100% — 3 P2s fixed (overflow, table layout → className)
  • —BreakAnalysisPanel 94→100% — 3 P2s fixed (overflow, table layout, list-style → className)

Token compliance Wave 3: 19 warning-band components promoted to passing — DAN-205

TokensComponents
  • —IconButton 70→100, Dialog 70→100, DeadlineCountdown 70→100, ISINChip 70→100
  • —Table 71→96, AgencyIndicator 71→98, FXRateTicker 71→98, CovenantDashboard 71→91, CurrencyAmount 71→96
  • —TaskQueue 73→98, Watermark 73→98, CovenantGauge 73→98, RatioBar 73→93
  • —CashPositionCard 74→98, WaterfallChart 74→94, SettlementTimeline 74→98
  • —Skeleton 75→100, SegmentedControl 75→100, TableOfContents 75→100

Continuous documentation sync SOP — DAN-183

ComponentsDocs
  • —Created lib/changelog-data.ts as the single machine-readable source of truth for release history
  • —ChangelogEntry interface with date, version?, ticket, title, kind, summary, items[]
  • —CURRENT_VERSION constant synced to package.json
  • —Back-filled 10 entries from DAN-001 (v1.0.0) through DAN-190 (today)
  • —Added RecentChanges component above the ProvenanceChain sprint narrative

Treasury Executive Summary dark mode fix + QA gate — DAN-180

TemplatesTokensDocs
  • —Replaced A2UI_DARK as D with A2UI_LIGHT as C in app/(site)/a2ui/templates/treasury-executive-summary/page.tsx
  • —Page now renders in light mode by default; CSS token vars in A2UI_LIGHT auto-adapt to dark mode
  • —Updated footer note from "dark · fixed presentation" to "light default · dual-theme"
  • —Added status?: "stable" | "beta" | "incomplete" field to the TEMPLATES data schema in the gallery index
  • —Gallery cards now render a visible badge for "beta" and "incomplete" templates

Fix invisible FIXED tags on release notes page — DAN-178

TokensDocs
  • —Added --ds-color-outcome-positive-subtle-fg — dark green on pale subtle bg (light), white on dark subtle bg
  • —Added --ds-color-outcome-caution-subtle-fg — consistent coverage for caution subtle surfaces
  • —Added --ds-color-outcome-negative-subtle-fg — consistent coverage for negative subtle surfaces
  • —Extended data-fin-region="asia" override with matching -subtle-fg tokens
  • —FIXED badge: color changed from --ds-color-outcome-positive-fg → --ds-color-outcome-positive-subtle-fg

Component API documentation — DAN-190

TemplatesTokensComponentsDocs
  • —Added accessibilityNotes?: string[] field to ComponentEntry interface in registry-extended.ts
  • —Authored accessibility notes for 6 core UI primitives: Button, IconButton, Input, Badge, Tag, Card
  • —ComponentPageTemplate now renders an Accessibility section (between Props and Metadata strip) when notes are present
  • —Notes cover: ARIA attributes, keyboard interaction, contrast compliance, dynamic update patterns
  • —GET /api/components — full registry list with optional ?category, ?status, ?q filters

Experimental area: ∞ Canvas Grid + Spline 3D — DAN-169

TokensComponents
  • —Infinite-depth pan/zoom canvas using retained-mode Canvas 2D rendering
  • —40×40 node grid with semantic status colors tied to --ds-color-* tokens
  • —rAF draw loop outside React reconciler for 60fps performance
  • —Mock mode (built-in random state churn) + Live WebSocket mode (JSON diff array)
  • —Pan via drag, zoom via scroll, configurable update rate

Font floor sweep — DAN-171

TokensComponentsDocs
  • —app/(site)/page.tsx — 11 badge/label instances
  • —app/(site)/a2ui/components/page.tsx — 21 spec-type label instances
  • —app/(site)/_components/component-showcase.tsx — 5 instances
  • —app/(site)/_components/token-demo.tsx — 5 instances
  • —app/(site)/foundation/heuristics/page.tsx — 5 instances (Tailwind)

v1.4.0 — Sprint 26 Release

DAN-159TemplatesTokensComponentsDocs
  • —@nodus/design-system@1.4.0 — AI components, Reconciliation domain, White-Label Theme Builder, i18n + tokens v2 exports fixed
  • —@nodus/design-system-native@1.4.0 — React Native adapters (synchronized version bump)
  • —@nodus/tokens@1.4.0 — standalone CSS token package (CDN-ready via jsDelivr + unpkg)
  • —Three-package publish pipeline with synchronized 1.4.0 versioning
  • —Bundle size budgets updated for v1.4.0 scope: ESM 260 kB / CJS 270 kB (gzip) — PASS

Post-Release Fixes & Theme Builder Enhancement

TokensDocs
  • —Triadic hue rotation (fromSeedColor()): pick any brand hex color and derive a full semantic palette. The seed becomes agency; temporal and validation are derived at +120° / +240° on the hue wheel — mirroring the near-triadic relationship of the Nodus default palette.
  • —"Seed" UI strip in the Theme Builder top bar: native <input type="color"> + hex text input + Generate button. Pressing Generate applies the derived triadic palette and marks the theme as Custom.
  • —hexToHsl, hslToHex, isLightColor helpers added as pure math utilities (no DOM reads, SSR-safe).
  • —Subtle and fg variants auto-computed: subtle = S 20% / L 96%; fg = white or #18181B based on Y-luminance.
  • —Search index: Added 7 missing entries — Agentic Design Language overview, Agent Identity, Trust & Provenance, LLM Latency States, Agentic Playground, White-Label Theming guide, and Theme Builder sandbox tool. These pages were all unreachable via search.

Sprint 26 — White-Label Theme Builder

DAN-154Tokens
  • —/sandbox/theme-builder: 3-pane interactive token editor.
  • —Left: grouped token editor — agency/temporal/validation colors (color pickers), surface palette, radius scale (slider), mono font toggle
  • —Center: live preview — miniature finance dashboard renders in real-time as tokens change; dark/light mode toggle
  • —Right: export panel — CSS custom property sheet, Tailwind config object, JSON token map, createTheme() call
  • —Powered by lib/create-theme.ts themeToVars() and serializeThemeToCss() utilities

Sprint 26 — Reconciliation Domain

DAN-156Components
  • —ReconciliationSummary — Dashboard header card: total items, matched %, open exceptions, oldest break age, last run time. MetricCard grid with match-rate progress bar. Optional refresh callback.
  • —MatchQueue — Two-column match interface. Left: unmatched items with queue-depth badge. Right: ranked candidates with confidence scores (HIGH/MED/LOW). Match and Reject actions per candidate.
  • —ExceptionManager — Full reconciliation exception table with type/status/age filters, batch-action toolbar (escalate, resolve, assign), and inline status update per row. Age-sorted by default.
  • —BreakAnalysisPanel — Root-cause analysis for a single break. Metadata grid, field-level criteria diff view (internal vs counterparty), chronological history timeline, resolution notes editor.
  • —AgedItemsList — Time-bucketed aged items: 0–30 / 31–60 / 61–90 / 90+ days. Color-coded signals (green → amber → red). Click-to-drill bucket headers. Sparkline trend per bucket.

Sprint 26 — Documentation v2

DAN-157TokensComponentsDocs
  • —/foundation/component-graph: Interactive SVG dependency graph — 223 components, filter by category, hover highlights, click to navigate.
  • —Row-layered layout: Patterns → AI & Agent → Finance → UI Primitives with 22 curated dependency edges.
  • —/components/ai-agent: Full docs page for the AI & Agent component category.
  • —Streaming state machine (IDLE → STREAMING → COMPLETE/ERROR)
  • —When to use / When not to use guidance for all 5 components

Sprint 26 — Brand & Portfolio

DAN-160TemplatesTokensComponentsDocs
  • —AI-Era Visual Narrative: Awards page now leads with "The only design system built for the agent era" — the 5 Sprint 26 AI-native components (StreamingArtifact, ContextWindowMeter, FunctionCallVisualizer, ModelSelector, GenerativeFormFill) are the flagship showcase with code snippet + architecture diagram.
  • —Submission Package Updated: Description, tags, and version updated to v1.4.0 (Sprint 26). Technologies updated to include Anthropic SDK and WebGL.
  • —CSS Design Awards Added: Second awards track added alongside Awwwards — AI-Native Component Showcase submission.
  • —Recognition Track Expanded: 6 entries (was 4) — Claude API Integration and CSS Design Awards added.
  • —Social Assets Spec: Twitter/X (1200×675), LinkedIn (1200×627), Product Hunt gallery, OG image — all documented with ready-to-publish copy.

Sprint 26 — AI-Native Templates

DAN-155TemplatesComponents
  • —Multi-Agent Pipeline (/a2ui/templates/multi-agent-pipeline) — Visualize and manage multi-agent workflows: pipeline stage diagram with status pills, per-agent status cards with progress bars, task delegation tree, conflict detection and auto-resolution panel, full audit trail feed (HANDOFF/CONFLICT/INTERRUPT/COMPLETE events), and human interrupt/override controls.
  • —Knowledge Base Search (/a2ui/templates/knowledge-search) — Semantic search and retrieval interface: query bar with suggested hints, vault boundary filter with access-level badges (RESTRICTED/CONFIDENTIAL/PUBLIC), result cards with confidence meters, highlighted excerpt rendering, collapsible source provenance chain (ingest → chunk → embed → index), and latency/model stats panel.

Sprint 26 — AI-Native Component Pack

DAN-153TokensComponents
  • —StreamingArtifact — Renders streamed code/markdown/text output. Features: line-numbered code view, diff view (+/- rows with semantic colors), streaming cursor blink, auto-scroll to tail, copy button, error state. Props: content, kind, language, state, diff, showLineNumbers, showCopy, maxHeight.
  • —ContextWindowMeter — Segmented token usage bar for conversation context. Segments: system (muted), user (temporal/blue), assistant (agency/red), tools (validation/gold). Warning state at 80%, critical overflow alert at 95%. Props: segments, limit, model, warnAt, criticalAt, showLegend, compact.
  • —ModelSelector — Model picker for Claude model family (Haiku/Sonnet/Opus). Each option: context window size, capability chips (VISION, TOOLS, STREAM, THINK, BATCH, FILES, CUA), cost tier, latency tier. Dropdown and inline panel variants. Props: options, value, onChange, variant, label.
  • —FunctionCallVisualizer — Tool call execution card. Line-numbered JSON input/output, status badge (PENDING/RUNNING/DONE/ERROR), semantic border color per state, duration display, call ID traceability, collapsible panel. Props: name, input, status, output, error, durationMs, callId, defaultCollapsed.
  • —GenerativeFormFill — Form field with AI-assist. Ghost suggestion text, Tab-to-accept / Escape-to-reject keyboard shortcuts, provenance badge (model + confidence %), generating pulse animation, full ARIA labeling. Props: value, suggestion, model, generating, confidence, onAccept, onReject.

v1.3.0 — Sprint 25 Release

DAN-150TemplatesTokensComponents
  • —@nodus/design-system@1.3.0 — web components + tokens v2 + WebGL charts + i18n
  • —@nodus/design-system-native@1.3.0 — React Native adapters (renamed from @dana/nodus-ds-native)
  • —@nodus/tokens@1.3.0 — standalone CSS token package (new; CDN-ready via jsDelivr + unpkg)
  • —Three-package publish pipeline with synchronized versioning
  • —Bundle size budgets via size-limit (250 kB ESM / 260 kB CJS)

Design Tokens v2 Implementation

DAN-149TokensComponents
  • —lib/dark-mode-derive.ts — OKLCH-based dark-mode derivation algorithm. Converts hex → OKLCH color space (Björn Ottosson), transforms lightness by semantic role (surface/text/accent/subtle), reconverts to hex. Perceptually uniform — hue and chroma preserved across modes.
  • —scripts/build-tokens.ts — Build step that generates public/tokens/semantic.v2.css. Combines OKLCH-derived dark values for accent colors with precision-hardcoded Bauhaus neutral scale for surfaces/text. Run via npm run build:tokens.
  • —public/tokens/semantic.v2.css — Generated v2 token file served statically. Consumed by the Playwright token regression spec (DAN-151) for v1→v2 visual comparison runs.
  • —lib/token-versions.ts — Token versioning and deprecation registry. TOKEN_VERSION = "2.0.0". Provides isDeprecated(), getDeprecationInfo(), warnIfDeprecated() for dev-mode console warnings on v1 token usage.
  • —lib/create-theme.ts — Added v2 palette shorthand API: createTheme({ palette: { primary, accent, neutral } }). Derives agency/temporal colors from 3 brand colors. Backward-compatible with existing colors field API.

i18n Runtime

DAN-147Components
  • —formatNumber(value, locale, options) — decimal, compact, scientific notation; NaN/Infinity guards; normalizes -0
  • —formatCurrency(value, currency, locale, options) — symbol placement, accounting currencySign, compact notation, per-locale decimal precision
  • —formatDate(value, locale, options) — accepts Date / timestamp / ISO string; short/medium/long/full dateStyle
  • —formatRelativeTime(value, locale, now?) — "yesterday", "1 hour ago", "in 3 days"; 7-step resolution cascade
  • —SUPPORTED_LOCALES — ["en-US", "en-GB", "de-DE", "fr-FR", "ja-JP", "ar-SA", "he-IL"]

WebGL Chart Primitives

DAN-144TokensComponents
  • —Shared infrastructure context for all GL charts
  • —CSS token bridge: tokenToRGBA(cssVar) resolves DS semantic tokens → WebGL RGBA float arrays via computed style; handles nested var() chains and oklch values
  • —Shared requestAnimationFrame scheduler: single rAF loop, multiple chart callbacks
  • —createWebGLContext: WebGL2 preferred, automatic fallback to WebGL1
  • —compileProgram: vertex + fragment shader compilation with error logging

v1.3.0-alpha — Sprint 24 Release

DAN-141TokensComponentsDocs
  • —React Native package scaffold: packages/nodus-ds-native/ (@dana/nodus-ds-native v1.3.0-alpha.0)
  • —RN token bridge: semantic color, spacing, typography, and finance-density tokens ported to React Native StyleSheet values
  • —RN CI pipeline: .github/workflows/rn-ci.yml (TypeScript check, Jest native tests, Expo bundle check, EAS Update)
  • —EAS configuration: expo-eas.json — development, preview, and production build + update channels
  • —Native README: README.native.md with live preview QR code placeholder and package split documentation

v1.2.0 — Sprint 23 Release

DAN-134TemplatesTokensComponentsDocs
  • —FX & Derivatives: FXRateCard, VolatilitySurfaceChart, DerivativesPositionTable, SwapTicketCard, OptionChainTable, HedgeEffectivenessGauge (DAN-127)
  • —Payments & Settlement: PaymentLifecycleTracker, DualControlPanel, SanctionsCheckBadge, SettlementNetCard (DAN-131)
  • —Risk Analytics A2UI templates: VaR Dashboard, Stress Testing, Limit Management (DAN-128)
  • —Developer CLI: npx nodus-ds add <component> scaffolding tool (DAN-129)
  • —Storybook 8: 55 stories for Sprint 21-22 voice and collaboration components (DAN-130)

Sprint 23: Payments & Settlement component suite

DAN-131TemplatesTokensComponentsDocs

Sprint 23: Storybook 8 — Sprint 21-22 story coverage

DAN-130TemplatesComponentsDocs

Sprint 23: FX & Derivatives component suite

DAN-127TemplatesTokensComponentsDocs

Sprint 23: Developer CLI — `npx nodus-ds add <component>`

DAN-129TokensComponentsDocs
  • —npx nodus-ds add <component> — copies component source with token hints and import snippet
  • —npx nodus-ds add --list — lists all components by category from registry.json
  • —npx nodus-ds init — detects framework, injects CSS token imports, checks Tailwind
  • —npx nodus-ds tokens [--format ts|css] — exports tokens as CSS or TypeScript const

Sprint 22: Voice interface primitives

DAN-123Components
  • —States: idle, listening (agency red), processing (temporal blue), done, error
  • —7-bar waveform animates to audio amplitude during listening (real or simulated)
  • —Bars freeze and stagger-pulse during STT processing
  • —Color rule: listening = agency red (user-initiated agentic action), processing = temporal blue (computation)
  • —Full keyboard + screen reader support; aria-pressed for listening state

Sprint 22: Runtime ThemeProvider — CSS custom property theming system

DAN-119TemplatesTokensComponentsDocs
  • —ThemeConfig interface — typed overrides for colors, typography, radius, and spacing
  • —ResolvedTheme — fully-populated theme with all fields present (no optionals)
  • —createTheme(config?) — factory that merges user overrides onto Nodus base defaults
  • —themeToVars(theme) — maps a ResolvedTheme to a Record<string, string> of --ds-* CSS custom properties
  • —serializeThemeToCss(theme, selector?) — SSR-safe serializer; outputs a CSS declaration block for injection into <style> tags

Sprint 22: Design system governance — RFC process, versioning, contribution model

DAN-125TemplatesTokensComponentsDocs

Sprint 22: Realtime collaboration UI patterns

DAN-122TokensComponentsDocs

Sprint 22: TypeDoc API documentation — TSDoc on top 20 components

DAN-121ComponentsDocs
  • —Button — variant/size/loading prop descriptions + example
  • —Badge — semantic color pillar descriptions per variant + example
  • —Card — variant/label prop docs + example
  • —Input — label/state/hint prop docs + accessible ID wiring note + example
  • —Modal — composite usage example, ModalTrigger/ModalClose inline docs

Sprint 22: Performance & bundle audit — Lighthouse CI, route splitting, Web Vitals

DAN-124TemplatesTokensComponents
  • —@next/bundle-analyzer — npm run analyze script (ANALYZE=true npm run build) opens interactive treemap for identifying bundle bloat
  • —@vercel/analytics + @vercel/speed-insights — real-user Web Vitals reporting via Vercel's edge analytics pipeline
  • —app/(site)/a2ui/templates/loading.tsx — route-level Suspense streaming boundary for all A2UI template pages; reduces TBT by deferring heavy client component hydration until after FCP
  • —.github/workflows/lighthouse.yml — Lighthouse CI workflow running on every PR and main push; audits 4 key routes with desktop preset
  • —lighthouserc.json — performance budget configuration: LCP ≤ 2500ms, TBT ≤ 200ms, performance score ≥ 0.8, accessibility score ≥ 0.9

Sprint 22: NPM package distribution — @nodus/design-system publishable build

DAN-118TokensComponents
  • —src/index.ts — top-level library entry point re-exporting all UI, finance, and pattern components plus cn and useStagedReveal utilities
  • —tsup.config.ts — dual ESM/CJS build with tree-shaking, source maps, and type declarations; all React/Next/Radix peers externalized
  • —CONTRIBUTING.md — component authoring guide covering token architecture, design rules, WCAG requirements, Strict Mode safety, and library build
  • —package.json — added main, module, types, exports, and files fields for proper NPM distribution; moved React, react-dom, next, framer-motion, lucide-react, and recharts to peerDependencies; added build:lib script and tsup ^8.4.0 devDependency; token CSS files exposed via exports subpaths

Sprint 21: Storybook links and version bump to v1.1.0

DAN-116TemplatesTokensComponentsDocs

Sprint 21: Claude Agent SDK × Nodus DS integration showcase templates

DAN-112TemplatesTokensDocs
  • —Human-in-the-loop approval interface for Claude Agent SDK require_approval flows
  • —Pending requests list with risk-level badges (critical/high/medium/low), expiry timers, and approve/deny actions
  • —Expandable detail panels: description, risk assessment, full tool_use input block, agent context metadata
  • —Fleet metrics: pending count, approved/denied today, avg response time
  • —Mock data mirrors real SDK approval request shapes (batch payments, sanctions overrides, FX forwards)

Sprint 21: Brand identity refresh — design system site visual polish

DAN-115TokensComponentsDocs
  • —Hero subtitle updated: "The design language for the autonomous era." (sharper value statement)
  • —Body copy tightened: 233 components, 41 agent patterns, semantic color in one sentence
  • —Removed local divider constant — now imported from lib/site-styles.ts
  • —All inline font stacks replaced with MONO, DISPLAY, SERIF constants
  • —sectionHeading, sectionHeadingSerif, arrowLink, indexNumber, tagPill applied throughout

Sprint 21: Changelog site — versioned release notes page

DAN-113TemplatesTokensComponentsDocs
  • —Category filter tabs: All / Components / Tokens / Templates / Docs — live counts per category
  • —SERIF italic release titles, MONO dates and ticket chips, DS-styled category pills
  • —Agency red left accent for sprint-tagged major releases, temporal blue for minor
  • —RSS feed link in page header, entry count in footer

Sprint 21: Global component search and filtering

DAN-114TemplatesComponentsDocs
  • —Search page (/search): debounced input (300ms), instant results from the full index, keyboard navigation (↑↓ + Enter), clear button, result count
  • —Category filters: All, UI Components, Finance, Patterns, A2UI Templates, Foundation — rendered in left sidebar (desktop) or collapsible panel (mobile)
  • —Domain tag filters: Agentic, Treasury, Payments, Risk, Compliance, FX, Derivatives, Liquidity, Funding, Data Viz — multi-select checkboxes
  • —Domain tags in search index: SearchEntry.domainTags field added; inferred from subcategory (e.g. finance → treasury/payments/risk/compliance/fx, agentic subcategories → agentic) and from A2UI template descriptions via keyword matching
  • —Match highlighting: query terms highlighted in result name and description

Sprint 21: Vercel deployment pipeline with CI gate

DAN-109TokensDocs
  • —CI gate for production: ci.yml now triggers on push to main (was dev-only). Added deploy job gated on needs: [typecheck, build, test, attestation] — production deployment to Vercel only fires after all CI checks pass.
  • —README status badges: CI workflow badge, Chromatic badge, and Vercel deployment badge added.
  • —Deployment pipeline confirmed: design.dana.xyz resolves to the production Next.js site. Each PR receives a unique Vercel preview URL via the Vercel GitHub integration. Chromatic visual tests run on Storybook stories for every push to main and PRs.
  • —Required secrets: VERCEL_TOKEN, VERCEL_ORG_ID (team_pamm24pZnHtoePWFtJldYofV), VERCEL_PROJECT_ID (prj_CmSvgVn3weuFQ0NCyQnTXirKCPAW) must be set in GitHub repo secrets to activate the CI-gated deploy job.
  • —Dashboard note: Disable Vercel's auto-deploy for the production branch (Project → Settings → Git) to prevent a double-deploy race with the GitHub Actions deploy job.

Sprint 20: Advanced agentic interaction templates

DAN-105TemplatesTokensComponents

Interactive token explorer: live preview + cross-reference

DAN-108TokensComponentsDocs

S20: Agent Process Portfolio Showcase

DAN-107TemplatesTokensComponentsDocs
  • —Hero + System Metrics — 6-stat grid: 150+ templates, 325 components, 20 sprints, 153 commits, 216 docs, 8 agents
  • —Agent Roster — Full 8-agent table: Chief Visionary, Component Engineer, UX Designer, Template Architect, Quality & Accessibility Lead, Documentation Architect, DevOps & Release Engineer, Brand & Portfolio Lead. Color-coded by tier (red/blue/gold/gray), each with domains and responsibility description.
  • —S01–S20 Sprint Timeline — 20-cell grid showing focus area and deliverables per sprint. Color encodes phase: blue=infrastructure, gold=quality gates, red=expansion.
  • —6-Phase Agentic Workflow — Brief → Design → Validate → Document → Ship → Showcase. Each phase shows owning agent, action taken, and output artifact.
  • —Quality Signal Table — 8 metrics: WCAG AA, TypeScript strict, visual regression, token compliance, font floor, touch targets, mobile responsiveness, component docs — all passing.

Storybook coverage: 26 missing UI component stories

DAN-106TokensComponentsDocs

Mobile-first component documentation for all 216 UI components

DAN-98TemplatesTokensComponentsDocs
  • —Works As-Is → --ds-color-outcome-positive (green)
  • —Touch Target → --ds-color-validation (gold)
  • —Stacks / Scroll → --ds-color-temporal (blue)
  • —Mobile Adapted → --ds-color-temporal (blue)
  • —works-as-is: 57 components (display, typography, small indicators)

FX Hedging Workflow A2UI template

DAN-100TemplatesDocs

AdaptiveChartContainer: responsive chart/metric card pattern

DAN-101TokensComponents
  • —Desktop (≥768px): renders the full chart passed as the chart prop (LineChart, BarChart, AreaChart, etc.)
  • —Mobile (<768px): collapses to a compact 64px metric card showing key stat + period-over-period delta + optional sparkline trend
  • —chart — any ReactNode (full chart component)
  • —metricValue / metricLabel — primary KPI for mobile card
  • —metricFormat — currency | rate | percent | bps | integer | decimal

Token Fix: A2UI_LIGHT categorical color tokens

DAN-30TemplatesTokens
  • —blueBg → color-mix(in srgb, --ds-viz-cat-1 10%, transparent)
  • —purple → var(--ds-viz-cat-4, #7B52CC) (violet — correct categorical color for tier differentiation)
  • —purpleBg → color-mix(in srgb, --ds-viz-cat-4 10%, transparent)

Payment Approval Workflow Template

DAN-99TemplatesComponentsDocs
  • —Payment Queue — 5 payment entries across SWIFT, SEPA, Fedwire rails with amount, beneficiary, status, and priority. Click-to-select panel detail view.
  • —Sanctions Screening — AI screener panel showing OFAC SDN, EU Consolidated, UN Security Council, HM Treasury, and OFSI list results. Clear/review/match/pending states with confidence detail.
  • —Dual-Control Authorization — Maker/Checker approval ceremony. Independent authorization steps with timestamp and name attribution. Sequential gate enforcement.
  • —Release Gate — Pre-flight checklist (Sanctions, Maker Auth, Checker Auth, Value Date, Nostro Funds) before payment submission to network. Animated releasing state.
  • —Settlement Tracking — UETR display, 5-step settlement timeline (Initiated → Authorized → Submitted → Acknowledged → Settled), completion timestamp.

S18: Mobile Responsiveness + Workflow Templates (DAN-95, DAN-96)

TemplatesTokensDocs
  • —.ds-stat-grid — 4-column stat rows collapse to 2×2 on ≤600px (homepage, A2UI listing)
  • —.ds-foundation-grid — 3-column foundation grids → 2-col at 900px, 1-col at 600px
  • —.ds-a2ui-scroll-container — complex financial templates scroll horizontally on mobile with scroll indicators
  • —a2uiWrapper padding now uses clamp() for fluid mobile-to-desktop scaling
  • —app/(site)/page.tsx — stat row grid responsive

S17: A2UI Template Expansion — 142 Templates

DAN-90TemplatesTokensComponents
  • —Agent Pipeline Dashboard: Multi-stage pipeline table with per-agent assignment, all-pipelines list, event log, 4 metrics cards.
  • —Agent Coordination Center: 12-agent registry by tier (orchestrator/specialist/utility), dispatch queue with priority levels, activity log.
  • —AI Model Comparison: Side-by-side opus/sonnet/haiku evaluation with quality scoring (faithfulness/groundedness/conciseness), cost/latency metrics, eval history.
  • —Agent Performance Analytics: 7-agent leaderboard with task throughput, success %, token cost, escalations; daily volume chart; error log.
  • —Prompt Engineering Workspace: Prompt library sidebar, variable injection panel, version history (v1–v4), A/B test runner results.

S16: Agentic AI Pattern Library — 16 New Components

DAN-88TokensComponents
  • —AgentThinkingIndicator: Streaming token-by-token output visualization with cursor blink and temporal-blue pulse. Distinct from ThinkingState (reasoning trace) — renders raw stream.
  • —AgentConfidenceBar: Horizontal confidence score bar with outcome-based color tiers (low/moderate/high). Role: meter, context label, numeric pct.
  • —AgentSourceCitation: Structured provenance citation list — source type badges (DOC/DB/WEB/AGT/VLT/USR), relevance scores, excerpts, retrieval timestamps.
  • —AgentDecisionTree: Recursive branching decision visualization — taken/skipped/pending nodes, terminal pass/fail outcomes, depth-indented rendering.
  • —MultiAgentOrchestrationView: Horizontal pipeline view for multi-agent workflows — step nodes with status, connector lines, derived overall state.

S15: Capability Patterns

DAN-86ComponentsDocs
  • —AgentCapabilityMatrix: Scannable grid of agents × capability dimensions (read/write/approve/transact/escalate). Blue=granted, gold=conditional, red=denied. Agent sigil + name + role in identity column. Click-to-focus row for drill-down. Legend bar.
  • —CapabilityGrant: Single permission grant card with condition badges (time-bounded/value-bounded/approval-gated), status chip, meta row, collapsible audit trail (via ProvenanceChain), and revoke trigger.
  • —CapabilityRevokeFlow: Two-step revocation ceremony. Step 1: impact preview (will-fail / may-degrade / unaffected tasks). Step 2: mandatory written reason — enforces non-repudiable audit trail.
  • —Foundation page: /foundation/capability-patterns — color semantics table, live demos for all three components, prop tables, anti-patterns, and design rules.
  • —Exported from components/patterns/index.ts, registered in registry-extended.ts, added to lib/sidebar-nav.ts.

S15: Post-deploy Smoke Test Script

DAN-84TemplatesComponents
  • —Routes covered: all 215 UI components (/components/:slug), 47 patterns (/patterns/:slug), all A2UI templates (/a2ui/templates/:slug), plus ~35 static site routes — ~300+ routes total
  • —SSG failure detection: checks for React null/render crashes, useContext missing provider crashes, and module resolution / chunk load failures
  • —Output: console pass/fail summary + scripts/smoke-test-report.md (route table with status, duration, failure signals)
  • —Timeout: 30s total (parallel fetch, 15s per-route limit)
  • —Exit codes: 0 = all pass, 1 = failures found, 2 = global timeout or fatal error

S15: Theme Configuration Template

DAN-85TemplatesTokensComponents
  • —3 preset skins: Nodus Default (base), Enterprise Dark (dark surface trading floor), High-Contrast Accessible (WCAG AAA)
  • —SkinProvider component: wraps any React subtree in data-skin attribute for scoped CSS custom property overrides
  • —Live Preview panel: header bar, KPI row, status badges, and mini payment table rendered in the active skin
  • —Token Overrides table: shows every --ds-* token that changes per skin with color swatch, base hex, and override hex
  • —CSS Export panel: generates ready-to-drop [data-skin="..."] { } CSS block with copy-to-clipboard

S14: Real-time Hooks

DAN-80TokensComponentsDocs
  • —Returns { chunks, status, latency, start, reset }
  • —status: "idle" | "streaming" | "complete" | "error"
  • —latency: time-to-first-token in ms (simulated)
  • —Options: content, chunkInterval (ms), chunkSize (chars), ttft (ms)
  • —React Strict Mode safe — all timers cleared on unmount

S13: Prop Stress-Tester

DAN-77Components
  • —Test matrix per component: empty strings, 200+ char strings, unicode, special chars, all enum values, boundary numerics (0, 1, -1, out-of-range), null/undefined optional props, empty/large arrays
  • —Components covered: AgentIdentityBadge, ProvenanceChain, StreamingResponse, ApprovalQueue, AgentConflictPanel, ChartTooltip, ChartAnnotation, ConfidenceMeter, ConsentFlow, EscalationBanner, ThinkingState
  • —Report written to scripts/stress-test-report.md on each run

S13: Error State Pattern System

DAN-79TokensComponentsDocs
  • —severity="fatal" → --ds-color-outcome-negative (red) — unrecoverable, no retry
  • —severity="recoverable" → --ds-color-validation (gold) — human action resolves it
  • —severity="transient" → --ds-color-temporal (blue) — auto-retry in progress

S12: Enhanced Component Search with Faceted Filtering

DAN-74TemplatesTokensComponentsDocs
  • —components/site/CommandSearch.tsx — full rewrite
  • —Filter bar with 3 facets: Category (All / UI / Patterns / A2UI / Foundation), Status (All / Stable / Experimental), Since (All Sprints / S9+ / S11+)
  • —Match highlights: query text highlighted in gold (--ds-color-validation) in result names and descriptions
  • —Rich result rows: name + status badge + sprint label + description + subcategory metadata inline
  • —Active indicator bar (--ds-color-temporal) on selected result

S12: Debt Capital Markets Template

DAN-75TemplatesTokensDocs
  • —app/(site)/a2ui/templates/debt-capital-markets/page.tsx — new template
  • —Mandate: deal terms panel + mandate detail + ProvenanceChain
  • —Structuring: AgentConflictPanel (Pricing Agent vs. Risk Agent on yield spread) + ProvenanceChain
  • —Book-building: coverage ratio bar, investor order book (10 investors), EscalationBanner when coverage < 100%, ApprovalQueue for board sign-off
  • —Settlement: deal summary, 6-item checklist, full 11-step ProvenanceChain

S11: Component Versioning System

DAN-71TemplatesComponentsDocs
  • —registry-extended.ts — ComponentEntry interface extended with four new optional fields:
  • —version?: string — semver string (e.g. "1.0.0")
  • —since?: string — sprint label when component was introduced (e.g. "S1")
  • —deprecated?: boolean — triggers DeprecationBanner on component pages when true
  • —deprecationNote?: string — migration guidance shown in the banner

S10: v1.0 Release Notes Foundation Page

DAN-69TokensComponentsDocs
  • —app/(site)/foundation/release-notes/page.tsx — new Foundation page with:
  • —Stat strip: 9 sprints · 233+ components · 1,534 tests · 200+ tokens
  • —ProvenanceChain visual metaphor — S1→S9 chain using source → transform → inference → output step types to map the system's evolution
  • —Sprint timeline — S1–S9 grouped sections (newest first), collapsible per sprint, EXPAND ALL / COLLAPSE ALL controls
  • —Status badges — NEW (blue) · UPDATED (gold) · FIXED (green) with WCAG AA contrast

S10: Intercompany Netting Template

DAN-70TemplatesTokensComponentsDocs
  • —New A2UI template at /a2ui/templates/intercompany-netting
  • —Replaced hardcoded hex colors with var(--ds-*) semantic tokens
  • —Fixed setTimeout calls — cleanup-safe useFlowRunner hook with useRef + useEffect cleanup
  • —Canonical footer: left spacer · center Templates link · right skill tags
  • —Registered in lib/sidebar-nav.ts and lib/search-index.ts

S9: TypeScript Type Exports Barrel

DAN-65TemplatesTokensComponentsDocs
  • —components/patterns/ProvenanceChain.tsx — exported ProvenanceStep, ProvenanceStepType, ProvenanceStepStatus, ProvenanceChainProps
  • —components/patterns/ApprovalQueue.tsx — exported ApprovalItem, RiskLevel, ApprovalQueueProps
  • —components/patterns/ConfidenceMeter.tsx — exported ConfidenceMeterProps, ConfidenceSingleAxis, ConfidenceDualAxis, ConfidenceThresholds
  • —components/patterns/ConsentFlow.tsx — exported ConsentStep, ConsentPermission, ConsentFlowProps
  • —components/patterns/index.ts — added ConflictLayout, ResolutionOutcome, AvatarUser, AvatarStackProps, SourceType, and all newly exported pattern types

S9: Iconography System

DAN-67TokensComponentsDocs

S8: Light Mode Parity Audit

DAN-64TokensComponents
  • —AgentIdentityBadge — CapabilityChip (requiresApproval), PanelBadge approval notice
  • —EscalationBanner — "ESCALATION — HUMAN REQUIRED" header text
  • —StreamingResponse — "CONTINUE RESPONSE" button text (--ds-surface-ground on gold was 2.4:1)
  • —AgentConflictPanel — validation button variant, SELECTED badges (×3), consequence warning text, mode header badge
  • —EscalationBanner — suggested action body text

S8: Token Explorer Live CSS Reads

DAN-62TokensComponentsDocs
  • —Token Explorer (/foundation/tokens) — all color swatches now read live values from the browser via getComputedStyle, replacing hardcoded hex fallbacks
  • —Color tokens resolved by setting background-color: var(--token) on probe spans and reading computed RGB — correctly resolves var() chains
  • —Light and dark values pre-loaded at mount by temporarily toggling data-theme on documentElement (synchronous, no visible flash)
  • —Contrast ratios recompute from live hex values after hydration
  • —darkPreview toggle picks from the pre-loaded dark map — no re-read required on toggle

S8: Motion Token Migration

DAN-61TemplatesTokensComponentsDocs
  • —Hardcoded transitions eliminated — 39 files migrated from raw 0.15s/80ms/120ms/300ms values to --ds-motion-* semantic tokens
  • —80ms → var(--ds-motion-interaction) var(--ds-motion-easing-productive) (hover/click micro-feedback)
  • —120ms / 150ms / 0.15s → var(--ds-motion-duration-fast) var(--ds-motion-easing-productive) (state changes, badge hover)
  • —300ms → var(--ds-motion-deliberate) var(--ds-motion-easing-productive) (bar fills, major reveals)
  • —0.12s linear / 0.15s linear → var(--ds-motion-duration-fast) linear (progress bars)

S7: Token Explorer

DAN-58TokensComponentsDocs
  • —Token Explorer (/foundation/tokens) — Rebuilt as a live, searchable three-layer reference:
  • —Search — filters across all ~200 tokens (name + description) with layer badges (primitive / semantic / component)
  • —Dark/Light preview toggle — swatch panels and live component previews update reactively
  • —Contrast ratios — every color token shows ratio vs current surface with WCAG AA/AAA grade badge
  • —WCAG note on --ds-color-validation — "dark mode only — 5.96:1 on dark surface" per DAN-57 audit

S7: Portfolio Risk Overview Template

DAN-59TemplatesTokensComponents
  • —Portfolio Risk Overview (/a2ui/templates/portfolio-risk-overview) — Flagship morning risk dashboard showcasing all 10 agentic pattern components in a single-scroll 3-column layout.
  • —AgentIdentityBadge × 3 — Risk Orchestrator (active), Credit Risk Agent (idle), Market Microstructure (pending) in card surface
  • —ProvenanceChain — 8-step overnight analysis pipeline with 2 pending steps
  • —StreamingResponse — Portfolio commentary arriving live with token-rate display
  • —ThinkingState — Market Microstructure agent reasoning with partial trace

S6: Agentic Component Playground

DAN-55TokensComponentsDocs
  • —Agentic Playground (/foundation/agentic-playground) — Interactive reference page for all 10 core agentic pattern components. No external Storybook dependency.
  • —AgentIdentityBadge — role (5 tiers), surface (inline/card/panel), state (5 states), offline and timeout edge states
  • —ProvenanceChain — fresh, stale (staleness threshold demo), and partial completion variants
  • —StreamingResponse — live stream simulation, truncated output, token timeout silence detection
  • —ApprovalQueue — 4 items at mixed risk levels, partial completion (pre-decided items) edge state

S6: Live FX Monitor Template

DAN-56TemplatesComponents
  • —Live FX Monitor (/a2ui/templates/live-fx-monitor) — Real-time FX desk monitor template demonstrating the design system under rapid state changes.
  • —Simulated live rate feeds for EUR/USD, GBP/USD, USD/JPY, USD/CHF via setInterval with strict mode safe cleanup
  • —Interactive rate ticker cards — click to switch chart focus; threshold proximity warning in gold
  • —Intraday sparkline chart with ChartAnnotation (threshold line + live dot observation)
  • —AgentIdentityBadge (inline + card) for FX-Executor (EXECUTOR role) — state transitions: pending → active → idle

S5: Cascaded Conflict (3+ Agents) in AgentConflictPanel

DAN-51TokensComponents
  • —AgentConflictPanel — Added conflictLayout?: 'parallel' | 'ranked' | 'auto' prop (default: 'auto'). Also added ConflictLayout type export.
  • —auto: uses parallel for exactly 2 positions, ranked for 3+.
  • —ranked: new RankedConflictView — positions sorted by confidence descending; each card is full-width with #N rank badge; expandable reasoning per card; radio-style selection; 5+ positions collapse to top 5 with SHOW ALL (N) POSITIONS control.
  • —Post-resolution (ranked): all positions remain visible — winner highlighted with --ds-color-outcome-positive border + ACCEPTED badge; others rendered at 50% opacity with DEFERRED label.
  • —Dynamic header: "{N}-WAY CONFLICT DETECTED" when N >= 3; "Agents disagree" for N=2.

S5: Agent Unavailable Edge States

DAN-50TokensComponents
  • —AgentIdentityBadge — Added agentOnline?: boolean prop. When false: monogram renders at 50% opacity with --ds-color-outcome-negative border and static red corner dot; inline variant shows "(unavailable)" suffix and muted name; card variant adds an AGENT UNAVAILABLE banner strip with negative background; panel variant adds a full-width offline banner with last-seen timestamp. Role badge shifts to neutral gray. StatusDot hidden in panel variant (no live status to show).
  • —EscalationBanner — Added targetAgentOnline?: boolean prop. When false: a TARGET AGENT OFFLINE — CANNOT ACCEPT ESCALATION notice strip renders above the gold escalation header (using --ds-color-outcome-negative); suggestedAction is overridden to "REASSIGN to an available agent"; the "SUGGESTED ACTION" label becomes "REQUIRED ACTION" in negative color. Gold banner structure unchanged.
  • —ConsentFlow — Added agentOnline?: boolean prop. When false: a REQUESTING AGENT OFFLINE — GRANT UNAVAILABLE banner renders above the step indicator (negative color); GRANT button is disabled (muted, cursor: not-allowed, opacity: 0.5); DENY button label changes to DISMISS REQUEST at step 0.
  • —ThinkingState — Added agentOnline?: boolean prop. When false: left border → --ds-color-outcome-negative (static, no pulse); label → AGENT OFFLINE — REASONING UNAVAILABLE; elapsed timer suppressed (reasoning never started); component renders even with no trace/thinking state.
  • —Agent unavailable = --ds-color-outcome-negative throughout — offline is a system failure state, not a validation gate

S5: Partial Completion Edge States

DAN-49TokensComponents
  • —ApprovalQueue — Added initialDecided?: Record<string, "approved" | "rejected"> prop for externally pre-decided items. Header text switches to N REMAINING · M DECIDED format in partial state. Remaining items get a gold left border (--ds-color-validation). Decided items render with outcome tint background (green/red 8% mix) and an APPROVED/REJECTED badge; non-interactive. Internal outcomes state tracks both directions.
  • —StreamingResponse — Added isTruncated?: boolean and onContinue?: () => void props. When isTruncated=true and not in timeout state: left border shifts to --ds-color-validation, status label shows TRUNCATED, and a gold notice block renders below content: RESPONSE TRUNCATED — OUTPUT INCOMPLETE · REVIEW BEFORE ACTING with an optional CONTINUE RESPONSE button.
  • —ThinkingState — Added isInterrupted?: boolean prop. When true: left border → --ds-color-validation, pulse animation stops, label → REASONING INTERRUPTED — PARTIAL TRACE, elapsed counter → gold, trace gets a gold truncation marker — REASONING INTERRUPTED — INCOMPLETE — appended, footer shows PARTIAL TRACE ONLY — CONCLUSION NOT REACHED · REVIEW BEFORE ACTING. Early-returns guard updated to permit render when isInterrupted=true even with no active isThinking.
  • —ProvenanceChain — Added status?: "complete" | "pending" | "failed" | "skipped" per step (backward-compatible; default = "complete"). When a chain has any non-complete step: muted rendering (opacity 0.6, --ds-text-muted label, --ds-border-separator indicator dot with dashed outline, model/confidence/timestamp suppressed), a gold STOPPED HERE truncation divider before the first incomplete step, and a gold footer CHAIN INCOMPLETE — N OF M STEPS EXECUTED. failed steps additionally use --ds-color-outcome-negative for the status badge.
  • —Partial completion = --ds-color-validation (gold) throughout — partial output is a validation gate, not a system failure

S5: Stale Provenance Edge States

DAN-48TokensComponents
  • —ProvenanceChain — Added stalenessThresholdMs?: number (default 1h) prop and lastUpdatedAt?: string per step. Steps with source data older than the threshold show a gold dot indicator and append (stale · {age}) to the label in --ds-color-validation. When any step is stale, a gold warning header row renders: ⚠ DATA STALENESS — OLDEST SOURCE: {age} AGO. Stale data is never hidden — it requires human judgment.
  • —ChartInteractionLayer / ChartTooltip — Added staleAnalysisAge?: string prop. When source="agent" and this prop is set, a gold stale warning row renders at the top of the tooltip: ⚠ STALE ANALYSIS — {age} OLD, above the standard agent highlight header.
  • —ChartInteractionLayer / ChartAnnotation — Added lastAnalyzedAt?: string, stalenessThresholdMs?: number (default 1h), and staleAnalysisAge?: string (pre-formatted override) props. Stale annotations display a gold outline ring on the marker pin/label and prepend ⚠ STALE ANALYSIS — {age} OLD to the expanded detail panel.
  • —ConfidenceMeter — Added staleEvidenceAge?: string to both single-axis and dual-axis modes. When set, a gold stale warning renders below the meter(s): ⚠ BASED ON STALE EVIDENCE — {age} OLD. The displayed confidence score is never degraded — the score is accurate for the evidence it had.
  • —Stale provenance = --ds-color-validation (gold) throughout — signals quality degradation requiring human judgment, not system failure

S5: Timeout Edge States

DAN-47TokensComponents
  • —AgentIdentityBadge — Added timeoutAt?: string and timeoutThresholdSecs?: number props. When timeoutAt is set: caution-colored border and status dot, inline variant appends (no response · Ns), card variant shows AGENT TIMEOUT — NO RESPONSE banner, panel variant shows full caution banner with descriptive copy. No pulse while timed out.
  • —StreamingResponse — Added tokenTimeoutMs?: number (default 30s), onRetry?: () => void, onCancel?: () => void props. Detects token silence via useEffect with setTimeout cleanup (Strict Mode safe). On timeout: left border shifts to caution, streaming cursor removed, notice + RETRY REQUEST / DISCARD actions appear. Partial content is always preserved.
  • —ThinkingState — Added timeoutSecs?: number (default 120), onTimeout?: () => void, onRetry?: () => void, onEscalate?: () => void props. When elapsed >= timeoutSecs: pulse stops, border → caution, label → "TIMEOUT — REASONING HALTED", elapsed counter freezes at threshold, trace footer appended, retry/escalate actions rendered. onTimeout fires exactly once via ref guard.
  • —Timeout = --ds-color-outcome-caution (orange) throughout — degraded waiting state, not failure
  • —No agency red (agent has not acted; it has failed to respond)

S5: Agent Simulation Lab Template

DAN-45TemplatesTokensComponentsDocs
  • —Agent Simulation Lab (app/(site)/a2ui/templates/agent-simulation-lab/page.tsx) — Treasury decision backtesting sandbox. Tests AI agent recommendations against historical stress scenarios.
  • —Scenario selector — entity dropdown, 4 stress presets (SVB Stress, Rate Shock, Yen Carry Unwind, Base Case) with date ranges
  • —Agent Stream tab — StreamingResponse component showing live agent reasoning via useStagedReveal (React Strict Mode safe); dual-axis ConfidenceMeter on completion
  • —Results tab — Decision comparison table (AI recommendation vs actual outcome), per-decision ConfidenceMeter scores, alpha/match-rate summary cards
  • —Provenance tab — ProvenanceChain showing source → transform → inference → output decision chain with model attribution

Hotfix: Treasury Daily Brief SVG hour label font floor

DAN-44TemplatesDocs
  • —Treasury Daily Brief — Changed SVG hour label fontSize from 9 to 10 (app/(site)/a2ui/templates/treasury-daily-brief/page.tsx:183). Enforces the 10px font floor required by CLAUDE.md.

S4: TypeScript Strict Pass + Visual Regression Coverage

DAN-41TemplatesTokensComponentsDocs
  • —TypeScript strict pass — tsc --noEmit now exits clean across the entire codebase. Fixed 60+ type errors across Sprint 1–3 additions:
  • —registry-extended.ts — Added missing TokenFamily values ("color", "viz", "fin") and Subcategory values ("coordination", "dataviz")
  • —tsconfig.json — Added components/__tests__/ and figma/ to the exclude list (test files are type-checked by vitest; figma Code Connect files require @figma/code-connect which is a separate tool)
  • —6 A2UI templates — Fixed narrow status type causing unreachable comparisons in KPICard/MetricCard component functions (bank-fee-analysis, budget-vs-actuals, credit-rating-monitor, derivatives-reporting, green-bond-tracker, ma-treasury)
  • —foundation/agent-identity/page.tsx — Changed state="running" → state="active" to match DotState type

Treasury Daily Brief: Flagship Agentic Template

DAN-42TemplatesComponents
  • —Treasury Daily Brief (/a2ui/templates/treasury-daily-brief) — Flagship template exercising the full agentic component stack in a single production-ready layout. Composes all six S3 agentic components: AgentIdentityBadge (card surface, which agent produced the brief), ProvenanceChain (6-source data trail with confidence scores), StreamingResponse (LLM-generated treasury commentary), ApprovalQueue (4 risk-rated actions), AgentConflictPanel (conflict mode: FX Agent vs. Risk Agent on EUR hedge), and ChartInteractionLayer (annotated intraday liquidity chart with comparison bar and three agent annotations). Showpiece template demonstrating why the design system exists.

S3: Token Compliance Audit + Font Floor Fix (DAN-32/35/36)

TokensComponents
  • —Font floor enforcement — Raised all SVG and inline fontSize values below 10px to 10px across 11 finance chart components (AreaChart, BacktestingChart, BenchmarkComparisonChart, DivergingBarChart, GroupedBarChart, HeatmapChart, LearningMultiplierGraph, LineChart, ScatterChart, ScatterPlot, YieldCurveViewer) and 3 agentic pattern components (AgentConflictPanel, AgentIdentityBadge, ChartInteractionLayer)
  • —Token compliance — Replaced color: "#fff" in VaultBoundary.tsx with var(--ds-text-inverse)
  • —chart-tokens.ts — tickTextProps.fontSize raised from 9 to 10

A2UI Finance Sprint HB5: +20 Templates → 121 total (DAN-4 HB5)

TemplatesComponents
  • —Netting Engine (/a2ui/templates/netting-engine) — multilateral net position matrix, settlement instructions, cycle compression
  • —Credit Line Management (/a2ui/templates/credit-line-management) — facility utilization bars, drawdown history, covenant compliance
  • —Custody & Clearing (/a2ui/templates/custody-clearing) — settlement queue, CSD connectivity, fails management, custody positions
  • —FX Overlay Manager (/a2ui/templates/fx-overlay-manager) — currency exposure vs hedge bars, return attribution waterfall, rebalancing triggers
  • —Liquidity Coverage Ratio (/a2ui/templates/liquidity-coverage-ratio) — Basel III HQLA composition, NCO categories, LCR trend chart

Multi-agent Coordination + Data Viz Interaction Components (DAN-29/DAN-30)

TemplatesTokensComponents
  • —Multi-agent conflict resolution panel covering three interaction modes: conflict, delegation, and override
  • —Conflict mode: parallel position display with agent identity, confidence meter, and reasoning trail; human selects winner or forces deferral
  • —Delegation mode: explicit agent assignment or orchestrator hand-off with routing legibility before commit
  • —Override mode: surfaces original agent action verbatim; requires explicit acknowledgement before applying override
  • —Composes AgentIdentityBadge, ProvenanceChain, and ConfidenceMeter

Agentic Component Library: Spec Compliance Pass (DAN-23/25/26/27/28)

TokensComponents
  • —Removed banned fake streaming simulation (setInterval character-by-character reveal)
  • —Now displays content immediately; isStreaming prop drives the cursor/border only
  • —StreamingDot disappears instantly on completion — no border-color transition (DAN-18 spec)
  • —Changed all colors from --ds-color-agency (red) to --ds-color-validation (gold)
  • —"Waiting for human" is an epistemic state, not an authority state (DAN-18 taxonomy)

Gap Fill Sprint: Risk, Payments, Compliance, Reconciliation

DAN-22Components
  • —ES (CVaR) at configurable confidence level with VaR comparison and ES/VaR multiplier
  • —Optional limit utilization bar; absolute loss display when notional provided
  • —Credit rating with agency label and tier color coding (IG=green, sub-IG=caution, distressed=agency red)
  • —Badge and card variants; outlook/watch status display
  • —Historical and hypothetical stress scenarios with impact bars and pass/warn/fail status

Portfolio Domain Gap Components

DAN-21Components
  • —Portfolio vs. benchmark cumulative return comparison with excess return (alpha) badge
  • —Dashed benchmark line vs. solid portfolio line; color-coded excess return display
  • —Fixed-income duration and convexity metrics: modified, effective, Macaulay duration, convexity, DV01
  • —Optional ±100bps rate sensitivity scenario using first/second-order approximation
  • —Par, spot (zero), and forward yield curve on single SVG chart with tenor dots

Finance Sprint: +12 A2UI Templates (DAN-4 Heartbeat 3)

TemplatesTokensDocs
  • —Business unit capital envelope allocation vs. deployed YTD, ROIC tracking with hurdle rate bars
  • —Capital request pipeline with status tracking (approved/pending/rejected)
  • —Per-entity ESG scores with E/S/G sub-score breakdown bars; framework disclosure coverage (GRI, SASB, TCFD, CDP, SFDR)
  • —Emissions stacked bar chart by scope 1/2/3 with YoY trend
  • —SANCTIONS/AML/PEP/FATF screening queue with risk tier badges and rule performance table

Funding & Capital Domain + Shared Finance Primitives (DAN-19 / DAN-20)

TokensComponents
  • —Compact chip for ISIN, CUSIP, SEDOL, FIGI, and RIC instrument identifiers
  • —Standard label + monospace value side-by-side with optional click-to-copy
  • —Type-coded colors: ISIN/CUSIP → temporal, SEDOL/FIGI → validation, RIC → muted
  • —Tenor label chip (ON, TN, 1W, 3M, 1Y, 5Y, 30Y and free-form)
  • —Duration-band color coding: overnight → positive, short → temporal, medium → validation, long → muted

Sankey & Treemap Chart Enhancements

DAN-11TokensComponents
  • —Added density prop ("default" | "compact") — compact reduces node padding and label sizes
  • —Fixed font sizes to meet 10px floor: node labels 11px (compact: 10px), sublabels 10px, flow labels 10px
  • —All colors already token-native — dark/light mode verified
  • —Added density prop ("default" | "compact")
  • —Fixed dynamic font sizes to enforce 10px minimum (Math.max(10, ...) replacing Math.max(8, ...))

Risk Analytics Domain Primitives

DAN-9Components
  • —Semicircular gauge showing VaR limit utilization 0–100%
  • —Four severity tiers: safe → positive | elevated → validation | warning → caution | critical → negative
  • —Threshold tick marks at configurable breakpoints (default: 60% / 80% / 95%)
  • —Shows current value, limit, confidence level, and horizon in footer
  • —Multi-band horizontal bar for counterparty, sector, and concentration limits

FX & Derivatives Domain Primitives

DAN-8TokensComponents
  • —Horizontally scrolling rate ticker for live FX rate display
  • —Continuous loop animation, pause-on-hover, configurable speed
  • —Severity-colored directional change indicators via outcome token system
  • —Designed for top-of-screen or panel header placement
  • —Two modes: predefined pairs list (trading desk) or free-select from full ISO 4217 currency list

A2UI Template Anatomy Standard + Heartbeat 2 Templates (DAN-13/14/15)

TemplatesTokens
  • —5-slot system: header · metric strip (conditional) · tabs · content · footer
  • —Footer canonical structure (left: toggle / center: ← Templates / right: skill tags)
  • —3 layout patterns: table, grid, flow
  • —Data contract shape — naming conventions, status unions, approved mock entities
  • —Agent-context props: AgentRef shape, status semantics, trust chain, token budget display

Full CLAUDE.md Enforcement Pass + Token Alias Fix + Navigation Redesign

TemplatesTokensComponents
  • —apps/nodus-design-system/CLAUDE.md — full design context, token rules, anti-patterns, quality checklist
  • —.claude/rules/design-system.md — auto-loading rules for any file touch in the design system
  • —lib/site-styles.ts — shared style registry (single source of truth for site page styles)
  • —lib/use-staged-reveal.ts — React strict mode safe staged reveal hook
  • —--ds-bg-default → var(--ds-surface-ground)

Finance DS Adoption + Spacing Token Migration + Package Exports

TokensComponents
  • —shared/card-states.tsx — Hand-rolled shimmer keyframes + SkeletonBar replaced with Skeleton; custom error/empty states replaced with EmptyState + Button
  • —StatusBadge.tsx — Standalone <span> badge converted to thin adapter over DS Badge with outcome color overrides via style prop
  • —ThresholdAlert.tsx — Raw <button> action replaced with Button variant="outline"
  • —PaymentTracker, PaymentQueue, ExceptionQueue, RegulatoryReport, ScreeningResultCard, RiskDisclosure — Inline badge <span> elements (8-10 style props each) replaced with Badge component
  • —ConfirmationCard, SanctionsCheck, SurveillanceQueue (compliance) — status/severity/count badges

Test Coverage Expansion + Package Hardening

TokensComponents
  • —DANA-1491: CashFlowTimeline, LiquidityGauge, SettlementLadder, TransactionFeed, CurrencyPair (32 tests)
  • —DANA-1499: CardLoading, CardError, CardEmpty shared states, ThresholdAlert urgency levels, CashPositionCard data/empty/loading/error states, CounterpartyRiskCard score bands (33 tests)
  • —DANA-1500: FXRateCard states + change display, PaymentStatusTracker pipeline/badges/maker-checker, RatioBar meter + threshold marker, TrendBadge direction icons + formatted values (30 tests)
  • —DANA-1492: Table column sort toggle, MultiSelect tag add/remove/clear, MFAInput digit entry + paste, ConfirmModal confirm/cancel callbacks (26 tests)
  • —DANA-1493: CheckboxGroup select/deselect/select-all, Select open/close/keyboard, SegmentedControl switch, Tabs panel switching (24 tests)

Auth & Identity Components + Responsive Infrastructure

TokensComponents
  • —MFAInput — 6/8-digit OTP code entry with auto-advance, arrow key navigation, paste from any digit, countdown timer, shake-on-error animation
  • —PasswordStrength — 4-segment visual meter with requirement checklist, role="meter" ARIA, transition on segment fill and checkmark scale
  • —RoleBadge — Treasury persona badge (analyst, manager, director, controller, compliance_officer) with color-mix() token-derived backgrounds for theme safety
  • —APIKeyDisplay — Masked API key with reveal toggle, copy-to-clipboard with success state, metadata row, revoke action
  • —LoginCard — Full auth form with email/password, password visibility toggle, PasswordStrength integration, SSO provider slots, brand slot, error banner with slide-down animation
NODUS DESIGN SYSTEM · 243 ENTRIESSPRINT RELEASE NOTES →