diff --git a/apps/website/src/app/page.tsx b/apps/website/src/app/page.tsx
index cdfdd8a26..4766d0f67 100644
--- a/apps/website/src/app/page.tsx
+++ b/apps/website/src/app/page.tsx
@@ -3,6 +3,7 @@ import { LogoRibbon } from '../components/landing/LogoRibbon';
import { YesWall } from '../components/landing/YesWall';
import { FeatureBlock } from '../components/landing/FeatureBlock';
import { StackDiagramSection } from '../components/landing/StackDiagramSection';
+import { HomeConceptGrid } from '../components/landing/HomeConceptGrid';
import { DemoShowcase } from '../components/landing/DemoShowcase';
import { MediumSwitcher } from '../components/landing/MediumSwitcher';
import { SECTION_MEDIA } from '../lib/section-media';
@@ -46,6 +47,8 @@ export default async function HomePage() {
caption="The chat surface never imports a runtime SDK — only the contract."
/>
+
+
{/* Interactive demo showcase */}
diff --git a/apps/website/src/app/pilot-to-prod/page.tsx b/apps/website/src/app/pilot-to-prod/page.tsx
index 3972089ab..0fa5d9537 100644
--- a/apps/website/src/app/pilot-to-prod/page.tsx
+++ b/apps/website/src/app/pilot-to-prod/page.tsx
@@ -8,6 +8,8 @@ import { BrowserFrame } from '../../components/ui/BrowserFrame';
import { WhitePaperBlock } from '../../components/landing/WhitePaperBlock';
import { Promises } from '../../components/landing/Promises';
import { FinalCTA } from '../../components/landing/FinalCTA';
+import { DiagramSection } from '../../components/landing/DiagramSection';
+import { PilotJourney } from '../../components/docs/diagrams';
import { createPageMetadata } from '../../lib/site-metadata';
export const metadata = createPageMetadata({
@@ -45,6 +47,15 @@ export default function PilotToProdPage() {
+
+
+
+
{/* Discover */}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ );
+}
diff --git a/apps/website/src/components/docs/diagrams/DiagramFrame.tsx b/apps/website/src/components/docs/diagrams/DiagramFrame.tsx
index 72d42bb7b..2c03045f5 100644
--- a/apps/website/src/components/docs/diagrams/DiagramFrame.tsx
+++ b/apps/website/src/components/docs/diagrams/DiagramFrame.tsx
@@ -13,8 +13,18 @@ interface DiagramFrameProps {
/** Accessible one-sentence description of what the diagram shows. */
label: string;
caption?: string;
- /** Marketing pages render the same SVG larger. */
- scale?: 'docs' | 'marketing';
+ /**
+ * Marketing pages render the same SVG larger; compact cards render it small with a bigger type
+ * ramp. Compact compositions author at a ~320 viewBox with the compact type ramp (eyebrow 10 /
+ * mono title 13.5 / sans title 12 / meta 11 / pill 10.5, viewBox units) — see DiagramNode's note
+ * for the shared baseline offsets; rendered width is capped at 420px regardless of card size.
+ * Compact rhythm: 18px inter-node gaps, edges stop 4px short of the node they arrive at
+ * (arrowhead fills the rest), 16px outer margins, and a uniform viewHeight of 240 across the
+ * homepage concept-card set — so every card in that set is the same height regardless of
+ * viewWidth or node count. Second rhythm, for node↔pill gaps: 12px (segment lengths flex to
+ * fit; pills abut their segments).
+ */
+ scale?: 'docs' | 'marketing' | 'compact';
children: ReactNode;
}
diff --git a/apps/website/src/components/docs/diagrams/DiagramNode.tsx b/apps/website/src/components/docs/diagrams/DiagramNode.tsx
index da0a66d6c..a6f81404a 100644
--- a/apps/website/src/components/docs/diagrams/DiagramNode.tsx
+++ b/apps/website/src/components/docs/diagrams/DiagramNode.tsx
@@ -7,11 +7,16 @@ interface DiagramNodeProps {
title: string;
eyebrow?: string;
meta?: string;
+ /** 'accent' has two conventions: concept/marketing compositions accent the single
+ * PAYOFF node (what the customer gets); StackDiagram uses it as a subject
+ * highlight ("the node this page is about"). 'dim' marks inputs/externals. */
tone?: 'neutral' | 'accent' | 'dim';
/** 'middle' centers text horizontally (title-only summary nodes). */
align?: 'start' | 'middle';
/** 'sans' for prose-y titles (backend lists); default mono for package names. */
titleStyle?: 'mono' | 'sans';
+ /** 'mono' for code-shaped meta lines (JSON fragments, API names); default Inter. */
+ metaStyle?: 'sans' | 'mono';
}
const PAD = 16;
@@ -21,6 +26,10 @@ const PAD = 16;
* Minimum heights: `h >= 64` with eyebrow+meta, `h >= 52` with meta only,
* `h >= 52` with eyebrow and no meta (eyebrow at y+20, title at y+38),
* any `h` for title-only (vertically centered).
+ *
+ * Compact-scale compositions author at a ~320 viewBox with the compact type
+ * ramp (eyebrow 10 / mono title 13.5 / sans title 12 / meta 11 / pill 10.5,
+ * viewBox units); the same baseline offsets above apply unchanged.
*/
export function DiagramNode({
x,
@@ -33,6 +42,7 @@ export function DiagramNode({
tone = 'neutral',
align = 'start',
titleStyle = 'mono',
+ metaStyle = 'sans',
}: DiagramNodeProps) {
const tx = align === 'middle' ? x + w / 2 : x + PAD;
const anchor = align === 'middle' ? 'middle' : undefined;
@@ -41,7 +51,7 @@ export function DiagramNode({
const titleY = eyebrow ? y + 38 : meta ? y + 26 : y + h / 2 + 4;
const metaY = eyebrow ? y + 54 : y + 42;
return (
-
+
{eyebrow ? (
diff --git a/apps/website/src/components/docs/diagrams/DiagramPill.tsx b/apps/website/src/components/docs/diagrams/DiagramPill.tsx
index 2e9b7ae55..2eef73eb5 100644
--- a/apps/website/src/components/docs/diagrams/DiagramPill.tsx
+++ b/apps/website/src/components/docs/diagrams/DiagramPill.tsx
@@ -5,13 +5,19 @@ interface DiagramPillProps {
cy: number;
w: number;
label: string;
+ /** 'accent' (default) for a payoff-adjacent pill; 'neutral' for an event
+ * label that should not compete with the card's one accented node. The
+ * default preserves the older docs diagrams, but NEW compositions should
+ * pass 'neutral' — pills label events/gates, and an accent pill competes
+ * with the payoff node. */
+ tone?: 'accent' | 'neutral';
}
const PILL_H = 24;
-export function DiagramPill({ cx, cy, w, label }: DiagramPillProps) {
+export function DiagramPill({ cx, cy, w, label, tone = 'accent' }: DiagramPillProps) {
return (
-
+
{label}
diff --git a/apps/website/src/components/docs/diagrams/PilotJourney.tsx b/apps/website/src/components/docs/diagrams/PilotJourney.tsx
new file mode 100644
index 000000000..3c78119a7
--- /dev/null
+++ b/apps/website/src/components/docs/diagrams/PilotJourney.tsx
@@ -0,0 +1,106 @@
+// SPDX-License-Identifier: MIT
+import { DiagramFrame } from './DiagramFrame';
+import { DiagramNode } from './DiagramNode';
+import { DiagramEdge } from './DiagramEdge';
+import { DiagramPill } from './DiagramPill';
+
+const SLUG = 'pilot-journey';
+
+/**
+ * /pilot-to-prod marketing graphic: the three FeatureBlock phases of the
+ * engagement (`id="discover"`, `id="build"`, `id="harden"` on the page) laid
+ * out as a horizontal journey. Eyebrows and titles are lifted verbatim from
+ * the page's own phase framing ("Week 1–2 · Discover", "Week 3–5 · Build",
+ * "Week 6–7 · Harden"); each node's meta abbreviates that phase's actual
+ * `rows` claims (stack audit + roadmap; real data + weekly demos) or its
+ * own body copy (Harden's is literally "on-call runbook") rather than
+ * inventing new copy.
+ *
+ * Gate pills mark every transition the page's copy actually names an
+ * artifact for: Discover's own row api is literally "roadmap" (carried into
+ * Build), Build's headline is literally "Ship a working agent on your real
+ * data" (the thing Harden then hardens), and the "Roadmap draft" panel in
+ * the Discover block ends on a row that is literally "W8 · Train your team ·
+ * handoff" (the page's own week-8 close, not a fourth phase — Harden is
+ * still the last FeatureBlock). All three pills
+ * stay neutral so they read as hand-offs, not payoffs; the terminal one
+ * after Harden has no outbound arrow since there is no fourth node. The
+ * Harden node carries the sole accent: it is what the customer is left
+ * holding at the end of the engagement (a working agent, a trained team, an
+ * on-call runbook), matching the outcomes section below it on the page ("A
+ * working agent. A trained team. A runbook.").
+ *
+ * Tiled at the kit-standard 640 viewWidth (16px outer margins) against
+ * measured glyph widths (JetBrains Mono / Inter, actual `getBBox()` reads via
+ * a live render of this exact composition — not a character-count estimate):
+ * node "Discover" meta "stack audit · roadmap" text 107.1 → w=132 (slack 8.9)
+ * node "Build" meta "real data · weekly demos" text 123.4 → w=146 (slack 6.6)
+ * node "Harden" meta "on-call runbook" text 78.5 → w=101 (slack 6.5)
+ * pill "roadmap" text 42.0 → w=52 (pad 5.0/side)
+ * pill "working agent" text 78.0 → w=94 (pad 8.0/side)
+ * pill "handoff" text 42.0 → w=52 (pad 5.0/side)
+ * (meta is the widest line in every node; eyebrow and title both measure
+ * narrower at 132/146/101.)
+ * Inter-node rhythm mirrors RenderTransform: a 5px segment into each pill, a
+ * 7px arrow-bearing segment out, a 2px arrowhead stop-short. The Build→Harden
+ * gap (a real named artifact, "working agent") gets the same pill treatment
+ * rather than a bare arrow, since the page names it too. The terminal
+ * Harden→handoff gap is a bare 5px segment into the pill with no exit
+ * segment or arrowhead — it is the end of the line, not a hand-off to a
+ * fourth node. Margins land at 16px left / 14px right (was 16/36 — the
+ * 20px right-margin surplus, plus shortening Harden's meta from
+ * "observability · runbook" to the page's own "on-call runbook", made room
+ * for the terminal pill without touching the earlier two nodes/pills).
+ */
+export function PilotJourney() {
+ return (
+
+
+
+
+
+
+
+
+
+
+
+
+
+ );
+}
diff --git a/apps/website/src/components/docs/diagrams/RenderConcept.tsx b/apps/website/src/components/docs/diagrams/RenderConcept.tsx
new file mode 100644
index 000000000..810393155
--- /dev/null
+++ b/apps/website/src/components/docs/diagrams/RenderConcept.tsx
@@ -0,0 +1,56 @@
+// SPDX-License-Identifier: MIT
+import { DiagramFrame } from './DiagramFrame';
+import { DiagramNode } from './DiagramNode';
+import { DiagramEdge } from './DiagramEdge';
+
+const SLUG = 'concept-render';
+
+/**
+ * Homepage concept card: a UI spec — abbreviated from the `@threadplane/render`
+ * intro's own example (`type: 'Text'`, `props: { … }`) — resolves through
+ * `defineAngularRegistry()` into your own Angular components.
+ */
+export function RenderConcept() {
+ return (
+
+
+
+
+
+
+
+ );
+}
diff --git a/apps/website/src/components/docs/diagrams/RenderTransform.tsx b/apps/website/src/components/docs/diagrams/RenderTransform.tsx
new file mode 100644
index 000000000..574f5ddb5
--- /dev/null
+++ b/apps/website/src/components/docs/diagrams/RenderTransform.tsx
@@ -0,0 +1,80 @@
+// SPDX-License-Identifier: MIT
+import { DiagramFrame } from './DiagramFrame';
+import { DiagramNode } from './DiagramNode';
+import { DiagramEdge } from './DiagramEdge';
+import { DiagramPill } from './DiagramPill';
+
+const SLUG = 'render-transform';
+
+/**
+ * /render marketing graphic: a horizontal transform story. A UI spec —
+ * abbreviated from the `@threadplane/render` intro's own example
+ * (`type: 'Text'`, `props: { label: { $state: '/message' } }`) — resolves
+ * through `@threadplane/render`'s registry/state/handlers into components
+ * you already own. The right-hand node carries the payoff accent; the
+ * renderer and both pills stay neutral so the accent reads as a single beat.
+ *
+ * Tiled at the kit-standard 640 viewWidth (16px outer margins) against
+ * measured glyph widths (JetBrains Mono / Inter, actual `getBBox()` reads —
+ * not a character-count estimate) so every node keeps real slack between its
+ * widest line and its right edge at this width, not just at the larger
+ * marketing scale that stretches the SVG in CSS:
+ * node "type: 'Text'" text 90.0 → w=113 (slack 7.0)
+ * node "@threadplane/render" text 142.5 → w=166 (slack 7.5)
+ * node "your components" /
+ * "your styles · your rules" text 112.8 → w=136 (slack 7.2)
+ * pill "UI spec" text 42.0 → w=52 (pad 5.0/side)
+ * pill "bindings + events" text 102.0 → w=112 (pad 5.0/side)
+ * Inter-node gaps are a plain 5px segment, the pill, a 7px arrow-bearing
+ * segment, and a 2px arrowhead stop-short — tight but exact, verified
+ * against the live render rather than assumed.
+ */
+export function RenderTransform() {
+ return (
+
+
+
+
+
+
+
+
+
+
+
+ );
+}
diff --git a/apps/website/src/components/docs/diagrams/ShipConcept.tsx b/apps/website/src/components/docs/diagrams/ShipConcept.tsx
new file mode 100644
index 000000000..943564f2a
--- /dev/null
+++ b/apps/website/src/components/docs/diagrams/ShipConcept.tsx
@@ -0,0 +1,49 @@
+// SPDX-License-Identifier: MIT
+import { DiagramFrame } from './DiagramFrame';
+import { DiagramNode } from './DiagramNode';
+import { DiagramEdge } from './DiagramEdge';
+import { DiagramPill } from './DiagramPill';
+
+const SLUG = 'concept-ship';
+
+/**
+ * Homepage concept card: the thread survives everything between question and
+ * answer — phrased against the `@threadplane/langgraph` persistence guide
+ * ("keeps conversations alive across page refreshes, browser restarts, and
+ * server deployments" / "users resume exactly where they left off"), not a
+ * runtime-neutral claim (the design spec's CLAIMS table flags AG-UI history
+ * as out of scope, so this card stays scoped to the LangGraph contract via
+ * the reload/deploy pills rather than promising the same for every adapter).
+ *
+ * Geometry (viewHeight 240, centered): a single 52px-tall row — Thread
+ * "Starts" (dim) breaks around the `reload` and `deploy` pills (12px
+ * segments, 48px pills) into Thread "Resumes" (accent, the payoff). Content
+ * height is just the 52px row; the remaining 188 splits into 94px top/bottom
+ * margins. That is a wide empty band above and below a single row, but it is
+ * accepted for grid uniformity with the other three cards (the dot grid
+ * fills it) — a second row was considered (e.g. a "history intact" caption)
+ * but dropped: the persistence guide's own "Adapter-defined behavior" callout
+ * says thread-history restore is LangGraph-specific, and this card already
+ * carries a LangGraph-scoped claim via reload/deploy, so adding another
+ * unverifiable-across-runtimes line risked stacking the same overclaim twice
+ * rather than earning its space.
+ */
+export function ShipConcept() {
+ return (
+
+
+
+
+
+
+
+
+
+ );
+}
diff --git a/apps/website/src/components/docs/diagrams/StreamConcept.tsx b/apps/website/src/components/docs/diagrams/StreamConcept.tsx
new file mode 100644
index 000000000..4e6aee304
--- /dev/null
+++ b/apps/website/src/components/docs/diagrams/StreamConcept.tsx
@@ -0,0 +1,25 @@
+// SPDX-License-Identifier: MIT
+import { DiagramFrame } from './DiagramFrame';
+import { DiagramNode } from './DiagramNode';
+import { DiagramEdge } from './DiagramEdge';
+
+const SLUG = 'concept-stream';
+
+/** Homepage concept card: tokens arrive as signals; the UI updates itself. */
+export function StreamConcept() {
+ return (
+
+
+
+
+
+
+
+ );
+}
diff --git a/apps/website/src/components/docs/diagrams/compositions.spec.tsx b/apps/website/src/components/docs/diagrams/compositions.spec.tsx
index 259a37dff..09ea14845 100644
--- a/apps/website/src/components/docs/diagrams/compositions.spec.tsx
+++ b/apps/website/src/components/docs/diagrams/compositions.spec.tsx
@@ -7,8 +7,10 @@ import { AgUiArchitecturePipeline } from './AgUiArchitecturePipeline';
import { A2uiMessageFlow } from './A2uiMessageFlow';
import { RenderHowItFits } from './RenderHowItFits';
import { RenderVsA2ui } from './RenderVsA2ui';
+import { RenderTransform } from './RenderTransform';
import { MiddlewareHowItFits } from './MiddlewareHowItFits';
import { TelemetryHowItFits } from './TelemetryHowItFits';
+import { PilotJourney } from './PilotJourney';
/**
* Compositions are hand-placed layouts; the spec guards that each mounts,
@@ -108,3 +110,53 @@ describe('TelemetryHowItFits', () => {
expect(container.querySelectorAll('path.tp-diagram-edge')).toHaveLength(5);
});
});
+
+describe('RenderTransform', () => {
+ it('mounts at standard scale with spec, render, and result stages', () => {
+ const { container } = render();
+ const titles = Array.from(container.querySelectorAll('.tp-diagram-title')).map((t) => t.textContent);
+ expect(titles).toContain('@threadplane/render');
+ expect(container.querySelectorAll('.tp-diagram-pill')).toHaveLength(2);
+ });
+
+ it('names the spec fragment, the transport pills, and the payoff node', () => {
+ const { container } = render();
+ const titles = Array.from(container.querySelectorAll('.tp-diagram-title')).map((t) => t.textContent);
+ expect(titles).toContain("type: 'Text'");
+ expect(titles).toContain('your components');
+ const pills = Array.from(container.querySelectorAll('.tp-diagram-pill text')).map((t) => t.textContent);
+ expect(pills).toEqual(['UI spec', 'bindings + events']);
+ });
+
+ it('accents only the payoff node', () => {
+ const { container } = render();
+ const accented = container.querySelectorAll('g.tp-diagram-node[data-tone="accent"]');
+ expect(accented).toHaveLength(1);
+ expect(accented[0]?.querySelector('.tp-diagram-title')?.textContent).toBe('your components');
+ });
+});
+
+describe('PilotJourney', () => {
+ it('mounts with three phase nodes on the journey line', () => {
+ const { container } = render();
+ expect(container.querySelectorAll('g.tp-diagram-node')).toHaveLength(3);
+ expect(container.querySelector('svg[role="img"]')?.getAttribute('aria-label')).toBeTruthy();
+ });
+
+ it('names the three phases and their gate pills', () => {
+ const { container } = render();
+ const titles = Array.from(container.querySelectorAll('.tp-diagram-title')).map((t) => t.textContent);
+ expect(titles).toEqual(['Discover', 'Build', 'Harden']);
+ const pills = Array.from(container.querySelectorAll('.tp-diagram-pill text')).map((t) => t.textContent);
+ expect(pills).toEqual(['roadmap', 'working agent', 'handoff']);
+ const neutralPills = container.querySelectorAll('.tp-diagram-pill[data-tone="neutral"]');
+ expect(neutralPills).toHaveLength(3);
+ });
+
+ it('accents only the Harden node — the production-ready system the customer keeps', () => {
+ const { container } = render();
+ const accented = container.querySelectorAll('g.tp-diagram-node[data-tone="accent"]');
+ expect(accented).toHaveLength(1);
+ expect(accented[0]?.querySelector('.tp-diagram-title')?.textContent).toBe('Harden');
+ });
+});
diff --git a/apps/website/src/components/docs/diagrams/concepts.spec.tsx b/apps/website/src/components/docs/diagrams/concepts.spec.tsx
new file mode 100644
index 000000000..76ba7966e
--- /dev/null
+++ b/apps/website/src/components/docs/diagrams/concepts.spec.tsx
@@ -0,0 +1,89 @@
+// SPDX-License-Identifier: MIT
+// @vitest-environment jsdom
+import React from 'react';
+import { describe, expect, it } from 'vitest';
+import { render } from '@testing-library/react';
+import { StreamConcept } from './StreamConcept';
+import { RenderConcept } from './RenderConcept';
+import { ApproveConcept } from './ApproveConcept';
+import { ShipConcept } from './ShipConcept';
+
+/** Compact homepage concept cards: each must mount labeled, at compact scale,
+ * and carry its load-bearing API/package names. */
+describe('StreamConcept', () => {
+ it('mounts compact with the signals claim', () => {
+ const { container } = render();
+ const svg = container.querySelector('svg');
+ expect(container.querySelector('figure')?.getAttribute('data-scale')).toBe('compact');
+ expect(svg?.getAttribute('aria-label')).toBeTruthy();
+ const titles = Array.from(container.querySelectorAll('.tp-diagram-title')).map((t) => t.textContent);
+ expect(titles).toContain('injectAgent()');
+ });
+
+ it('accents the UI-updates-itself payoff node, not the signals source', () => {
+ const { container } = render();
+ const accentNodes = container.querySelectorAll('g.tp-diagram-node[data-tone="accent"]');
+ expect(accentNodes.length).toBe(1);
+ expect(accentNodes[0].querySelector('.tp-diagram-title')?.textContent).toBe('UI updates itself');
+ });
+});
+
+describe('RenderConcept', () => {
+ it('accents the your-components payoff node', () => {
+ const { container } = render();
+ expect(container.querySelector('figure')?.getAttribute('data-scale')).toBe('compact');
+ const titles = Array.from(container.querySelectorAll('.tp-diagram-title')).map((t) => t.textContent);
+ expect(titles).toContain("type: 'Text'");
+ expect(titles).toContain('defineAngularRegistry()');
+ const accentNodes = container.querySelectorAll('g.tp-diagram-node[data-tone="accent"]');
+ expect(accentNodes.length).toBe(1);
+ expect(accentNodes[0].querySelector('.tp-diagram-title')?.textContent).toBe('Your own Angular components');
+ });
+});
+
+describe('ApproveConcept', () => {
+ it('mounts compact with the interrupt/resume loop', () => {
+ const { container } = render();
+ const svg = container.querySelector('svg');
+ expect(container.querySelector('figure')?.getAttribute('data-scale')).toBe('compact');
+ expect(svg?.getAttribute('aria-label')).toBeTruthy();
+ const titles = Array.from(container.querySelectorAll('.tp-diagram-title')).map((t) => t.textContent);
+ expect(titles).toContain('Agent');
+ expect(titles).toContain('Human');
+ expect(titles).toContain('Resumes with the decision');
+ const pills = Array.from(container.querySelectorAll('.tp-diagram-pill text')).map((t) => t.textContent);
+ expect(pills).toContain('interrupt');
+ expect(pills).toContain('resume');
+ const accentNodes = container.querySelectorAll('g.tp-diagram-node[data-tone="accent"]');
+ expect(accentNodes.length).toBe(1);
+ expect(accentNodes[0].querySelector('.tp-diagram-title')?.textContent).toBe('Human');
+ const pillGroups = container.querySelectorAll('.tp-diagram-pill');
+ expect(pillGroups.length).toBeGreaterThan(0);
+ pillGroups.forEach((pill) => {
+ expect(pill.getAttribute('data-tone')).toBe('neutral');
+ });
+ });
+});
+
+describe('ShipConcept', () => {
+ it('mounts compact with the thread crossing reload and deploy', () => {
+ const { container } = render();
+ const svg = container.querySelector('svg');
+ expect(container.querySelector('figure')?.getAttribute('data-scale')).toBe('compact');
+ expect(svg?.getAttribute('aria-label')).toBeTruthy();
+ const titles = Array.from(container.querySelectorAll('.tp-diagram-title')).map((t) => t.textContent);
+ expect(titles).toContain('Starts');
+ expect(titles).toContain('Resumes');
+ const pills = Array.from(container.querySelectorAll('.tp-diagram-pill text')).map((t) => t.textContent);
+ expect(pills).toContain('reload');
+ expect(pills).toContain('deploy');
+ const accentNodes = container.querySelectorAll('g.tp-diagram-node[data-tone="accent"]');
+ expect(accentNodes.length).toBe(1);
+ expect(accentNodes[0].querySelector('.tp-diagram-title')?.textContent).toBe('Resumes');
+ const pillGroups = container.querySelectorAll('.tp-diagram-pill');
+ expect(pillGroups.length).toBeGreaterThan(0);
+ pillGroups.forEach((pill) => {
+ expect(pill.getAttribute('data-tone')).toBe('neutral');
+ });
+ });
+});
diff --git a/apps/website/src/components/docs/diagrams/index.ts b/apps/website/src/components/docs/diagrams/index.ts
index a17101f51..6a8888303 100644
--- a/apps/website/src/components/docs/diagrams/index.ts
+++ b/apps/website/src/components/docs/diagrams/index.ts
@@ -8,5 +8,11 @@ export { AgUiArchitecturePipeline } from './AgUiArchitecturePipeline';
export { A2uiMessageFlow } from './A2uiMessageFlow';
export { RenderHowItFits } from './RenderHowItFits';
export { RenderVsA2ui } from './RenderVsA2ui';
+export { RenderTransform } from './RenderTransform';
export { MiddlewareHowItFits } from './MiddlewareHowItFits';
export { TelemetryHowItFits } from './TelemetryHowItFits';
+export { StreamConcept } from './StreamConcept';
+export { RenderConcept } from './RenderConcept';
+export { ApproveConcept } from './ApproveConcept';
+export { ShipConcept } from './ShipConcept';
+export { PilotJourney } from './PilotJourney';
diff --git a/apps/website/src/components/docs/diagrams/primitives.spec.tsx b/apps/website/src/components/docs/diagrams/primitives.spec.tsx
index 64ec1e7b7..e2a097ce4 100644
--- a/apps/website/src/components/docs/diagrams/primitives.spec.tsx
+++ b/apps/website/src/components/docs/diagrams/primitives.spec.tsx
@@ -76,6 +76,29 @@ describe('diagram kit primitives', () => {
expect(container.querySelector('g.tp-diagram-node')?.getAttribute('data-title')).toBe('sans');
});
+ // This runtime assertion is trivially green — 'compact' is a literal in the
+ // scale union, so any string reaching here has already type-checked. The
+ // real guard is tsc during `nx build` (spec .tsx files are type-checked
+ // because tsconfig excludes only `*.spec.ts`, not `*.spec.tsx`) — don't
+ // "fix" that tsconfig exclude without replacing this guard.
+ it('DiagramFrame accepts the compact scale', () => {
+ const { container } = render(
+
+
+
+ );
+ expect(container.querySelector('figure')?.getAttribute('data-scale')).toBe('compact');
+ });
+
+ it('DiagramNode renders a mono meta when metaStyle is mono', () => {
+ const { container } = render(
+
+ );
+ expect(container.querySelector('g.tp-diagram-node')?.getAttribute('data-meta')).toBe('mono');
+ });
+
it('DiagramPill renders a centered label', () => {
const { container } = render(