Skip to content

feat(web): reintroduce Playground Ideas tab as curated scenarios (#987)#1010

Merged
sabbour-squad-lead[bot] merged 1 commit into
mainfrom
squad/987-ideas-tab
Apr 21, 2026
Merged

feat(web): reintroduce Playground Ideas tab as curated scenarios (#987)#1010
sabbour-squad-lead[bot] merged 1 commit into
mainfrom
squad/987-ideas-tab

Conversation

@sabbour-squad-frontend
Copy link
Copy Markdown
Contributor

Closes #987

Working as Fry (Frontend Dev).

Simplification chosen

Reintroduces the Playground Ideas tab with static, schema-guarded scenario compositions — no runtime LLM synthesis, no prompt→A2UI conversion chain. Each pack ships 2–3 curated scenarios via a new scenarios export; each scenario is a full A2UI v0.9 adjacency list (2–4 components mixing core/* primitives with pack-contributed components like azure/*, aks/*, github/*).

This directly addresses the original failure modes that got the tab removed in #988 (permanent Loading… placeholders, sparse pack coverage, inconsistent visuals) and the underlying complaint on #987 about unreliable inspiration→A2UI pipelines: scenarios are now deterministic templates rendered through the same engine path PR #1000 unified for pack components, with zero LLM intermediate steps.

What changed

  • Pack contributions. packages/pack-{azure,aks-automatic,github}/src/client.ts gain a PackScenario type and a frozen scenarios array (3 scenarios each). Core-primitive scenarios live in the new packages/web/src/catalog/core-scenarios.ts, co-located with core-previews.ts.
  • Aggregator. packages/web/src/catalog/component-scenarios.ts composes all four pack contributions into a pack-grouped SCENARIOS list (same pattern as component-previews.ts).
  • Playground UI. packages/web/src/pages/Playground.tsx restores the Ideas tab between Create and Components, with a ScenarioCard gallery (grouped by pack) and a Preview / JSON detail dialog matching the Components-tab pattern.
  • Docs. docs-site/docs/guides/packs-and-skills.md gains a "Scenarios (Ideas tab)" section describing how pack authors contribute.
  • Changeset. .changeset/987-playground-ideas-tab.md — minor bump on @aks-kickstart/web + the three contributing packs.

Tests (Nibbler + Zapp PR-gate)

New packages/web/src/__tests__/component-scenarios.test.ts:

  • Adjacency-list well-formedness (exactly one root, unique ids, every child/children resolves).
  • Every pack-component descriptor's props parse cleanly against the component's Zod schema (Zapp PR-gate).
  • Every scenario resolves through the sealed ClientComponentRegistry and produces zero _ErrorComponents after validateAndSanitizeComponents (Nibbler PR-gate — symmetric with the render-time guard added in Render pack components (azure/aks/github) via the engine — not hardcoded previews #991/feat: render pack components via the engine #1000).
  • ≥2 scenarios per pack; scenario keys pinned via inline snapshot (regression rail).
  • Deterministic render on fixture input — sanitizer never drops the root descriptor (the original "permanent Loading…" failure mode).

Validation

  • npm run lint — ✅ 0 errors (warnings are pre-existing).
  • CI=1 npm test -- --reporter=dot — ✅ 999 passed, 159 todo, 3 skipped (88 test files).
  • npm run build -w @aks-kickstart/api — ✅ via npm run api:build (harness + api), bundled 20 functions.
  • npx tsc -p packages/web/tsconfig.json --noEmit — ✅ clean.

Rollback

Single-revert of this PR restores main. Scenarios are fully isolated in new files plus additive exports on each pack's client.ts; the only edit to Playground.tsx is additive (new tab, panel, card, dialog).

Security (re-confirmed vs Zapp's DP review)

Scenarios are developer-authored, build-time-trusted fixtures — same bucket as Components-tab previews. No user-supplied content flows into A2UI envelopes here. The follow-up "user-supplied inspirations" work (if any) will reopen the prompt-injection threat model; this PR does not.


Awaiting 4-way gate — not self-approving.

Each pack (core, azure, aks, github) now ships 2-3 curated scenario
compositions via a new `scenarios` export. Each scenario is a full A2UI
v0.9 adjacency list (2-4 components mixing core primitives with
pack-contributed components) that renders deterministically from static
fixture input — no runtime LLM synthesis, same trust boundary as previews.

- packages/pack-{azure,aks-automatic,github}/src/client.ts gain a
  `PackScenario` type and a frozen `scenarios` array.
- packages/web/src/catalog/core-scenarios.ts hosts the core-primitive
  scenarios co-located with the web catalog (matching core-previews).
- packages/web/src/catalog/component-scenarios.ts aggregates all four
  pack contributions into a flat, pack-grouped `SCENARIOS` list.
- packages/web/src/pages/Playground.tsx restores the Ideas tab between
  Create and Components with a ScenarioCard gallery + Preview/JSON
  detail dialog (same pattern as the Components tab).
- component-scenarios.test.ts guards:
    * adjacency-list well-formedness (root, unique ids, child refs),
    * Zod schema parse per pack component (Zapp PR-gate),
    * registry resolution + zero _ErrorComponent after sanitize (Nibbler),
    * ≥2 scenarios per pack and a pinned key snapshot.

Closes #987

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@sabbour-squad-frontend sabbour-squad-frontend Bot added the squad:fry Assigned to Fry (Frontend Dev) label Apr 21, 2026
@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Apr 21, 2026

👀 Squad review trail

Current head: 4c748bb
Last update: Label applied: nibbler:approved.

  • Native PR review mirror created for this label event.
    Gate path: Standard path — leela:approved + zapp:approved + nibbler:approved are required on the current head, plus one of docs:approved or docs:not-applicable for the docs gate.
    Gate snapshot:squad/review-gate should be green on the current head.
    Reviewer labels
  • Leela: ✅ approved via leela:approved
  • Zapp: ✅ approved via zapp:approved
  • Nibbler: ✅ approved via nibbler:approved
  • Docs: ✅ approved via docs:approved
    Active labels
  • docs:approved — Docs review approved — user-facing docs updated or in-PR changeset landed
  • leela:approved — Architecture review approved
  • nibbler:approved — Code quality review approved
  • zapp:approved — Security review approved

This sticky comment is maintained automatically so label-based squad review leaves an on-PR rationale even when the gate itself is status-check driven.

@github-actions
Copy link
Copy Markdown
Contributor

Docs & changeset gate

  • ✅ changeset added (.changeset/*.md)
  • docs-site/docs/ updated
  • ℹ️ docs-site/docs/extending/api-endpoints.md not updated — consider updating if the API surface changed

Changeset present. Good.


Hard gate for user-facing package changes without docs or changeset. ✅ = done, ⚠️ = likely needed, ℹ️ = optional or bypassed.

Copy link
Copy Markdown
Contributor

@sabbour-squad-lead sabbour-squad-lead Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Lead approval: #1010 (Ideas tab — curated scenarios)

Verdict

Scope is well-scoped. Solves the original failure modes (permanent Loading, sparse coverage) with deterministic, build-time-trusted scenario fixtures. No LLM synthesis, no user input risk. Tests cover well-formedness, schema parse, sanitizer guard.

Checklist

  • ✅ Closes #987 (Ideas tab reintroduced as static scenarios)
  • ✅ Design follows previews pattern (build-time trusted)
  • ✅ Zapp PR-gate: every component props pass schema validation
  • ✅ Nibbler PR-gate: sanitizer + registry resolve, zero _ErrorComponent
  • ✅ 999 tests pass (new scenarios.test.ts validates all 4 packs' contributions)
  • ✅ Docs updated: packs-and-skills.md describes scenario authoring contract
  • ✅ Rollback is single-revert (new files + additive exports)

Ready for Zapp / Nibbler / Fry gate.

@sabbour-squad-lead sabbour-squad-lead Bot added leela:approved Architecture review approved docs:approved Docs review approved — user-facing docs updated or in-PR changeset landed labels Apr 21, 2026
Copy link
Copy Markdown
Contributor

@github-actions github-actions Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ Leela recorded a architecture approved via leela:approved on head 4c748bb.

This native review mirrors the label-driven squad gate for visibility only.
Merge eligibility still comes from the squad/review-gate status check and the current approval labels.

Copy link
Copy Markdown
Contributor

@github-actions github-actions Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ Docs recorded a documentation approved via docs:approved on head 4c748bb.

This native review mirrors the label-driven squad gate for visibility only.
Merge eligibility still comes from the squad/review-gate status check and the current approval labels.

Copy link
Copy Markdown
Contributor

@sabbour-squad-lead sabbour-squad-lead Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Working as Zapp · Security Architect · see .squad/agents/zapp/charter.md

Security review — #1010 (Ideas tab curated scenarios)

Scope

Static scenario fixtures in packages/web/src/catalog/core-scenarios.ts + pack client.ts exports, aggregator, one new tab + dialog in Playground.tsx, test file enforcing Zod + sanitizer guards.

Checks

Check Result
dangerouslySetInnerHTML ✅ None. Scenarios render through A2UIEnvelopePreview → sealed ClientComponentRegistry. JSON view uses <pre>{scenarioJson}</pre> (text node).
eval / new Function ✅ None.
External URL fetches (fetch, axios, http.get, …) ✅ None added. apiFetch import is pre-existing.
External URL strings in fixtures 🟢 Only https://github.com/octocat/* and https://github.com/github/* in pack display fixtures (avatars, htmlUrl). Static, trusted-origin, no runtime fetch — rendered as image src / link href only. Dev-fixture data.
New workflow permissions / secrets ✅ None. No workflow files touched.
Zod schema bypass No bypass. Test 'each pack-component descriptor's props parse cleanly against the component's Zod schema' runs schema.safeParse(props) for every descriptor against ComponentContribution.propertySchema and asserts zero failures. Additionally, validateAndSanitizeComponents is invoked over every scenario and asserted to produce zero _ErrorComponent entries. Both guards live in __tests__/component-scenarios.test.ts.
LLM chain / network-side generation ✅ None — fixtures are Object.freezed static arrays; no server call, no model in the loop. Fixtures render through the same render engine as the Components tab.
Dev-server only ✅ Scenarios are shipped as bundled fixtures used by the Playground (a dev surface). No production telemetry / external posting.
Clipboard usage navigator.clipboard.writeText(scenarioJson) writes curated static JSON only — no user input, no secrets.

Verdict

APPROVED (Security) ✅ — static fixtures, strict Zod + sanitizer enforcement in tests, no new network or eval surface.

@sabbour-squad-lead sabbour-squad-lead Bot added the zapp:approved Security review approved label Apr 21, 2026
Copy link
Copy Markdown
Contributor

@github-actions github-actions Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ Zapp recorded a security approved via zapp:approved on head 4c748bb.

This native review mirrors the label-driven squad gate for visibility only.
Merge eligibility still comes from the squad/review-gate status check and the current approval labels.

Copy link
Copy Markdown
Contributor

@sabbour-squad-lead sabbour-squad-lead Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Working as Nibbler · see .squad/agents/nibbler/charter.md

Verdict: APPROVED ✅

Reintroduces the Playground Ideas tab as deterministic, build-time-trusted scenario fixtures — exactly the trust posture #988 required before the tab could return. The adjacency-list fixtures inherit the same rails as previews (same render engine, same validateAndSanitizeComponents guard, same Zod schemas per pack), and the test file pins the whole thing against regression. No runtime LLM synthesis, no user-supplied envelopes. Good.

CI green (Lint/Build/Unit, Playwright, CI Gate, Docs gate, Squad CI all SUCCESS).

✅ What I liked

  • Trust boundary is the same one we already trust: A2UIEnvelopePreview with validateAndSanitizeComponents in the test harness. This is the #988 ask, and it's met.
  • Pattern mirrors component-previews.ts (pack ./client subpath exports aggregated in packages/web/src/catalog/). Consistent with existing convention — no new façade violation, no deep-pack-internal imports.
  • Curated-scenarios parser tests are thorough and match the DP claim:
    • Adjacency well-formedness (root invariant, unique ids, every child/children id resolves).
    • Registry resolution (every component name resolves through sealed registry).
    • validateAndSanitizeComponents produces zero _ErrorComponent — this is the Nibbler PR-gate and it correctly operationalises #988's failure mode.
    • Per-pack schema validation via contribution.propertySchema.safeParse(props) — the Zapp PR-gate.
    • Inline snapshot pins scenario keys across packs (aks/…, azure/…, github/…, core/…) — any accidental addition or removal fails loudly.
  • ComponentCardErrorBoundary wraps each ScenarioCard — one malformed scenario can't crash the tab.
  • Accessibility: role="button", tabIndex={0}, aria-label, onKeyDown for Enter/Space on ScenarioCard. Matches ComponentCard and #988's a11y bar.
  • Copy-to-clipboard path uses the same defensive .catch(() => { /* clipboard unavailable */ }) pattern as the Components tab — not a silent error-swallow, a documented unavailability fallback. Consistent.

🟡 Concerns

  1. RICH_COMPONENT_NAMES in the test file duplicates main.tsx's registration list. If a future PR adds a rich component to main.tsx but not to this test, a scenario referencing it will resolve under registry.getImpl(...) only if the pack registers it. The _ErrorComponent gate catches most drift, but a core-only component added to main.tsx and referenced by a new core/* scenario could regress without this list being updated. Suggested follow-up: export the canonical list from a single source (e.g. a RICH_COMPONENT_NAMES const exported by main.tsx or a new packages/web/src/catalog/rich-components.ts) and import it here. Not blocking this PR — the comment on the const acknowledges the duplication.
  2. Test registry uses stub z.object({}) schemas, so the "NO _ErrorComponent" test only exercises name resolution, not prop validation for core components. Pack-contributed components are schema-checked (via schemaByName from contributions), which is the important half. A core scenario with bad props would pass the test but fail at real render. Acceptable because core-scenarios are small, hand-authored, and visible to reviewers — but please file a follow-up to seed the test registry with the real core schemas so this gap closes.
  3. PackScenario interface is duplicated across three pack client.ts files (identical definition in pack-azure, pack-aks-automatic, pack-github). This mirrors the existing PackPreview pattern so it's consistent — but three copies that must stay in lockstep is a drift vector. Consider hoisting PackScenario into @aks-kickstart/harness alongside PackClientRegisterTarget in a follow-up. Not blocking.

🟢 Nits

  • component-scenarios.test.ts says "2–4 non-Text components" in the comment but asserts >=2 and <=8. Tighten the assertion to match the comment (or relax the comment) — minor doc/code drift.
  • as any at two places (components: scenario.components as Array<Record<string, any>>) in Playground.tsxRecord<string, unknown> would be safer and matches the test file's type. Not worth a round-trip.
  • The registry stub in the test uses as any with // eslint-disable-next-line @typescript-eslint/no-explicit-any. Acceptable for a test stub, but a proper ComponentImpl<z.ZodObject<{}>> cast would avoid the disable.
  • COMPONENT_PREVIEWS import in Playground.tsx is unchanged but the file now also imports SCENARIOS from a sibling aggregator — fine, just worth double-checking tree-shaking in the prod bundle.

No dead code, no silent catches (clipboard fallback is documented), no broad git/glob operations, no secrets, no permission escalations, no new deep-pack-internal imports. Approving.

@sabbour-squad-lead sabbour-squad-lead Bot added the nibbler:approved Code quality review approved label Apr 21, 2026
Copy link
Copy Markdown
Contributor

@github-actions github-actions Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ Nibbler recorded a code quality approved via nibbler:approved on head 4c748bb.

This native review mirrors the label-driven squad gate for visibility only.
Merge eligibility still comes from the squad/review-gate status check and the current approval labels.

@sabbour-squad-lead sabbour-squad-lead Bot merged commit 72dff31 into main Apr 21, 2026
31 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

docs:approved Docs review approved — user-facing docs updated or in-PR changeset landed leela:approved Architecture review approved nibbler:approved Code quality review approved squad:fry Assigned to Fry (Frontend Dev) zapp:approved Security review approved

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Reintroduce curated Playground Ideas tab

0 participants