docs: record defense presentation visual pass
This commit is contained in:
@@ -123,8 +123,10 @@ Implementation order:
|
||||
|
||||
Presentation wishlist / defense readiness:
|
||||
|
||||
- Visual pass for Scenes 6, 7, and 10: fix spacing, graph scale, captions,
|
||||
evidence receipt placement, and remove remaining generic AI-slide styling.
|
||||
- Completed: visual pass for Scenes 6, 7, and 10 fixed architecture figure
|
||||
scale, authoring-loop clarity, interrupt/evidence emphasis, and presenter-note
|
||||
treatment. Implementation:
|
||||
[`defense presentation visual pass`](historical/superpowers/plans/2026-07-08-defense-presentation-visual-pass.md).
|
||||
- Presenter runbook:
|
||||
[`defense presentation runbook`](runbooks/defense-presentation.md) covers
|
||||
exact URLs, keyboard controls, live RPC/demo fallback steps, story spine,
|
||||
|
||||
@@ -0,0 +1,871 @@
|
||||
# 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/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
|
||||
<section className="architecture-scene" data-testid="architecture-scene">
|
||||
```
|
||||
|
||||
to:
|
||||
|
||||
```tsx
|
||||
<section className="architecture-scene" data-testid="architecture-scene" data-visual-pass="architecture-stage">
|
||||
```
|
||||
|
||||
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="<beat.id>"`
|
||||
- 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(
|
||||
<SceneBody
|
||||
location={location}
|
||||
demo={demo}
|
||||
selectedNodeId={null}
|
||||
selectNode={noop}
|
||||
openEvidence={noop}
|
||||
openDiscussion={noop}
|
||||
onFocusPathChange={noop}
|
||||
motionDisabled={false}
|
||||
/>,
|
||||
);
|
||||
|
||||
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 }) => (
|
||||
<>
|
||||
<StageCaption eyebrow="Act II · implemented" title={scene.title}>
|
||||
<p>{beat.caption}</p>
|
||||
</StageCaption>
|
||||
<div className="scene-body__authoring-loop" aria-label="agent authoring loop" data-active-stage={beat.id}>
|
||||
<div className="scene-body__authoring-loop-rail" aria-hidden="true" />
|
||||
{authoringSteps.map((step, i) => {
|
||||
const isActive = beat.id === step.id;
|
||||
const isPast = authoringSteps.findIndex((candidate) => candidate.id === beat.id) > i;
|
||||
return (
|
||||
<div
|
||||
key={step.id}
|
||||
className="scene-body__authoring-node"
|
||||
data-authoring-active={isActive}
|
||||
data-authoring-past={isPast}
|
||||
>
|
||||
<span className="scene-body__authoring-number">{i + 1}</span>
|
||||
<strong>{step.label}</strong>
|
||||
<small>{step.detail}</small>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
<p className="scene-body__evidence">{scene.evidencePointer}</p>
|
||||
</>
|
||||
);
|
||||
```
|
||||
|
||||
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
|
||||
<div className="demo-workflow-stage" data-beat={beat.id}>
|
||||
```
|
||||
|
||||
to:
|
||||
|
||||
```tsx
|
||||
<div className="demo-workflow-stage" data-beat={beat.id} data-demo-layout={layout} aria-label="demo workflow stage">
|
||||
```
|
||||
|
||||
Change:
|
||||
|
||||
```tsx
|
||||
<InterruptContractPreview contract={contract} mode={contractMode} />
|
||||
```
|
||||
|
||||
to:
|
||||
|
||||
```tsx
|
||||
<InterruptContractPreview
|
||||
contract={contract}
|
||||
mode={contractMode}
|
||||
hero={layout === "approval"}
|
||||
/>
|
||||
```
|
||||
|
||||
- [ ] **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
|
||||
<aside
|
||||
className="interrupt-contract-preview"
|
||||
data-mode={mode}
|
||||
data-hero={hero ? "true" : "false"}
|
||||
aria-label="typed interrupt contract"
|
||||
>
|
||||
```
|
||||
|
||||
If the file currently uses a different prop type name, keep its local naming and add the `hero` field there.
|
||||
|
||||
- [ ] **Step 6: Add approval layout CSS**
|
||||
|
||||
In `web/apps/console/src/presentation/styles/demo-workflow.css`, add below `.demo-workflow-stage__graph` rules:
|
||||
|
||||
```css
|
||||
.demo-workflow-stage[data-demo-layout="approval"] .demo-workflow-stage__graph,
|
||||
.demo-workflow-stage[data-demo-layout="interrupt"] .demo-workflow-stage__graph {
|
||||
grid-template-columns: minmax(19rem, 34%) minmax(0, 1fr);
|
||||
gap: 1rem;
|
||||
}
|
||||
|
||||
.demo-workflow-stage[data-demo-layout="approval"] .interrupt-contract-preview,
|
||||
.demo-workflow-stage[data-demo-layout="interrupt"] .interrupt-contract-preview {
|
||||
grid-column: 1;
|
||||
grid-row: 1;
|
||||
}
|
||||
|
||||
.demo-workflow-stage[data-demo-layout="approval"] .workflow-graph-stage,
|
||||
.demo-workflow-stage[data-demo-layout="interrupt"] .workflow-graph-stage {
|
||||
grid-column: 2;
|
||||
grid-row: 1;
|
||||
}
|
||||
|
||||
.interrupt-contract-preview[data-hero="true"] {
|
||||
border-color: color-mix(in oklch, var(--accent-amber) 72%, white);
|
||||
background:
|
||||
linear-gradient(180deg, color-mix(in oklch, var(--accent-amber) 14%, transparent), transparent 48%),
|
||||
color-mix(in oklch, var(--stage-surface) 92%, black);
|
||||
}
|
||||
|
||||
.interrupt-contract-preview[data-hero="true"] h2 {
|
||||
font-size: clamp(1.45rem, 2.3vw, 2rem);
|
||||
}
|
||||
|
||||
@media (max-width: 1050px) {
|
||||
.demo-workflow-stage[data-demo-layout="approval"] .demo-workflow-stage__graph,
|
||||
.demo-workflow-stage[data-demo-layout="interrupt"] .demo-workflow-stage__graph {
|
||||
grid-template-columns: minmax(17rem, 40%) minmax(0, 1fr);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
If CSS order conflicts with the existing `:has(.interrupt-contract-preview)` rule, place these rules after the `:has` rule so layout-specific selectors win.
|
||||
|
||||
- [ ] **Step 7: Run focused tests**
|
||||
|
||||
Run:
|
||||
|
||||
```bash
|
||||
pnpm --dir web --filter @lda/console test -- src/presentation/DemoWorkflowScene.test.tsx
|
||||
```
|
||||
|
||||
Expected: PASS.
|
||||
|
||||
- [ ] **Step 8: Commit Task 3**
|
||||
|
||||
```bash
|
||||
git add web/apps/console/src/presentation/DemoWorkflowScene.tsx web/apps/console/src/presentation/InterruptContractPreview.tsx web/apps/console/src/presentation/styles/demo-workflow.css web/apps/console/src/presentation/DemoWorkflowScene.test.tsx
|
||||
git commit -m "feat: make interrupt approval the demo hero"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Task 4: Presenter-Only Discussion Hint Treatment
|
||||
|
||||
**Files:**
|
||||
- Modify: `web/apps/console/src/presentation/DiscussionPanel.tsx`
|
||||
- Modify: `web/apps/console/src/presentation/presentation.css`
|
||||
- Test: `web/apps/console/src/presentation/DiscussionPanel.test.tsx`
|
||||
|
||||
**Interfaces:**
|
||||
- Consumes: `speakerHint?: string` on discussion branches.
|
||||
- Produces: presenter note element:
|
||||
- `aria-label="presenter note"`
|
||||
- class `discussion-panel__presenter-note`
|
||||
|
||||
- [ ] **Step 1: Add failing test**
|
||||
|
||||
In `web/apps/console/src/presentation/DiscussionPanel.test.tsx`, add:
|
||||
|
||||
```tsx
|
||||
it("renders speaker hints as presenter notes instead of answer content", () => {
|
||||
render(<DiscussionPanel branchId="where-is-ai-agent" onClose={onClose} />);
|
||||
|
||||
const note = screen.getByLabelText("presenter note");
|
||||
expect(note).toHaveTextContent(/Answer directly first/i);
|
||||
expect(note).toHaveTextContent(/Presenter note/i);
|
||||
});
|
||||
```
|
||||
|
||||
- [ ] **Step 2: Run failing test**
|
||||
|
||||
Run:
|
||||
|
||||
```bash
|
||||
pnpm --dir web --filter @lda/console test -- src/presentation/DiscussionPanel.test.tsx
|
||||
```
|
||||
|
||||
Expected: FAIL if the current element uses `discussion-panel__speaker-hint` and lacks the accessible label.
|
||||
|
||||
- [ ] **Step 3: Rename the rendered hint**
|
||||
|
||||
In `DiscussionPanel.tsx`, replace:
|
||||
|
||||
```tsx
|
||||
<p className="discussion-panel__speaker-hint">
|
||||
<span>Speaker hint</span>
|
||||
{branch.speakerHint}
|
||||
</p>
|
||||
```
|
||||
|
||||
with:
|
||||
|
||||
```tsx
|
||||
<p className="discussion-panel__presenter-note" aria-label="presenter note">
|
||||
<span>Presenter note</span>
|
||||
{branch.speakerHint}
|
||||
</p>
|
||||
```
|
||||
|
||||
- [ ] **Step 4: Update CSS**
|
||||
|
||||
In `presentation.css`, replace `.discussion-panel__speaker-hint` selectors with `.discussion-panel__presenter-note` and keep the old class only if needed for a transition. Desired treatment:
|
||||
|
||||
```css
|
||||
.discussion-panel__presenter-note {
|
||||
margin: 0;
|
||||
padding: 0.55rem 0.65rem;
|
||||
border: 1px dashed color-mix(in oklch, var(--stage-line) 70%, transparent);
|
||||
border-radius: 0.55rem;
|
||||
background: color-mix(in oklch, var(--stage-surface) 72%, transparent);
|
||||
color: var(--text-muted);
|
||||
font-size: 0.82rem;
|
||||
line-height: 1.35;
|
||||
}
|
||||
|
||||
.discussion-panel__presenter-note span {
|
||||
display: block;
|
||||
margin-bottom: 0.2rem;
|
||||
color: var(--accent-amber);
|
||||
font: 750 0.64rem/1 var(--font-mono, monospace);
|
||||
letter-spacing: 0.08em;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
```
|
||||
|
||||
- [ ] **Step 5: Run focused tests**
|
||||
|
||||
Run:
|
||||
|
||||
```bash
|
||||
pnpm --dir web --filter @lda/console test -- src/presentation/DiscussionPanel.test.tsx
|
||||
```
|
||||
|
||||
Expected: PASS.
|
||||
|
||||
- [ ] **Step 6: Commit Task 4**
|
||||
|
||||
```bash
|
||||
git add web/apps/console/src/presentation/DiscussionPanel.tsx web/apps/console/src/presentation/DiscussionPanel.test.tsx web/apps/console/src/presentation/presentation.css
|
||||
git commit -m "feat: demote discussion speaker hints"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Task 5: Screenshot Smoke And Docs
|
||||
|
||||
**Files:**
|
||||
- Modify: `docs/current_roadmap.md`
|
||||
- Move: `docs/superpowers/plans/2026-07-08-defense-presentation-visual-pass.md` to `docs/historical/superpowers/plans/2026-07-08-defense-presentation-visual-pass.md`
|
||||
|
||||
**Interfaces:**
|
||||
- Consumes: Tasks 1-4.
|
||||
- Produces: verified visual pass and live roadmap link.
|
||||
|
||||
- [ ] **Step 1: Run focused tests**
|
||||
|
||||
Run:
|
||||
|
||||
```bash
|
||||
pnpm --dir web --filter @lda/console test -- src/presentation/scenes/ArchitectureScene.test.tsx src/presentation/figures/InteractiveFigure.test.tsx src/presentation/SceneBody.test.tsx src/presentation/DemoWorkflowScene.test.tsx src/presentation/DiscussionPanel.test.tsx
|
||||
```
|
||||
|
||||
Expected: PASS.
|
||||
|
||||
- [ ] **Step 2: Run build checks**
|
||||
|
||||
Run:
|
||||
|
||||
```bash
|
||||
pnpm --dir web --filter @lda/console typecheck
|
||||
pnpm --dir web --filter @lda/console build
|
||||
git diff --check
|
||||
```
|
||||
|
||||
Expected:
|
||||
- Typecheck passes.
|
||||
- Build succeeds. Existing chunk-size warning is acceptable if unchanged.
|
||||
- No whitespace errors.
|
||||
|
||||
- [ ] **Step 3: Run screenshot smoke**
|
||||
|
||||
Start dev server if not already running:
|
||||
|
||||
```bash
|
||||
pnpm --dir web dev
|
||||
```
|
||||
|
||||
In another terminal, create screenshot directory:
|
||||
|
||||
```powershell
|
||||
New-Item -ItemType Directory -Force web/apps/console/.visual-smoke | Out-Null
|
||||
```
|
||||
|
||||
Capture `1280x720` screenshots:
|
||||
|
||||
```powershell
|
||||
pnpx playwright screenshot --viewport-size=1280,720 "http://127.0.0.1:5173/present#scene/architecture/client" web/apps/console/.visual-smoke/scene-06-client-1280x720.png
|
||||
pnpx playwright screenshot --viewport-size=1280,720 "http://127.0.0.1:5173/present#scene/architecture/runtime/focus/runtime-providers" web/apps/console/.visual-smoke/scene-06-runtime-1280x720.png
|
||||
pnpx playwright screenshot --viewport-size=1280,720 "http://127.0.0.1:5173/present#scene/authoring/discover" web/apps/console/.visual-smoke/scene-07-authoring-1280x720.png
|
||||
pnpx playwright screenshot --viewport-size=1280,720 "http://127.0.0.1:5173/present#scene/interrupt-evidence/approval" web/apps/console/.visual-smoke/scene-10-approval-1280x720.png
|
||||
```
|
||||
|
||||
Capture `1024x768` architecture fallback:
|
||||
|
||||
```powershell
|
||||
pnpx playwright screenshot --viewport-size=1024,768 "http://127.0.0.1:5173/present#scene/architecture/runtime/focus/runtime-providers" web/apps/console/.visual-smoke/scene-06-runtime-1024x768.png
|
||||
```
|
||||
|
||||
Inspect screenshots manually. Acceptance:
|
||||
- No slide-level scrollbars.
|
||||
- Scene 6 figure is larger and horizontally navigable if needed.
|
||||
- Scene 7 authoring loop is readable and not generic tiny cards.
|
||||
- Scene 10 approval makes the interrupt contract visually primary.
|
||||
- Speaker hints in discussion modal look like presenter notes, not answer body.
|
||||
|
||||
Do not commit `.visual-smoke` screenshots unless the repo already tracks visual snapshots. If needed, add `web/apps/console/.visual-smoke/` to `.gitignore`.
|
||||
|
||||
- [ ] **Step 4: Update roadmap**
|
||||
|
||||
In `docs/current_roadmap.md`, under `Presentation wishlist / defense readiness`, replace:
|
||||
|
||||
```md
|
||||
- Visual pass for Scenes 6, 7, and 10: fix spacing, graph scale, captions,
|
||||
evidence receipt placement, and remove remaining generic AI-slide styling.
|
||||
```
|
||||
|
||||
with:
|
||||
|
||||
```md
|
||||
- Completed: visual pass for Scenes 6, 7, and 10 fixed architecture figure
|
||||
scale, authoring-loop clarity, interrupt/evidence emphasis, and presenter-note
|
||||
treatment. Implementation:
|
||||
[`defense presentation visual pass`](historical/superpowers/plans/2026-07-08-defense-presentation-visual-pass.md).
|
||||
```
|
||||
|
||||
- [ ] **Step 5: Archive plan**
|
||||
|
||||
Run:
|
||||
|
||||
```bash
|
||||
git mv docs/superpowers/plans/2026-07-08-defense-presentation-visual-pass.md docs/historical/superpowers/plans/2026-07-08-defense-presentation-visual-pass.md
|
||||
```
|
||||
|
||||
If the plan file is not tracked yet, use PowerShell:
|
||||
|
||||
```powershell
|
||||
Move-Item docs/superpowers/plans/2026-07-08-defense-presentation-visual-pass.md docs/historical/superpowers/plans/2026-07-08-defense-presentation-visual-pass.md
|
||||
```
|
||||
|
||||
- [ ] **Step 6: Commit Task 5**
|
||||
|
||||
```bash
|
||||
git add docs/current_roadmap.md docs/historical/superpowers/plans/2026-07-08-defense-presentation-visual-pass.md
|
||||
git commit -m "docs: record defense presentation visual pass"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Final Verification
|
||||
|
||||
- [ ] Run full web tests:
|
||||
|
||||
```bash
|
||||
pnpm --dir web test
|
||||
```
|
||||
|
||||
Expected: PASS.
|
||||
|
||||
- [ ] Run full web typecheck:
|
||||
|
||||
```bash
|
||||
pnpm --dir web typecheck
|
||||
```
|
||||
|
||||
Expected: PASS.
|
||||
|
||||
- [ ] Run full web build:
|
||||
|
||||
```bash
|
||||
pnpm --dir web build
|
||||
```
|
||||
|
||||
Expected: PASS; unchanged chunk-size warning is acceptable.
|
||||
|
||||
- [ ] Check git status:
|
||||
|
||||
```bash
|
||||
git status --short
|
||||
```
|
||||
|
||||
Expected: only intentional files are changed or clean after commits.
|
||||
|
||||
---
|
||||
|
||||
## Review Checklist
|
||||
|
||||
- Scene 6 is bigger and usable, not just scaled down to fit.
|
||||
- Scene 7 communicates a public-surface authoring loop.
|
||||
- Scene 10 makes typed interrupt approval the hero.
|
||||
- Q&A speaker hints are not visually equivalent to audience answers.
|
||||
- No `/console` styling changed.
|
||||
- No new dependency added.
|
||||
- No screenshot artifacts committed unless intentionally tracked.
|
||||
- No global overflow hacks added.
|
||||
@@ -0,0 +1,145 @@
|
||||
# Defense Presentation Visual Pass Design
|
||||
|
||||
## Purpose
|
||||
|
||||
This slice improves the weakest visible parts of `/present` without changing the
|
||||
storyboard, transport, demo replay, or chat system. The target is a defense
|
||||
screen at `1280x720` with fallback usability at `1024x768`.
|
||||
|
||||
The presentation should look like a precise product walkthrough, not a generic
|
||||
AI slide deck. The workflow graph and evidence surfaces remain the proof. Chat
|
||||
and Q&A remain supporting surfaces.
|
||||
|
||||
## Scope
|
||||
|
||||
In scope:
|
||||
|
||||
- Scene 6 architecture figure scale, horizontal fit, and focused hierarchy.
|
||||
- Scene 7 authoring visual treatment.
|
||||
- Scene 10 interrupt and evidence treatment.
|
||||
- Discussion panel treatment for speaker-only hints.
|
||||
- Screenshot smoke checks at `1280x720` and `1024x768`.
|
||||
|
||||
Out of scope:
|
||||
|
||||
- AI/chat component replacement.
|
||||
- Schema form surface.
|
||||
- Guided run beat gates.
|
||||
- Presenter companion.
|
||||
- Full theme redesign.
|
||||
- Rewriting the 12-scene storyboard.
|
||||
|
||||
## Design Direction
|
||||
|
||||
Use the existing editorial canvas and presentation tokens. Do not introduce a
|
||||
third theme or another global palette. The pass should make the current
|
||||
presentation calmer and more intentional:
|
||||
|
||||
- One dominant visual per beat.
|
||||
- Bigger diagrams, fewer side panels.
|
||||
- Evidence as a receipt or deliberate inspector, not a random drawer.
|
||||
- Speaker-only notes should not look like audience content.
|
||||
- Motion should clarify state changes; avoid blur/zoom on the same object when
|
||||
it makes the transition feel unstable.
|
||||
|
||||
## Scene 6: Architecture
|
||||
|
||||
The architecture scene should feel like a navigable technical map. The current
|
||||
figure is correct but visually too small and cramped.
|
||||
|
||||
Required behavior:
|
||||
|
||||
- The figure owns most of the scene height.
|
||||
- Horizontal flow figures can overflow horizontally inside the figure frame
|
||||
rather than shrinking until unreadable.
|
||||
- Breadcrumbs stay visible and compact.
|
||||
- Active/current node is visibly dominant without making inactive nodes vanish.
|
||||
- Edge labels remain readable at `1280x720`.
|
||||
- `1024x768` keeps the figure usable through horizontal scroll, not through
|
||||
excessive text shrinking.
|
||||
|
||||
Implementation direction:
|
||||
|
||||
- Keep `InteractiveFigure` reusable.
|
||||
- Add a presentation-scale variant, for example `size="stage"` or refine
|
||||
existing `size="wide"`.
|
||||
- Add explicit tests for the size attribute and scrollable canvas class/shape.
|
||||
- Avoid changing figure catalog facts unless a label is factually wrong.
|
||||
|
||||
## Scene 7: Authoring
|
||||
|
||||
The authoring scene should explain the product UX loop, not display generic step
|
||||
cards.
|
||||
|
||||
Required behavior:
|
||||
|
||||
- Show a single loop: discover capability -> author draft -> validate/diagnose
|
||||
-> repair -> compile/save.
|
||||
- The active beat emphasizes one loop stage.
|
||||
- The visual implies agents/humans use public surfaces (`wf schema`, `wf draft`,
|
||||
diagnostics), without putting too much command text on the slide.
|
||||
- The scene remains readable at `720p`.
|
||||
|
||||
Implementation direction:
|
||||
|
||||
- Extract an `AuthoringLoopScene` or focused helper from `SceneBody.tsx` if the
|
||||
inline scene grows.
|
||||
- Use existing storyboard beat IDs: `discover`, `author`, `diagnose`, `repair`.
|
||||
- If a fifth compile/save stage is shown, it can be present as a terminal stage
|
||||
but does not need its own beat.
|
||||
|
||||
## Scene 10: Interrupt And Evidence
|
||||
|
||||
The interrupt scene should make typed human approval feel like the main point.
|
||||
The graph should provide context, not compete with the contract.
|
||||
|
||||
Required behavior:
|
||||
|
||||
- The approval beat shows the interrupt contract as the hero element.
|
||||
- The workflow graph remains visible enough to show where the interrupt sits.
|
||||
- The receipt/evidence affordance is visible but secondary.
|
||||
- The `submitted / cancelled` outcome language remains canonical.
|
||||
- The trace beat can emphasize evidence after resume, but should not make the
|
||||
approval contract look like a random side card.
|
||||
|
||||
Implementation direction:
|
||||
|
||||
- Add beat-specific layout states in `DemoWorkflowScene` via small named helpers
|
||||
instead of more anonymous booleans.
|
||||
- Consider a `demo-workflow-stage--approval` or `data-layout="approval"` state.
|
||||
- Keep `OperationBlock`, `WorkflowGraphStage`, and `InterruptContractPreview`
|
||||
as existing components; restyle/compose rather than rewrite.
|
||||
|
||||
## Discussion Panel Speaker Hints
|
||||
|
||||
`speakerHint` is useful for rehearsal but should not read as audience content.
|
||||
For now, keep it inside the modal but visually demote it:
|
||||
|
||||
- Use a small presenter-note treatment.
|
||||
- Label it as presenter note, not primary content.
|
||||
- Keep it after the answer body.
|
||||
- Make it easy to hide later if a presenter mode split lands.
|
||||
|
||||
## Motion
|
||||
|
||||
Use restrained motion only:
|
||||
|
||||
- 150-250ms transitions.
|
||||
- Transform/opacity are acceptable.
|
||||
- Do not combine blur and pan/zoom on the same object for core slide movement.
|
||||
- Respect existing `motionDisabled` and `prefers-reduced-motion`.
|
||||
|
||||
## Acceptance Criteria
|
||||
|
||||
- Focused tests for Scene 6, Scene 7, Scene 10, and discussion speaker hint
|
||||
treatment pass.
|
||||
- `pnpm --dir web --filter @lda/console typecheck` passes.
|
||||
- `pnpm --dir web --filter @lda/console build` succeeds.
|
||||
- Screenshot smoke verifies:
|
||||
- `#scene/architecture/client`
|
||||
- `#scene/architecture/runtime/focus/runtime-providers`
|
||||
- `#scene/authoring/discover`
|
||||
- `#scene/interrupt-evidence/approval`
|
||||
at `1280x720`.
|
||||
- A second smoke at `1024x768` verifies Scene 6 does not overflow the viewport
|
||||
and the figure remains horizontally navigable.
|
||||
Reference in New Issue
Block a user