From 91488eeba73bb126700a4172f040895623933362 Mon Sep 17 00:00:00 2001 From: Brian Love Date: Tue, 1 Sep 2026 15:19:53 -0700 Subject: [PATCH] feat(website): wire the docs -> cockpit handoff for deep-agents and runtimes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The cockpit -> docs direction shipped with both libraries (COCKPIT_DOCS_LINKS covers all six deep-agents entries and all four runtimes entries), but the reverse table only knew the five first-iteration pages. Every deep-agents page (#937) and every runtimes page (#913) sent its Run/Code/API buttons to Cockpit home instead of the demo the page describes. Add the 16 missing entries: - deep-agents: the five capability pages map to their core-capabilities topic; the introduction maps to deep-agents/getting-started/overview. - runtimes: all three pages of each runtime section (overview, quickstart, how-it-connects) map to that runtime's one demo, since a single demo serves them equally well; the introduction maps to runtimes/getting-started/overview. Also add deep-agents and runtimes to AnalyticsLibrary. Without it every handoff from these two libraries would have been attributed to 'unknown', so the new links would be invisible in the funnel they exist to feed. Each of the ten destination URLs was checked against production and returns a hard 200. The check discriminates: an unknown topic 307s to the fallback and an unknown product 500s, so the 200s are real pages rather than SPA soft-200s. Tests: the exact pin is updated, plus four guards — every key names a page that exists in docs-config (a typo'd key is otherwise invisible: the page just keeps the Cockpit-home fallback), the deep-agents/runtimes links round-trip against COCKPIT_DOCS_LINKS, the analytics library is no longer 'unknown', and each runtime section's three pages resolve to the same demo. All four were mutation-tested to confirm they fail when the mapping is wrong. Co-Authored-By: Claude Opus 5 --- apps/website/src/lib/analytics/events.ts | 9 +- apps/website/src/lib/cockpit-links.spec.ts | 132 ++++++++++++++++++- apps/website/src/lib/cockpit-links.ts | 144 +++++++++++++++++++++ 3 files changed, 283 insertions(+), 2 deletions(-) diff --git a/apps/website/src/lib/analytics/events.ts b/apps/website/src/lib/analytics/events.ts index 7fbbfd367..662898ed6 100644 --- a/apps/website/src/lib/analytics/events.ts +++ b/apps/website/src/lib/analytics/events.ts @@ -90,7 +90,14 @@ export type CtaId = // MediumSwitcher derives ids from section id + medium key at runtime | `medium_${string}`; -export type AnalyticsLibrary = 'langgraph' | 'render' | 'chat' | 'ag-ui' | 'unknown'; +export type AnalyticsLibrary = + | 'langgraph' + | 'render' + | 'chat' + | 'ag-ui' + | 'deep-agents' + | 'runtimes' + | 'unknown'; export type WhitepaperId = 'overview' | 'angular' | 'render' | 'chat'; diff --git a/apps/website/src/lib/cockpit-links.spec.ts b/apps/website/src/lib/cockpit-links.spec.ts index 70688ff1e..7b1ec599f 100644 --- a/apps/website/src/lib/cockpit-links.spec.ts +++ b/apps/website/src/lib/cockpit-links.spec.ts @@ -1,5 +1,6 @@ -import { cockpitManifest } from '@threadplane/cockpit-registry'; +import { COCKPIT_DOCS_LINKS, cockpitManifest } from '@threadplane/cockpit-registry'; import { describe, expect, it } from 'vitest'; +import { findDocsPage } from './docs-config'; import { buildCockpitHandoffProperties, buildCockpitModeHref, @@ -10,6 +11,54 @@ import { describe('Docs-to-Cockpit links', () => { it('contains the exhaustive first-iteration mapping set', () => { expect(docsCockpitMappings).toEqual({ + 'deep-agents/getting-started/introduction': { + product: 'deep-agents', section: 'getting-started', topic: 'overview', page: 'overview', language: 'python', + }, + 'deep-agents/capabilities/planning': { + product: 'deep-agents', section: 'core-capabilities', topic: 'planning', page: 'overview', language: 'python', + }, + 'deep-agents/capabilities/filesystem': { + product: 'deep-agents', section: 'core-capabilities', topic: 'filesystem', page: 'overview', language: 'python', + }, + 'deep-agents/capabilities/subagents': { + product: 'deep-agents', section: 'core-capabilities', topic: 'subagents', page: 'overview', language: 'python', + }, + 'deep-agents/capabilities/memory': { + product: 'deep-agents', section: 'core-capabilities', topic: 'memory', page: 'overview', language: 'python', + }, + 'deep-agents/capabilities/skills': { + product: 'deep-agents', section: 'core-capabilities', topic: 'skills', page: 'overview', language: 'python', + }, + 'runtimes/getting-started/introduction': { + product: 'runtimes', section: 'getting-started', topic: 'overview', page: 'overview', language: 'python', + }, + 'runtimes/aws-strands/overview': { + product: 'runtimes', section: 'core-capabilities', topic: 'aws-strands', page: 'overview', language: 'python', + }, + 'runtimes/aws-strands/quickstart': { + product: 'runtimes', section: 'core-capabilities', topic: 'aws-strands', page: 'overview', language: 'python', + }, + 'runtimes/aws-strands/how-it-connects': { + product: 'runtimes', section: 'core-capabilities', topic: 'aws-strands', page: 'overview', language: 'python', + }, + 'runtimes/microsoft-agent-framework/overview': { + product: 'runtimes', section: 'core-capabilities', topic: 'microsoft-agent-framework', page: 'overview', language: 'python', + }, + 'runtimes/microsoft-agent-framework/quickstart': { + product: 'runtimes', section: 'core-capabilities', topic: 'microsoft-agent-framework', page: 'overview', language: 'python', + }, + 'runtimes/microsoft-agent-framework/how-it-connects': { + product: 'runtimes', section: 'core-capabilities', topic: 'microsoft-agent-framework', page: 'overview', language: 'python', + }, + 'runtimes/mastra/overview': { + product: 'runtimes', section: 'core-capabilities', topic: 'mastra', page: 'overview', language: 'python', + }, + 'runtimes/mastra/quickstart': { + product: 'runtimes', section: 'core-capabilities', topic: 'mastra', page: 'overview', language: 'python', + }, + 'runtimes/mastra/how-it-connects': { + product: 'runtimes', section: 'core-capabilities', topic: 'mastra', page: 'overview', language: 'python', + }, 'langgraph/guides/streaming': { product: 'langgraph', section: 'core-capabilities', topic: 'streaming', page: 'overview', language: 'python', }, @@ -124,4 +173,85 @@ describe('Docs-to-Cockpit links', () => { )).toBe(true); } }); + + // A typo'd key is invisible at runtime: the page just never resolves and the + // reader silently keeps the Cockpit-home fallback. Pin the keys to the real + // content tree so a typo (or a docs rename) fails here instead. + it('keys every mapping to a docs page that exists', () => { + for (const key of Object.keys(docsCockpitMappings)) { + const [library, section, slug] = key.split('/'); + expect(findDocsPage(library, section, slug), key).toBeDefined(); + } + }); + + // The two directions are separate tables in separate packages. Where the + // Cockpit side already points at a docs page, that page must point back at + // the same capability, or the round trip lands somewhere else. + it('round-trips the deep-agents and runtimes links back to their capability', () => { + const forward = Object.entries(COCKPIT_DOCS_LINKS).filter(([key]) => + key.startsWith('deep-agents/') || key.startsWith('runtimes/'), + ); + expect(forward).toHaveLength(10); + + for (const [key, docsPath] of forward) { + const [product, section, topic] = key.split('/'); + const [library, docsSection, slug] = docsPath.replace('/docs/', '').split('/'); + + expect(resolveCockpitIdentity(library, docsSection, slug), key).toEqual( + expect.objectContaining({ product, section, topic }), + ); + } + }); + + it('reports deep-agents and runtimes as themselves, not unknown', () => { + expect( + buildCockpitHandoffProperties( + { library: 'deep-agents', section: 'capabilities', slug: 'planning' }, + 'Run', + ), + ).toEqual({ + library: 'deep-agents', + source_section: 'capabilities', + source_slug: 'planning', + destination_product: 'deep-agents', + destination_capability: 'planning', + requested_mode: 'run', + mapped: true, + }); + + expect( + buildCockpitHandoffProperties( + { library: 'runtimes', section: 'mastra', slug: 'quickstart' }, + 'Code', + ).library, + ).toBe('runtimes'); + }); + + // Every page of a runtime's docs section shares that runtime's one demo, so + // a reader on the quickstart gets the same handoff as one on the overview. + it('sends every page of a runtime section to that runtime demo', () => { + for (const slug of ['overview', 'quickstart', 'how-it-connects']) { + expect( + buildCockpitModeHref( + { library: 'runtimes', section: 'aws-strands', slug }, + 'Run', + 'https://cockpit.example', + ), + ).toBe( + 'https://cockpit.example/runtimes/core-capabilities/aws-strands/overview/python?mode=run', + ); + } + }); + + it('sends a deep-agents capability page to its capability demo', () => { + expect( + buildCockpitModeHref( + { library: 'deep-agents', section: 'capabilities', slug: 'skills' }, + 'Code', + 'https://cockpit.example', + ), + ).toBe( + 'https://cockpit.example/deep-agents/core-capabilities/skills/overview/python?mode=code', + ); + }); }); diff --git a/apps/website/src/lib/cockpit-links.ts b/apps/website/src/lib/cockpit-links.ts index 13d81d9cf..566e25b1b 100644 --- a/apps/website/src/lib/cockpit-links.ts +++ b/apps/website/src/lib/cockpit-links.ts @@ -11,7 +11,149 @@ export interface DocsIdentity { export const COCKPIT_ENVIRONMENT_LABEL = process.env.NEXT_PUBLIC_COCKPIT_ENVIRONMENT_LABEL ?? 'Shared development'; +/** + * Documentation page -> Cockpit capability (the reverse of + * `COCKPIT_DOCS_LINKS` in `@threadplane/cockpit-registry`). + * + * Keyed by `${library}/${section}/${slug}` — the three segments of the docs + * route, `/docs//
/`. Values are a full Cockpit + * identity, because the Cockpit route carries five segments; the extra two are + * fixed (`page: 'overview'`, `language: 'python'`) for every manifest entry + * today, so they are written out rather than derived. + * + * Rules for this table: + * + * - An unmapped page is not an error. `resolveCockpitIdentity` returns null and + * the control plane sends the reader to Cockpit home instead of guessing a + * URL — the five-segment formula 404s for pages that have no demo. + * - Several docs pages may share one Cockpit target. The runtimes library + * documents each runtime across three pages (overview, quickstart, + * how-it-connects) that one demo serves equally well. + * - An `introduction` maps to its product's `getting-started/overview` entry. + * Those entries are `entryKind: 'docs-only'` in the manifest, so Run mode + * lands on the product's Cockpit overview rather than a live demo — still a + * better destination than Cockpit home, and it keeps the Capability row + * truthful. + * - The spec pins this object exactly and cross-checks every target against + * `cockpitManifest`, so a Cockpit topic rename breaks the test rather than + * the link. + */ export const docsCockpitMappings = { + 'deep-agents/getting-started/introduction': { + product: 'deep-agents', + section: 'getting-started', + topic: 'overview', + page: 'overview', + language: 'python', + }, + 'deep-agents/capabilities/planning': { + product: 'deep-agents', + section: 'core-capabilities', + topic: 'planning', + page: 'overview', + language: 'python', + }, + 'deep-agents/capabilities/filesystem': { + product: 'deep-agents', + section: 'core-capabilities', + topic: 'filesystem', + page: 'overview', + language: 'python', + }, + 'deep-agents/capabilities/subagents': { + product: 'deep-agents', + section: 'core-capabilities', + topic: 'subagents', + page: 'overview', + language: 'python', + }, + 'deep-agents/capabilities/memory': { + product: 'deep-agents', + section: 'core-capabilities', + topic: 'memory', + page: 'overview', + language: 'python', + }, + 'deep-agents/capabilities/skills': { + product: 'deep-agents', + section: 'core-capabilities', + topic: 'skills', + page: 'overview', + language: 'python', + }, + 'runtimes/getting-started/introduction': { + product: 'runtimes', + section: 'getting-started', + topic: 'overview', + page: 'overview', + language: 'python', + }, + 'runtimes/aws-strands/overview': { + product: 'runtimes', + section: 'core-capabilities', + topic: 'aws-strands', + page: 'overview', + language: 'python', + }, + 'runtimes/aws-strands/quickstart': { + product: 'runtimes', + section: 'core-capabilities', + topic: 'aws-strands', + page: 'overview', + language: 'python', + }, + 'runtimes/aws-strands/how-it-connects': { + product: 'runtimes', + section: 'core-capabilities', + topic: 'aws-strands', + page: 'overview', + language: 'python', + }, + 'runtimes/microsoft-agent-framework/overview': { + product: 'runtimes', + section: 'core-capabilities', + topic: 'microsoft-agent-framework', + page: 'overview', + language: 'python', + }, + 'runtimes/microsoft-agent-framework/quickstart': { + product: 'runtimes', + section: 'core-capabilities', + topic: 'microsoft-agent-framework', + page: 'overview', + language: 'python', + }, + 'runtimes/microsoft-agent-framework/how-it-connects': { + product: 'runtimes', + section: 'core-capabilities', + topic: 'microsoft-agent-framework', + page: 'overview', + language: 'python', + }, + // The Mastra demo's backend is a Node AG-UI service, but the Cockpit route + // is `python` like every other manifest entry — the language segment names + // the Cockpit lane, not the runtime. + 'runtimes/mastra/overview': { + product: 'runtimes', + section: 'core-capabilities', + topic: 'mastra', + page: 'overview', + language: 'python', + }, + 'runtimes/mastra/quickstart': { + product: 'runtimes', + section: 'core-capabilities', + topic: 'mastra', + page: 'overview', + language: 'python', + }, + 'runtimes/mastra/how-it-connects': { + product: 'runtimes', + section: 'core-capabilities', + topic: 'mastra', + page: 'overview', + language: 'python', + }, 'langgraph/guides/streaming': { product: 'langgraph', section: 'core-capabilities', @@ -77,6 +219,8 @@ const toAnalyticsLibrary = (library: string): AnalyticsLibrary => { case 'render': case 'chat': case 'ag-ui': + case 'deep-agents': + case 'runtimes': return library; default: return 'unknown';