docs: define console product design contract

This commit is contained in:
lda
2026-07-03 07:12:52 +07:00 Verified
parent 241897eaa3
commit 47ac422965
8 changed files with 419 additions and 7 deletions
+1 -1
View File
@@ -73,7 +73,7 @@ Implementation order:
Design: Design:
[`React presentation mode`](superpowers/specs/2026-07-03-react-presentation-mode-design.md). [`React presentation mode`](superpowers/specs/2026-07-03-react-presentation-mode-design.md).
Implementation: Implementation:
[`React presentation mode plan`](superpowers/plans/2026-07-03-react-presentation-mode.md). [`React presentation mode plan`](historical/superpowers/plans/2026-07-03-react-presentation-mode.md).
8. Add a constrained demo agent that invokes one prepared recipe macro. 8. Add a constrained demo agent that invokes one prepared recipe macro.
9. Add a static slide/appendix shell only after presentation mode is clear. 9. Add a static slide/appendix shell only after presentation mode is clear.
Astro remains an option, not the default next surface. Astro remains an option, not the default next surface.
+17
View File
@@ -64,6 +64,23 @@ The browser communicates with Hono at `/api/connect` and `/api/rpc`. Hono
validates targets against loopback policy, executes typed JSON-RPC calls validates targets against loopback policy, executes typed JSON-RPC calls
through Effect, and returns plain JSON DTOs to the browser. through Effect, and returns plain JSON DTOs to the browser.
## Product And Design Context
The console app carries two design-context files:
- [`apps/console/PRODUCT.md`](apps/console/PRODUCT.md) captures the strategic
product contract: users, purpose, personality, anti-references, and design
principles.
- [`apps/console/DESIGN.md`](apps/console/DESIGN.md) captures the current visual
system: tokens, typography, components, elevation, and do/don't rules.
This structure comes from the local Impeccable design workflow. `PRODUCT.md`
uses its product-register format, and `DESIGN.md` follows the DESIGN.md
convention: YAML frontmatter for machine-readable tokens, followed by six fixed
sections (`Overview`, `Colors`, `Typography`, `Elevation`, `Components`, and
`Do's and Don'ts`). Future UI work should read these files before changing
visual direction.
## Lifecycle Explorer ## Lifecycle Explorer
After connecting, the console displays the lifecycle explorer with three After connecting, the console displays the lifecycle explorer with three
+270
View File
@@ -0,0 +1,270 @@
---
name: "lda.chat Workflow Console"
description: "A product console and defense presentation surface for inspecting workflow lifecycle evidence."
colors:
paper: "#faf8f5"
ink: "#1a1a1a"
slate: "#5a5a5a"
surface: "#f0ede8"
border: "#d4d0cb"
signal-green: "#2d8a4e"
amber: "#b8860b"
red: "#c0392b"
presentation-bg: "oklch(0.12 0.025 250)"
presentation-panel: "oklch(0.18 0.025 250)"
presentation-line: "oklch(0.36 0.04 250)"
presentation-accent: "oklch(0.7 0.16 195)"
presentation-interrupt: "oklch(0.76 0.18 70)"
typography:
display:
fontFamily: "Barlow Condensed, sans-serif"
fontWeight: 700
lineHeight: 1
letterSpacing: "0.02em"
body:
fontFamily: "Source Sans 3, sans-serif"
fontWeight: 400
lineHeight: 1.5
mono:
fontFamily: "IBM Plex Mono, monospace"
fontWeight: 400
lineHeight: 1.4
rounded:
control: "3px"
surface: "4px"
graph-node: "6px"
presentation-panel: "0.85rem"
spacing:
xs: "0.35rem"
sm: "0.5rem"
md: "0.75rem"
lg: "1rem"
xl: "1.25rem"
components:
button-primary:
backgroundColor: "{colors.ink}"
textColor: "{colors.paper}"
rounded: "{rounded.control}"
padding: "0.5rem 1.25rem"
input-text:
backgroundColor: "#ffffff"
textColor: "{colors.ink}"
rounded: "{rounded.control}"
padding: "0.5rem 0.75rem"
console-surface:
backgroundColor: "{colors.surface}"
textColor: "{colors.ink}"
rounded: "{rounded.surface}"
padding: "1rem"
presentation-panel:
backgroundColor: "{colors.presentation-panel}"
textColor: "oklch(0.96 0.01 250)"
rounded: "{rounded.presentation-panel}"
padding: "1rem"
---
# Design System: lda.chat Workflow Console
## 1. Overview
**Creative North Star: "The Evidence Theater"**
The console has two related visual modes. `/console` is a product tool: compact,
legible, and deliberately familiar. It should help an operator connect to a
workflow server, inspect records, and verify evidence without decoding raw JSON
first. `/present` is a defense surface: cinematic enough to keep attention, but
still grounded in real workflow operations, graph state, interrupt contracts,
and trace evidence.
This design system is transitional. The current implementation already has a
usable technical-console vocabulary and a separate dark presentation vocabulary,
but those token sets are not yet fully unified. Future visual work should
normalize the shared primitives before adding more styling.
The chat surface is not the core identity. Chat may frame a prepared operation
or narrate a workflow, but it must not become a hand-rolled generic chatbot. If
chat grows, use a mature assistant UI pattern and adapt it to the workflow
substrate.
**Key Characteristics:**
- Product console: readable, restrained, technical, evidence-first.
- Presentation route: cinematic, large-type, staged, graph-forward.
- Visual proof beats decorative flourish.
- Familiar product controls beat invented affordances.
- Chat stays professional and secondary.
## 2. Colors
The current palette is split between paper-console neutrals and a dark
presentation stage. Keep that split for now, but do not add a third unrelated
palette.
### Primary
- **Ink Black**: the console action and text anchor. Use for primary buttons,
code panels, and high-emphasis text.
- **Presentation Cyan**: the presentation accent. Use for active beat rail
items, selected graph nodes, and staged highlight moments only.
### Secondary
- **Signal Green**: success, connection, loaded/current status, and positive
validation.
- **Interrupt Amber**: typed human boundary, review state, and interrupt graph
emphasis.
- **Failure Red**: destructive or failed states only.
### Neutral
- **Warm Paper**: the current `/console` page background.
- **Stone Surface**: console sections, panels, and stable content blocks.
- **Soft Border**: console dividers, panel edges, and table rules.
- **Slate Text**: secondary labels, metadata, durations, and muted state text.
- **Night Stage**: the `/present` background and presentation canvas.
### Named Rules
**The One Evidence Accent Rule.** On any given screen, only one non-neutral color
should carry the user's attention. Green, amber, cyan, and red must not compete.
**The Palette Debt Rule.** Presentation OKLCH colors and console hex colors are
allowed today because they reflect existing code. New work should consolidate
them into named tokens instead of adding more one-off values.
## 3. Typography
**Display Font:** Barlow Condensed, sans-serif
**Body Font:** Source Sans 3, sans-serif
**Label/Mono Font:** IBM Plex Mono, monospace
**Character:** Condensed headings make the product feel operational and
technical without becoming terminal-only. Source Sans carries readable body
content. IBM Plex Mono is reserved for paths, commands, JSON, IDs, and evidence.
### Hierarchy
- **Display** (700, large route/beat headings, tight line-height): presentation
stage titles and major defense beats.
- **Headline** (700, uppercase, `1.25rem` to `1.75rem`): console section titles
and lifecycle panels.
- **Title** (600-700, uppercase, compact): card, graph node, and table group
labels.
- **Body** (400, `1rem`, `1.5` line-height): explanatory copy, status text, and
panel descriptions. Keep prose under 75ch where possible.
- **Label** (600, small, `0.05em` tracking): form labels, table headers, and
compact metadata.
- **Mono** (`0.74rem` to `0.85rem`): commands, IDs, JSON, paths, run IDs, and
equivalent CLI snippets.
### Named Rules
**The Mono Is Evidence Rule.** Monospace means something inspectable: a command,
path, ID, JSON body, or protocol field. Do not use mono as decoration.
**The Chat Restraint Rule.** Chat labels and messages should use the product
type system. Do not introduce playful display type inside the chat frame.
## 4. Elevation
The console is mostly flat and uses tonal layers, borders, and code panels for
depth. The workflow graph has a small mechanical shadow on nodes. Presentation
mode uses overlays and fixed drawers rather than heavy shadows.
### Shadow Vocabulary
- **Graph Node Lift** (`0 3px 0 rgba(26, 26, 26, 0.12)`): use only for graph
nodes where the block needs a tangible, diagram-like presence.
- **Overlay Depth**: currently expressed through fixed positioning, darker
surface color, and border contrast rather than a drop shadow.
### Named Rules
**The Flat By Default Rule.** Surfaces are separated with tone and borders.
Shadows are reserved for graph nodes or true overlays.
## 5. Components
### Buttons
- **Shape:** compact technical controls (`3px` radius).
- **Primary:** ink background, paper text, uppercase Barlow Condensed, `0.5rem`
by `1.25rem` padding.
- **Hover / Focus:** hover lightens toward slate; focus must remain explicit and
visible. Do not rely on color alone.
- **Presentation buttons:** may use darker panels and cyan selected states, but
should keep predictable button affordances.
### Chips
- **Style:** the current system does not have a mature chip primitive. Use
simple bordered labels or buttons until a component library lands.
- **State:** selected/current states should use the single active accent for the
screen.
### Cards / Containers
- **Corner Style:** console panels use restrained corners (`4px`). Presentation
panels use larger stage corners (`0.85rem`).
- **Background:** console panels use Stone Surface; presentation panels use dark
Night Stage layers.
- **Shadow Strategy:** flat by default; see Elevation.
- **Border:** one-pixel structural borders are the default separator.
- **Internal Padding:** `1rem` for normal panels; tighter `0.6rem` to `0.75rem`
for list and timeline items.
### Inputs / Fields
- **Style:** white field, ink text, mono type for URLs and command-like values,
`3px` radius, one-pixel border.
- **Focus:** explicit green outline, not subtle glow.
- **Error / Disabled:** disabled controls lower opacity and preserve shape.
Errors must include text; red alone is not enough.
### Navigation
- **Console navigation:** use familiar tabs/buttons for focus modes. Active
state must be visible through class, text, or control state.
- **Presentation navigation:** beat rail is horizontal, keyboard-addressable,
and should remain usable at 720p. Hash links identify beats.
### Workflow Graph
Workflow graph nodes are the signature component. They should feel like product
evidence, not decorative bubbles. Nodes use uppercase headings, mono references,
structural borders, and direct click targets. Interrupt nodes may use amber
border emphasis because the typed human boundary is conceptually important.
### Operation Block
Operation blocks show the tool call as a product event: equivalent CLI, raw
response, and interpreted response. The block should make the workflow substrate
legible without hiding raw evidence.
## 6. Do's and Don'ts
### Do:
- **Do** make the workflow graph, operation block, evidence drawer, lifecycle
explorer, typed interrupt/resume panel, and trace the main visual surfaces.
- **Do** keep `/console` familiar and product-like: readable tables, standard
buttons, clear labels, predictable focus states.
- **Do** let `/present` be cinematic with staged panels, larger text, and graph
zooms, as long as each moment maps back to real evidence.
- **Do** use mature component and chat primitives when the interaction is
standard.
- **Do** respect reduced motion and keep keyboard navigation reliable.
- **Do** use big text and few items during presentation beats.
### Don't:
- **Don't** make the console look like a generic AI chatbot.
- **Don't** hand-roll chat components as the foundation for the product.
- **Don't** let chat dominate presentation mode; it can slide in, collapse, or
move off-screen.
- **Don't** use funny display fonts, doodle shapes, novelty bubbles, or unclear
controls in the professional chat surface.
- **Don't** add decorative motion that does not explain state or guide attention.
- **Don't** overclaim a bundled autonomous AI-agent brain through UI copy.
- **Don't** add more one-off colors before normalizing the console and
presentation token sets.
+107
View File
@@ -0,0 +1,107 @@
# Product
## Register
product
## Users
The primary users are the thesis author during defense preparation, demo
operators during a live presentation, and technical reviewers who need to see
how `lda.chat` works without reading raw workflow JSON. Secondary users are
future developers and agents using the console to inspect workflow lifecycle
records, source capabilities, runs, traces, and interrupt/resume boundaries.
Users are usually under time pressure: a defense, demo, review session, or
debugging pass. The interface should help them move quickly from a high-level
story to concrete evidence without forcing them to decode raw protocol payloads
first.
## Product Purpose
The console is the product-facing visual surface for `lda.chat`. It exists to
show that the workflow substrate is inspectable, operable, and explainable: an
operator can connect to a workflow server, inspect lifecycle records, view graph
structure, start or resume prepared workflows, and inspect raw/evaluated
evidence.
Presentation mode is not a generic slide deck. It is a cinematic product demo
surface for explaining the thesis: external planners propose actions, the
workflow substrate owns typed lifecycle state and deterministic execution, and
the resulting runs remain auditable through traces and evidence records.
Chat is a framing device, not the core product. It may introduce or narrate a
prepared workflow, but the main surfaces are the workflow graph, operation
blocks, evidence drawer, lifecycle explorer, typed interrupt/resume panel, and
run trace.
Success means a viewer can answer three questions quickly:
- What does `lda.chat` own that an external AI agent does not own?
- How does a workflow move from authoring to deployment to execution?
- Where is the proof that a specific run happened and can be inspected?
## Brand Personality
The product should feel precise, competent, and cinematic.
The console should feel like a real technical product: calm, readable, and
trustworthy. The presentation route may be more theatrical: large text, clear
beats, animated panels, graph zooms, and staged evidence reveals. The showmanship
must support understanding, not cover missing substance.
The desired feel is closer to a polished developer tool plus a live product
walkthrough than a generic AI chat app.
## Anti-references
Do not make the console look like a generic AI chatbot. Avoid hand-rolled chat
components as the design foundation; if chat becomes important, adopt or adapt a
mature open-source assistant UI pattern instead of inventing message bubbles,
tool-call affordances, and composer behavior from scratch.
Do not let chat dominate presentation mode. Chat can slide in, collapse, or move
off-screen, but the workflow graph and evidence views carry the thesis.
Do not use playful or decorative styling inside the professional chat surface:
no funny display fonts, doodle shapes, novelty bubbles, or unclear controls.
Do not build a sleep-inducing slide deck that only lists architecture claims.
The presentation should tell a story through staged transitions and concrete
workflow evidence.
Do not overclaim the presence of a bundled autonomous AI-agent brain. The product
surface may demonstrate a prepared or scripted agent-like interaction, but the
core contribution remains the workflow substrate.
## Design Principles
1. **Evidence first.** Every impressive visual should have a path back to a
workflow record, operation, run trace, or captured protocol response.
2. **Graph over transcript.** Use chat sparingly; prefer graph, lifecycle,
operation, and evidence views for explaining the system.
3. **Cinematic, not cluttered.** Use large text, few items, and staged motion for
presentation mode. Avoid dense panels unless the viewer deliberately opens
them.
4. **Professional where users operate.** The `/console` surface should prioritize
familiar product patterns, predictable components, readable tables, and clear
failure states.
5. **Agent-facing, not agent-theater.** Show how external agents or scripted demo
flows operate `wf`; do not imply that the UI itself proves a new planning
algorithm.
6. **Adopt standard primitives.** Prefer proven component libraries and mature
chat UI patterns over custom controls when the interaction is standard.
## Accessibility & Inclusion
Target WCAG 2.2 AA for normal product UI. Presentation mode should remain usable
on a 720p projector or screen, with large text, high contrast, and no dependency
on color alone to distinguish models, stages, or outcomes.
Motion should respect `prefers-reduced-motion`. Presentation animations may be
cinematic, but they must not block comprehension, hide important content, or make
manual navigation difficult.
Keyboard navigation matters for defense use: arrow keys should move beats,
Escape should close overlays, and clickable graph nodes or drawers should remain
reachable through standard focus behavior.
@@ -19,8 +19,8 @@ export const NodeSpotlight = ({ nodeId, close }: NodeSpotlightProps) => {
const node = presentationNodes.find((candidate) => candidate.id === nodeId); const node = presentationNodes.find((candidate) => candidate.id === nodeId);
if (!node) return null; if (!node) return null;
return ( return (
<aside className="node-spotlight" role="dialog" aria-modal="true" aria-label={node.label}> <aside className="node-spotlight" role="dialog" aria-modal="true" aria-label={node.label}>
<button type="button" onClick={close}>Close</button> <button type="button" onClick={close}>Close</button>
<p>NodeUse</p> <p>NodeUse</p>
<h2>{node.label}</h2> <h2>{node.label}</h2>
@@ -45,4 +45,14 @@ describe("PresentationRoute", () => {
expect(await screen.findByText(/workflow.runs.start/i)).toBeInTheDocument(); expect(await screen.findByText(/workflow.runs.start/i)).toBeInTheDocument();
}); });
it("shows resume and trace operation blocks for later beats", async () => {
render(<PresentationRoute />);
await userEvent.click(screen.getByRole("button", { name: /resume output/i }));
expect(await screen.findByText(/workflow.runs.resume/i)).toBeInTheDocument();
await userEvent.click(screen.getByRole("button", { name: /trace evidence/i }));
expect(await screen.findByText(/workflow.runs.trace/i)).toBeInTheDocument();
});
}); });
@@ -9,6 +9,7 @@ import { StageCaption } from "./StageCaption.js";
import { WorkflowGraphStage } from "./WorkflowGraphStage.js"; import { WorkflowGraphStage } from "./WorkflowGraphStage.js";
import type { PresentationState } from "./presentation-state.js"; import type { PresentationState } from "./presentation-state.js";
import type { DemoTimelineController } from "../demo/useDemoTimeline.js"; import type { DemoTimelineController } from "../demo/useDemoTimeline.js";
import type { DemoEventStage } from "../demo/timeline/models.js";
type PresentationStageProps = { type PresentationStageProps = {
readonly state: PresentationState; readonly state: PresentationState;
@@ -21,6 +22,13 @@ type PresentationStageProps = {
readonly closeOverlay: () => void; readonly closeOverlay: () => void;
}; };
const operationStageByBeat: Partial<Record<BeatId, DemoEventStage>> = {
"tool-call-start": "run_start",
"interrupt-approval": "interrupt",
"resume-output": "run_resume",
"trace-evidence": "trace_read",
};
export const PresentationStage = ({ export const PresentationStage = ({
state, state,
demo, demo,
@@ -32,11 +40,11 @@ export const PresentationStage = ({
closeOverlay, closeOverlay,
}: PresentationStageProps) => { }: PresentationStageProps) => {
const beat = presentationBeats.find((candidate) => candidate.id === state.beat) ?? presentationBeats[0]!; const beat = presentationBeats.find((candidate) => candidate.id === state.beat) ?? presentationBeats[0]!;
const operationStage = operationStageByBeat[state.beat] ?? null;
const operationEvent = const operationEvent = operationStage
demo.state.events.find((event) => event.stage === "run_start") ?? ? demo.state.events.find((event) => event.stage === operationStage) ?? null
demo.state.events.find((event) => event.operation !== null) ?? : null;
null;
return ( return (
<div className="presentation-stage" data-beat={state.beat}> <div className="presentation-stage" data-beat={state.beat}>