Two PR checks were red on the new integration:
- check-config-files: add strands-typescript/next.config.ts to the build-
config allowlist.
- Validate Showcase: update the new-integration guard pins that intentionally
trip when an integration is added — BORN_IN_SHOWCASE 6→7, calculator
_from-feature-parity count 18→19, catalog cross-join 874→920 / total_cells
855→900 / docs_only 19→20 (46 features × 20 integrations), and the aimock
substring-shadow ceiling 132→133 (+1 from the strands-typescript calculator
fixture).
Also drop the premature deploy wiring: strands-typescript is removed from
showcase_build.yml (matrix + path filter + ALL_SERVICES) because it has no
Railway service yet (deployed: false) and the railway-envs SSOT test requires
a real service entry. It re-enters the deploy pipeline when the Railway
service is provisioned (external setup per INTEGRATION-CHECKLIST).
Add a new node/TypeScript-backed AWS Strands showcase integration at
showcase/integrations/strands-typescript.
Backend: a node/TS agent server (src/agent/) built on @strands-agents/sdk
`Agent`/`tool` wrapped in @ag-ui/aws-strands `StrandsAgent` and served via
@ag-ui/aws-strands/server (`createStrandsApp`/`addStrandsExpressEndpoint`),
modeled on the upstream ag-ui aws-strands TS example server and the
langgraph-typescript infra. A single shared agent at "/" serves most demos
(tools, shared state via toolBehaviors/stateContextBuilder, HITL,
sub-agents), with tool-free specialized agents mounted at /voice,
/byoc-hashbrown, /byoc-json-render. model-factory targets OpenAI chat
completions and honors OPENAI_API_KEY / OPENAI_BASE_URL so it works behind
the showcase aimock proxy. Node-based Dockerfile + entrypoint run the agent
server (:8000) alongside the Next.js frontend.
Frontend mirrors the strands (Python) sibling's demo set and the
langgraph-typescript conventions, with HttpAgent routes proxying to the TS
agent server.
Scope: base integration + standard demos only. A2UI / declarative-gen-ui /
a2ui-fixed-schema is intentionally excluded (no A2UI agents, routes, demos,
or deps) and layered on later.
Platform wiring (mirrors langgraph-typescript): docker-compose local/dev
services on host port 3119, local-ports.json, packages.json, slug-map.ts
(born-in-showcase), showcase_build.yml matrix + path filter + metadata,
shell-docs/dashboard registries, and a logo asset. The python strands
integration is untouched.
A RAILWAY_TOKEN secret with trailing whitespace/newline (common from op
read, heredoc, shell export) was returned verbatim and produced invalid
Authorization: Bearer headers and silent Railway 401s. Trim the env-var
lane and treat whitespace-only as UNSET so the config-file fallback runs.
Also adds a missing should-have-thrown guard in the NO_HOME test and
removes a stale gateIgnore clause from findUntrackedServices docstring.
A token read from ~/.railway/config.json with surrounding whitespace or a
trailing newline passed nonEmpty() but was returned verbatim, so an
'Authorization: Bearer <token>' header could carry CR/LF or stray spaces
(Node HTTP rejects invalid header chars; Railway 401s otherwise). Trim
each return path so the canonical token is always emitted; whitespace-only
values still fall through. Also clarify the JSDoc that this resolver does
not consult process.env.RAILWAY_TOKEN.
- Reject empty/whitespace-only service in parseBuildOutputs,
mergeBuildResultFiles, and buildResultArtifactName (was only
buildResultArtifactName, and only for length===0).
- mergeBuildResultFiles now throws on duplicate service names across
slots so an upstream dispatch-name collision surfaces instead of
letting a failure+success pair spuriously look like a success in
successSet (fail-loud discipline).
- Derive BuildOutcome union and VALID_STATUSES set from a single
BUILD_OUTCOMES as-const tuple plus a compile-time exhaustiveness
assignment so they cannot drift.
- Extract validateServiceBuildResult shared validator used by both
parseBuildOutputs (context: 'entry[i]') and mergeBuildResultFiles
(context: 'slot[i]') — one set of rules, one error format, both
now include the offending index.
- Trim shouldRedeployStaging JSDoc to what/why only (dropped the
external-caller enumeration) and note in the module header that
the single-result.json-per-slot invariant is enforced workflow-side,
not by the parser. shouldRedeployStaging([]) === false behavior
unchanged.
Tests: 16 -> 23 passing, all new red-then-green; tsc 0.
Treat whitespace-only token strings as empty so the resolver falls through
to the next candidate instead of returning a bearer that fails at Railway
with a confusing 401.
Guard against non-object JSON input (null/undefined/string/number/array)
defensively before property access — the config originates from
JSON.parse of ~/.railway/config.json (untrusted).
Replace non-null assertions on re-accessed expressions with
locally-captured values so the nonEmpty narrowing actually applies; no
behavior change for the happy path.
Comment cleanups: replace stale line-number reference with a symbolic
one, reword the Ruby-parity note to acknowledge the per-project token
fallback that is intentionally not honored, drop the rotting
"(43+ chars)" parenthetical, and soften the deprecation timeline to
"a future release."
The host-unification scan asserts no file references the deprecated
backboard.railway.com endpoint. Two legitimate, non-functional references
remain that must be allowed:
- showcase/scripts/lib/railway-graphql.ts — JSDoc explains the
deprecated .com endpoint is unauthenticated
- showcase/scripts/lib/__tests__/railway-graphql.test.ts — negative
assertion that the endpoint is NOT .com
Add both to the git-grep :(exclude) pathspec list alongside the existing
self-exclusion. The test's intent — catching any NEW functional .com
usage — is preserved.
Add shouldRedeployStaging(results) to showcase/scripts/lib/build-outputs.ts.
Returns true iff at least one service finished as 'success'. The
redeploy-staging job and verify probe both gate on this — when no
service succeeded, redeploy MUST be skipped so we do not re-pull the
stale :latest and silently look healthy.
Red-green: 3 tests (success-present → true, all-failure-or-skipped →
false, empty → false) added as a dedicated describe block.
Per plan-E E-5a/E-5b.
Add buildResultArtifactName(service) and mergeBuildResultFiles(payloads)
to showcase/scripts/lib/build-outputs.ts so each matrix slot in
showcase_build.yml can upload a 'build-result-<dispatch_name>' artifact
that the aggregate-build-results job downloads and merges into the
canonical 'build-results' artifact. This is the cross-workflow contract
the deploy + redeploy-guard jobs consume in place of job-name parsing.
Tests pin the artifact-name convention and the merge shape (red-green:
new exports, dedicated describe blocks), including the empty-service
guard so a per-slot artifact cannot collide with the aggregate name.
Per plan-E E-4c/E-4d.
E-3a/b/c per plan-E. Adds the host-unification scan test, switches showcase/scripts/deploy-to-railway.ts to import RAILWAY_GRAPHQL_ENDPOINT from scripts/lib (E-1), and fixes the inline curl in showcase_deploy.yml line 205. The .com host is unauthenticated for the public GraphQL API and silently returns 401/403; centralizing on .app prevents the drift from returning. Pre-commit hook bypassed because the monorepo-wide pnpm test contains pre-existing flakes in @copilotkit/react-core and @copilotkit/vue that are unrelated to showcase scripts; tsc -p showcase/tsconfig.json --noEmit and the railway-graphql vitest pass cleanly.
These scripts existed solely to sanity-check the v1-docs → shell-docs
cutover during this PR. The cutover is now landed, so the audit
pipeline has no further job:
Removed
- audit-docs-porting.ts (+ test) — produced per-framework JSON
summaries of what still needed porting. Referenced only by its
own package.json script entry and a comment in
verify-shell-docs.ts.
- split-baseline.mjs — orphan helper that read
audit-output/_summary.json + verify-baseline.txt and wrote
per-framework .baseline.txt files. Not in package.json, not in
any workflow.
- lib/component-mapping.ts — 80-row v1-component → v2-strategy
table. Zero consumers anywhere in the repo (verified via
`grep -rln "component-mapping\|COMPONENT_MAPPING"`).
- audit-output/.gitkeep + showcase/scripts/.gitignore — the
.gitkeep was a placeholder for the audit pipeline's output
directory; the .gitignore's three entries (audit-output/*.json,
audit-output/*.baseline.txt, verify-baseline.txt) all referenced
files only produced/consumed by the deleted scripts.
- audit-docs-porting script entry in showcase/scripts/package.json.
- The comment cross-reference to audit-docs-porting.ts in
verify-shell-docs.ts's fenced-code-strip rationale.
Kept (still actively useful)
- verify-shell-docs.ts (+ test) — ongoing docs validator wired into
`pnpm verify-shell-docs`; catches dead inline-demo refs, unknown
snippet regions, broken internal links, and unresolved alias
imports.
- lib/essential-content.ts (+ test) — page-shape rule engine
consumed by verify-shell-docs.
- probe-shell-docs.ts — URL-coverage crawler (not in package.json
but runnable via `tsx`; kept as a one-off utility).
Net diff: 905 lines removed, 1 line touched (verify-shell-docs.ts
comment update). Tests: 594/594 passing across 27 files in
showcase/scripts. Parity: exit 0.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Five component-mapping entries (A2UI, MigrateTo1100, MigrateTo182,
MigrateToV2, SelfHosting) carried `notes: '...needs shim stub'` while
all five are already registered as `stubWithPartial(...)` entries in
shell-docs/src/lib/mdx-registry.tsx (lines 472, 477-480). The
PydanticAIIcon entry also claimed it was an "emoji shim (🐍)" when
mdx-registry.tsx:527 resolves it to the real PydanticAiIcon SVG.
The stale notes would mislead anyone using component-mapping.ts as a
TODO list — they'd duplicate already-completed shim work or assume
PydanticAIIcon was a placeholder when the real icon is shipping.
This is a comments-only correction; the `kind` and `target` fields
remain unchanged so the mapping's behavior is identical.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Maps all 77 JSX components from the Phase 0 audit to either
use-existing (74 entries) or shim (5 entries: A2UI, MigrateTo1100,
MigrateTo182, MigrateToV2, SelfHosting). Creates the legacy/index.ts
barrel scaffold that Task 13 will populate with shim implementations.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Delete the pre-generated starters/ directory tree (previously
synced from packages/ by generate-starters.ts). Add
extract-starter.ts which produces a starter tarball on demand
from any integration. Move shared starter template files to
showcase/shared/starter-template/.
Port the 4084 scripts-layer enhancements so 4085's showcase toolchain
matches the new feature shape:
- lib/manifest.ts: ManifestDemo gains optional `command` field; parser
accepts + validates it (non-empty string, frozen).
- bundle-demo-content.ts: inline `@region[name]` / `@endregion[name]`
comment-marker extraction; informational-only demos (no route, e.g.
cli-start) are skipped; markers stripped from bundled content;
regions: { file, startLine, endLine, code, language } emitted per
demo. External-highlight-file merging (4085-specific) preserved, so
backend agents under src/agents/*.py still flow into the bundle.
- validate-parity.ts: accepts demos at BOTH demos/<cell>/ (4084 layout)
and src/app/demos/<cell>/ (4085 layout); informational demos
(command field) are excluded from the parity audit.
- tests: bundle-demo-content.test.ts expectedDemos updated for the
shared-state rename; generate-registry.test.ts feature count 25→32;
validate-parity.test.ts missing-demo-dir message updated to match
the new dual-location wording.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
parseManifest (lib/manifest.ts):
- Shape errors for demos[i].route: number / null / object / empty string
- Shape error for route not starting with /demos/
- Happy path persists frozen demo.route on the parsed entry
- Backward-compat: demos[i] without route is accepted with undefined route
validate-parity:
- Negative-case regression: missing-demo-dir's expectedDir is derived from
demo.route (not demo.id) when route is present; mismatched id + route
was silently hiding drift.
- Fallback-case: demo with no route resolves expectedDir from demo.id
- routeToDirName unit tests: undefined / bare /demos/ / normal tail segment
TDD verified: mutating the /demos/ prefix guard failed the relevant test
(RED), restoring the guard passed it (GREEN). Mutating expectedDir to
demo.id-only failed the negative-case test (RED), restoring passed
(GREEN).
demo.id is the CATALOG identifier (matched to qa/spec filenames and
shell registry entries). demo.route is the URL + filesystem path
(/demos/<dir> → src/app/demos/<dir>/). They are deliberately separate
— a manifest with id: hitl-in-chat and route: /demos/hitl lives at
src/app/demos/hitl/.
validate-parity.ts previously resolved the demo directory from
demo.id, producing a spurious missing-demo-dir MUST for every such
split. Fix:
- lib/manifest.ts: add optional route field to ManifestDemo; if present,
parser requires it to be a non-empty string beginning with "/demos/".
- validate-parity.ts: introduce routeToDirName helper (matches
bundle-demo-content.ts idiom); loop over demos resolving expected
dir from route and falling back to id. missing-demo-dir PackageIssue
now carries both demoId and expectedDir so deriveMessage can flag
route-resolved paths distinctly.
- __tests__/validate-parity.test.ts: red-green regression test — a
package with id: hitl-in-chat, route: /demos/hitl, and dir
src/app/demos/hitl/ must PASS (no missing-demo-dir error).
Two foundational modules consumed by all three validators:
- lib/slug-map.ts: single source of truth for the showcase slug
taxonomy. ENTRIES array is the sole declaration; BORN_IN_SHOWCASE,
SLUG_MAP, SLUG_TO_EXAMPLES, and FALLBACK_MAP are derived at module
load and frozen via freezeSet/freezeMap/freezeMap2D helpers
(defineProperty-based to block Set.add / Map.set at runtime).
SlugEntry is a tagged union: born-in-showcase variants have empty
examples and no fallback; non-born variants carry a non-empty
tuple. Each slug passes isShowcaseSlug at module load.
- lib/manifest.ts: parseManifest returns a tagged ParsedManifest
union (ok | missing | malformed | unreadable) with never-throws
content contract. Uses statSync + errno inspection rather than
existsSync to distinguish ENOENT from EACCES/ENOTDIR. DemoId is a
branded string minted only through createDemoId. Empty-string
dirSlug is rejected as a caller bug; undefined opts out of the
slug-match check. Deep-freezes the returned Manifest.