# Defense Presentation Visual Pass Implementation Plan > **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. **Goal:** Polish the weakest `/present` visuals for defense use: Scene 6 architecture, Scene 7 authoring, Scene 10 interrupt/evidence, and discussion speaker hints. **Architecture:** Keep the current presentation route, storyboard, replay, and component model. Improve composition through small scene helpers, CSS layout states, and focused tests; do not introduce a new theme system, chat framework, or slide runtime. Use existing editorial/presentation tokens and keep all changes scoped under `web/apps/console/src/presentation`. **Tech Stack:** React, TypeScript, CSS, React Flow, Vitest, Testing Library, Playwright CLI smoke screenshots. ## Global Constraints - Follow design spec: `docs/historical/superpowers/specs/2026-07-08-defense-presentation-visual-pass-design.md`. - Do not change the 12-scene storyboard order. - Do not replace chat UI in this slice. - Do not add new dependencies. - Do not introduce a third global theme or new `:root` palette. - Preserve keyboard navigation: arrow keys for presentation, Escape for overlays, roving focus inside `InteractiveFigure`. - Respect `motionDisabled` and `prefers-reduced-motion`. - Keep `/console` unaffected. - Verify at `1280x720` and `1024x768`. --- ## File Structure - Modify `web/apps/console/src/presentation/figures/InteractiveFigure.tsx` - Add or refine a stage-scale figure size variant. - Modify `web/apps/console/src/presentation/figures/interactive-figure.css` - Make wide/stage figures larger, horizontally navigable, and readable. - Modify `web/apps/console/src/presentation/scenes/ArchitectureScene.tsx` - Use the improved figure size and add a scene-level data hook if needed. - Modify `web/apps/console/src/presentation/scenes/ArchitectureScene.test.tsx` - Pin architecture scene sizing and nested focus usability. - Modify `web/apps/console/src/presentation/SceneBody.tsx` - Replace the generic Scene 7 authoring card row with a named authoring loop visual or extracted component. - Modify `web/apps/console/src/presentation/SceneBody.test.tsx` - Pin authoring loop labels and active beat state. - Modify `web/apps/console/src/presentation/DemoWorkflowScene.tsx` - Add named beat layout states for Scene 10 approval/trace composition. - Modify `web/apps/console/src/presentation/DemoWorkflowScene.test.tsx` - Pin approval hero layout and canonical outcomes. - Modify `web/apps/console/src/presentation/styles/demo-workflow.css` - Improve Scene 10 graph/contract/receipt layout. - Modify `web/apps/console/src/presentation/presentation.css` - Improve authoring loop, discussion speaker note, and any shared presentation-only styles. - Modify `web/apps/console/src/presentation/DiscussionPanel.test.tsx` - Pin speaker hint as presenter note. - Modify `docs/current_roadmap.md` - Mark visual pass completed after implementation. - Move this plan to `docs/historical/superpowers/plans/2026-07-08-defense-presentation-visual-pass.md` after implementation. --- ### Task 1: Scene 6 Architecture Figure Scale **Files:** - Modify: `web/apps/console/src/presentation/figures/InteractiveFigure.tsx` - Modify: `web/apps/console/src/presentation/figures/interactive-figure.css` - Modify: `web/apps/console/src/presentation/scenes/ArchitectureScene.tsx` - Test: `web/apps/console/src/presentation/scenes/ArchitectureScene.test.tsx` - Test: `web/apps/console/src/presentation/figures/InteractiveFigure.test.tsx` **Interfaces:** - Consumes: `InteractiveFigure` props: - `size?: "standard" | "wide"` - Produces: - `size?: "standard" | "wide" | "stage"` - DOM attribute `data-figure-size="stage"` for Scene 6. - [ ] **Step 1: Add failing tests for stage-sized architecture figure** In `web/apps/console/src/presentation/scenes/ArchitectureScene.test.tsx`, replace the existing wide-size test: ```ts it("uses the wide figure presentation for the defense architecture scene", () => { renderArchitecture({ focusPath: [] }); expect(screen.getByRole("group", { name: /architecture/i })).toHaveAttribute("data-figure-size", "wide"); expect(screen.getByTestId("architecture-scene")).toBeInTheDocument(); }); ``` with: ```ts it("uses the stage figure presentation for the defense architecture scene", () => { renderArchitecture({ focusPath: [] }); expect(screen.getByRole("group", { name: /architecture/i })).toHaveAttribute("data-figure-size", "stage"); expect(screen.getByTestId("architecture-scene")).toHaveAttribute("data-visual-pass", "architecture-stage"); }); ``` In `web/apps/console/src/presentation/figures/InteractiveFigure.test.tsx`, add: ```tsx it("marks stage figures as horizontally navigable presentation maps", () => { renderFigure({ focusPath: [], size: "stage" }); const figure = screen.getByRole("group", { name: /architecture/i }); expect(figure).toHaveAttribute("data-figure-size", "stage"); expect(figure.querySelector(".interactive-figure__canvas")).toBeInTheDocument(); }); ``` - [ ] **Step 2: Run failing tests** Run: ```bash pnpm --dir web --filter @lda/console test -- src/presentation/scenes/ArchitectureScene.test.tsx src/presentation/figures/InteractiveFigure.test.tsx ``` Expected: FAIL because `size="stage"` is not accepted and `ArchitectureScene` still emits `wide`. - [ ] **Step 3: Extend `InteractiveFigure` size type** In `web/apps/console/src/presentation/figures/InteractiveFigure.tsx`, change: ```ts readonly size?: "standard" | "wide"; ``` to: ```ts readonly size?: "standard" | "wide" | "stage"; ``` - [ ] **Step 4: Use `stage` from `ArchitectureScene`** In `web/apps/console/src/presentation/scenes/ArchitectureScene.tsx`, change: ```tsx
``` to: ```tsx
``` Change: ```tsx size="wide" ``` to: ```tsx size="stage" ``` - [ ] **Step 5: Add stage figure CSS** In `web/apps/console/src/presentation/figures/interactive-figure.css`, keep existing `wide` rules and add stage rules below them: ```css .interactive-figure[data-figure-size="stage"] { overflow-x: auto; overflow-y: hidden; padding: 0 0 0.45rem; scrollbar-gutter: stable; } .interactive-figure[data-figure-size="stage"] .figure-breadcrumbs { min-height: 1.6rem; padding: 0.15rem 0 0.35rem; } .interactive-figure[data-figure-size="stage"] .interactive-figure__canvas { min-width: 1440px; min-height: 470px; } .interactive-figure[data-figure-size="stage"] .react-flow { border: 1px solid color-mix(in oklch, var(--color-editorial-muted, oklch(0.48 0.025 65)) 42%, transparent); border-radius: 0.65rem; background: linear-gradient(90deg, color-mix(in oklch, var(--color-editorial-paper, oklch(0.975 0.012 82)) 92%, white), transparent 65%), color-mix(in oklch, var(--color-editorial-surface, oklch(0.96 0.012 82)) 94%, white); } .interactive-figure[data-figure-size="stage"] .figure-node { width: 256px; height: 112px; padding: 12px 16px; } .interactive-figure[data-figure-size="stage"] .figure-node__label { font-size: 16px; } .interactive-figure[data-figure-size="stage"] .figure-node__summary { font-size: 12.5px; line-height: 1.25; } ``` If this causes visible overflow outside `.presentation-canvas`, keep overflow inside `.interactive-figure` only. Do not set global viewport overflow. - [ ] **Step 6: Run focused tests** Run: ```bash pnpm --dir web --filter @lda/console test -- src/presentation/scenes/ArchitectureScene.test.tsx src/presentation/figures/InteractiveFigure.test.tsx ``` Expected: PASS. - [ ] **Step 7: Commit Task 1** ```bash git add web/apps/console/src/presentation/figures/InteractiveFigure.tsx web/apps/console/src/presentation/figures/interactive-figure.css web/apps/console/src/presentation/scenes/ArchitectureScene.tsx web/apps/console/src/presentation/scenes/ArchitectureScene.test.tsx web/apps/console/src/presentation/figures/InteractiveFigure.test.tsx git commit -m "feat: enlarge architecture presentation figure" ``` --- ### Task 2: Scene 7 Authoring Loop Visual **Files:** - Modify: `web/apps/console/src/presentation/SceneBody.tsx` - Modify: `web/apps/console/src/presentation/presentation.css` - Test: `web/apps/console/src/presentation/SceneBody.test.tsx` **Interfaces:** - Consumes: storyboard beat IDs `discover`, `author`, `diagnose`, `repair`. - Produces: authoring loop DOM: - `aria-label="agent authoring loop"` - `data-active-stage=""` - stage buttons/items with `data-authoring-active="true|false"`. - [ ] **Step 1: Add failing authoring loop test** Add this test to `web/apps/console/src/presentation/SceneBody.test.tsx`: ```tsx it("renders Scene 7 as an agent authoring loop with beat-specific emphasis", () => { const location: PresentationLocation = { kind: "main", sceneId: "authoring", beatId: "diagnose", focusPath: [] }; render( , ); const loop = screen.getByLabelText("agent authoring loop"); expect(loop).toHaveAttribute("data-active-stage", "diagnose"); expect(screen.getByText("Discover capability")).toBeInTheDocument(); expect(screen.getByText("Author draft")).toBeInTheDocument(); expect(screen.getByText("Validate and diagnose")).toHaveAttribute("data-authoring-active", "true"); expect(screen.getByText("Repair")).toBeInTheDocument(); expect(screen.getByText("Compile or save")).toBeInTheDocument(); }); ``` - [ ] **Step 2: Run failing test** Run: ```bash pnpm --dir web --filter @lda/console test -- src/presentation/SceneBody.test.tsx ``` Expected: FAIL because the current authoring scene renders generic step labels. - [ ] **Step 3: Replace `AuthoringScene` data** In `web/apps/console/src/presentation/SceneBody.tsx`, replace: ```ts const authoringSteps = [ { id: "discover", label: "Discover" }, { id: "author", label: "Author" }, { id: "diagnose", label: "Diagnose" }, { id: "repair", label: "Repair" }, ]; ``` with: ```ts const authoringSteps = [ { id: "discover", label: "Discover capability", detail: "wf schema / cap inspect" }, { id: "author", label: "Author draft", detail: "wf draft create / add-step / bind" }, { id: "diagnose", label: "Validate and diagnose", detail: "structured diagnostics + repair hints" }, { id: "repair", label: "Repair", detail: "focused edit, no full rewrite" }, { id: "compile", label: "Compile or save", detail: "artifact / deployment / run" }, ] as const; ``` - [ ] **Step 4: Replace `AuthoringScene` markup** In `SceneBody.tsx`, replace the current `AuthoringScene` body with: ```tsx const AuthoringScene = ({ scene, beat }: { scene: SceneDefinition; beat: SceneBeatDefinition }) => ( <>

{beat.caption}

{scene.evidencePointer}

); ``` This keeps the authoring scene declarative and avoids a new file for one local visual. If the file becomes unwieldy in review, extract to `web/apps/console/src/presentation/scenes/AuthoringScene.tsx` in a follow-up. - [ ] **Step 5: Add authoring loop CSS** In `web/apps/console/src/presentation/presentation.css`, replace or override the old `.scene-body__authoring` rules with: ```css .scene-body__authoring-loop { position: relative; display: grid; grid-template-columns: repeat(5, minmax(0, 1fr)); gap: 0.75rem; margin-top: 1rem; padding: 1rem 0 0.2rem; } .scene-body__authoring-loop-rail { position: absolute; z-index: 0; top: 2.15rem; left: 8%; right: 8%; height: 2px; background: linear-gradient(90deg, var(--accent-cyan), color-mix(in oklch, var(--accent-cyan) 20%, transparent)); } .scene-body__authoring-node { position: relative; z-index: 1; display: grid; align-content: start; gap: 0.35rem; min-height: 8.6rem; border: 1px solid color-mix(in oklch, var(--stage-line) 72%, transparent); border-radius: 0.75rem; padding: 0.75rem; background: color-mix(in oklch, var(--stage-surface) 88%, transparent); transition: opacity 180ms ease, transform 180ms ease, border-color 180ms ease, background 180ms ease; } .scene-body__authoring-node[data-authoring-active="true"] { transform: translateY(-0.35rem); border-color: var(--accent-cyan); background: color-mix(in oklch, var(--accent-cyan) 13%, var(--stage-surface)); } .scene-body__authoring-node[data-authoring-past="true"] { opacity: 0.76; } .scene-body__authoring-node small { color: var(--text-muted); font: 0.72rem/1.35 var(--font-interface); } @media (max-width: 1050px) { .scene-body__authoring-loop { grid-template-columns: repeat(3, minmax(0, 1fr)); } .scene-body__authoring-loop-rail { display: none; } } ``` Remove stale `.scene-body__authoring-step` selectors only if they are no longer used. Keep lifecycle selectors intact. - [ ] **Step 6: Run focused test** Run: ```bash pnpm --dir web --filter @lda/console test -- src/presentation/SceneBody.test.tsx ``` Expected: PASS. - [ ] **Step 7: Commit Task 2** ```bash git add web/apps/console/src/presentation/SceneBody.tsx web/apps/console/src/presentation/SceneBody.test.tsx web/apps/console/src/presentation/presentation.css git commit -m "feat: clarify authoring scene visual loop" ``` --- ### Task 3: Scene 10 Interrupt Contract Hero **Files:** - Modify: `web/apps/console/src/presentation/DemoWorkflowScene.tsx` - Modify: `web/apps/console/src/presentation/styles/demo-workflow.css` - Test: `web/apps/console/src/presentation/DemoWorkflowScene.test.tsx` **Interfaces:** - Consumes: - `beat.id` values `interrupt`, `approval`, `resume`, `trace`. - Existing `InterruptContractPreview`, `WorkflowGraphStage`, `OperationBlock`. - Produces: - `data-demo-layout="operation|graph|interrupt|approval|evidence"` - Approval beat contract rendered as the hero surface. - [ ] **Step 1: Add failing layout tests** In `web/apps/console/src/presentation/DemoWorkflowScene.test.tsx`, add: ```tsx it("makes the Scene 10 approval contract the primary visual", () => { renderBeat("approval", "interrupt-evidence"); const stage = screen.getByLabelText("demo workflow stage"); expect(stage).toHaveAttribute("data-demo-layout", "approval"); expect(screen.getByLabelText("typed interrupt contract")).toHaveAttribute("data-hero", "true"); expect(screen.getByLabelText("workflow graph")).toBeInTheDocument(); }); it("marks trace beat as evidence layout", () => { renderBeat("trace", "interrupt-evidence"); expect(screen.getByLabelText("demo workflow stage")).toHaveAttribute("data-demo-layout", "evidence"); expect(screen.getByLabelText("workflow.runs.trace operation")).toBeInTheDocument(); }); ``` - [ ] **Step 2: Run failing tests** Run: ```bash pnpm --dir web --filter @lda/console test -- src/presentation/DemoWorkflowScene.test.tsx ``` Expected: FAIL because `demo workflow stage`, `data-demo-layout`, and `data-hero` are absent. - [ ] **Step 3: Add named layout helper** In `web/apps/console/src/presentation/DemoWorkflowScene.tsx`, add above the component: ```ts type DemoWorkflowLayout = "operation" | "graph" | "interrupt" | "approval" | "evidence"; const layoutForBeat = (beatId: string): DemoWorkflowLayout => { if (beatId === "operation" || beatId === "resume") return "operation"; if (beatId === "interrupt") return "interrupt"; if (beatId === "approval") return "approval"; if (beatId === "trace" || beatId === "output") return "evidence"; return "graph"; }; ``` - [ ] **Step 4: Use layout state in markup** Inside `DemoWorkflowScene`, after `const execution = graphExecutionForBeat(beat.id);`, add: ```ts const layout = layoutForBeat(beat.id); ``` Change: ```tsx
``` to: ```tsx
``` Change: ```tsx ``` to: ```tsx ``` - [ ] **Step 5: Extend `InterruptContractPreview` props** Open `web/apps/console/src/presentation/InterruptContractPreview.tsx`. Add a `hero?: boolean` prop and emit `data-hero`. Expected final signature: ```ts type InterruptContractPreviewProps = { readonly contract: InterruptContractDisplay; readonly mode: "preview" | "approval"; readonly hero?: boolean; }; ``` Expected root element shape: ```tsx