# Demo Autoplay And Replay ## Status Current design contract for the next Workflow Console slice. ## Related Designs - [Workflow console, agent demo, and defense presentation](2026-07-01-workflow-console-agent-demo.md) - [Workflow console lifecycle explorer](2026-07-02-workflow-console-lifecycle-explorer.md) - [Self-describing interrupt contracts](2026-07-01-self-describing-interrupt-contracts.md) ## Purpose The prepared `lda_report_workflow` now works through the console, but its controls expose implementation actions rather than a coherent presentation sequence. The defense also needs a deterministic fallback when the live RPC server, model provider, or local environment fails. This slice turns the working demo into a lifecycle timeline with two modes: - **Live** executes public JSON-RPC operations and records normalized events. - **Replay** reads one committed, reviewed recording and renders the same event stream without contacting the workflow server. The timeline replaces the prominent refresh/start controls with a presentation flow: Start, Pause, Next, Continue at the human interrupt, and Restart. ## Scope The slice includes: - a normalized demo event envelope; - a deterministic timeline state machine; - stepwise live execution of the prepared deployment; - Play, Pause, Next, Continue, and Restart controls; - automatic pause on `issue_review`, errors, and completion; - capture of live public-RPC evidence into timeline events; - one committed canonical recording; - replay of that recording through the same view components; - visible mode and replay attribution. The slice does not include: - an LLM or demo-agent integration; - workflow authoring or deployment setup inside the UI; - Astro routes or presentation slides; - backward stepping in live mode; - automatic approval of an interrupt; - arbitrary recording import or editing; - persistence of ad-hoc recordings outside the browser session. ## Product Boundary The UI remains an ordinary client of the public JSON-RPC surface. Live mode uses the existing console backend and operation registry. Replay mode does not claim that recorded operations are live. It is labeled visibly and does not contact the workflow server. The prepared deployment remains external to the console: ```text lda_report_case_study.default ``` If it is absent in live mode, the existing missing-deployment instructions are shown. Replay mode remains available because it does not depend on the store. ## Architecture The slice introduces four focused units under `web/apps/console/src/demo/timeline/`: ```text timeline/ models.ts # Event and recording schemas/types reducer.ts # Pure timeline state machine live.ts # Live operation queue and event capture replay.ts # Canonical recording loader useDemoTimeline.ts # React adapter around live/replay controllers ``` The existing `LdaReportDemoPanel` remains responsible for report, issue, and interrupt presentation. It consumes a timeline controller instead of owning the operation sequence directly. The existing `useLdaReportDemo` operation logic is either moved into `live.ts` or reduced to a thin compatibility adapter. There must be only one owner of the live sequence. ## Demo Event Envelope Every visible lifecycle action uses one normalized event shape: ```ts type DemoEventStage = | "deployment_check" | "run_start" | "interrupt" | "run_resume" | "trace_read" | "completed" | "failed"; type DemoEvent = { readonly id: string; readonly sequence: number; readonly stage: DemoEventStage; readonly operation: string | null; readonly reason: string; readonly equivalentCli: string | null; readonly params: unknown; readonly rawResponse: unknown; readonly interpreted: unknown; readonly durationMs: number; readonly resultingIds: { readonly deploymentId: string | null; readonly runId: string | null; }; readonly recordedAt: string; }; ``` Synthetic presentation events such as `interrupt`, `completed`, and `failed` may have `operation: null`. They identify a state transition derived from a real operation result; they are not presented as agent or RPC calls. Event ids are stable inside a recording. The committed recording uses fixed ids and timestamps. Live events may derive ids from the sequence and current run id; they must not rely on `Date.now()` alone. ## Recording Contract A recording contains metadata and an ordered event list: ```ts type DemoRecording = { readonly schemaVersion: 1; readonly recordingId: string; readonly title: string; readonly createdAt: string; readonly deploymentId: "lda_report_case_study.default"; readonly source: "reviewed_live_capture"; readonly events: ReadonlyArray; }; ``` The canonical recording is committed at: ```text web/apps/console/src/demo/recordings/lda-report-success.v1.json ``` The recording must contain no machine-specific paths, credentials, session tokens, or unrelated store contents. Raw request and response fields are kept only for the prepared demo operations. The recording is decoded with Valibot before use; malformed recordings fail visibly rather than partially rendering. The reviewed recording covers: 1. deployment inspection; 2. run start; 3. `issue_review` interrupt; 4. submitted resume with at least one selected issue; 5. final trace read; 6. completed report and created issue output. ## Timeline State Machine ```mermaid stateDiagram-v2 [*] --> Ready Ready --> Running: Start presentation Running --> Paused: Pause Paused --> Running: Play Paused --> Running: Next Running --> Review: issue_review Review --> Running: Submit or cancel Running --> Completed: final event Running --> Failed: operation or decode error Paused --> Failed: malformed next event Completed --> Ready: Restart Failed --> Ready: Reset ``` Timeline state contains: ```ts type DemoTimelineState = { readonly mode: "live" | "replay"; readonly phase: | "ready" | "running" | "paused" | "review" | "completed" | "failed"; readonly events: ReadonlyArray; readonly cursor: number; readonly autoplay: boolean; readonly error: string | null; }; ``` `cursor` is an internal playback-position example, not a persisted recording field or public UI contract. An implementation may instead keep the current event id, split pending/applied event arrays, or derive a stage-to-position map. Presentation meaning always comes from the ordered event and its `stage`, not from a magic numeric value. Invariants: - `cursor` is the index of the last applied event, or `-1` before playback. - Replay may move only forward in this slice. Timeline scrubbing remains a later presentation refinement. - Live mode cannot apply an event before its RPC operation completes. - Pause stops before executing the next operation; it does not cancel an RPC already in flight. - `issue_review` always enters `review` and disables Play/Next until the user submits or cancels. - Completion and failure stop autoplay. - Restart clears transient live state and returns to `ready` without deleting persisted runs or issue-board records. ## Live Mode Live mode uses a small explicit operation queue: ```text workflow.deployments.inspect workflow.runs.start pause for issue_review workflow.runs.resume workflow.runs.trace ``` Each queue step declares: - operation name; - why the operation is performed; - parameter builder from current live context; - result decoder; - event builder; - next stage. The executor performs one step at a time. Autoplay schedules the next step only after the current event has been recorded and rendered. `Next` performs exactly one eligible step. Mutation operations are never retried automatically. The live executor records failed operation evidence as a `failed` event before entering the failed phase. Missing deployment remains a specific recoverable failure with the existing setup instructions. ## Replay Mode Replay loads and decodes the canonical recording when selected. It does not call `connectToServer()` or `callOperation()` for timeline playback. Replay timing is presentation timing, not the original network timing: - default delay between ordinary events: 900 ms; - transition to review: immediate after the interrupt event; - completion: immediate after the final event; - Pause and Next use the same reducer actions as live mode. The UI displays a persistent `Recorded replay` label. The evidence inspector may show the recording's stored request and response, but must also identify the recording id. The human approval remains interactive in replay mode. The replay recording contains the reviewed submitted branch; selecting Continue advances through that branch. The UI must not imply that replay choices alter the stored recording or create real issues. ## Controls The primary controls are: - mode selector: `Live` / `Replay`; - `Start presentation` in ready state; - `Pause` while autoplay is running; - `Play` while paused; - `Next` while paused; - `Continue` or `Cancel review` at the interrupt; - `Restart` after completion; - `Reset` after failure. `Refresh demo state` is secondary live-mode recovery. It is hidden or disabled while starting, interrupted, or resuming. It is not shown in replay mode. The UI includes a compact timeline showing stage label, status, and the current event. Raw payloads remain collapsed by default. ## Rendering The timeline drives existing views: - deployment check updates demo readiness; - run start updates run id and interrupt payload; - interrupt renders the existing issue selection form; - resume renders created issues and final markdown; - trace read renders the existing trace table; - raw event data remains available through evidence details. Live and replay must not fork into separate report or trace components. Mode differences belong in controllers and attribution labels. ## Error Handling Failures are typed at the controller boundary into these visible categories: - prepared deployment missing; - RPC transport or remote error; - malformed interpreted run result; - unexpected run state or interrupt kind; - malformed canonical recording; - trace unavailable after successful resume. The timeline stops on every failure. It shows the failed operation, reason, equivalent CLI when available, and raw evidence. It does not silently switch from live mode to replay. The operator may choose replay explicitly. ## Testing Unit tests cover: - reducer transitions and invariants; - Play, Pause, Next, Restart, and Reset; - automatic pause at review/completion/failure; - recording schema decoding; - stable ordering and ids; - replay performs no RPC calls; - live Next performs one operation; - live autoplay never crosses the interrupt; - mutation calls are not retried; - malformed recording and malformed RPC result failures. Component tests cover: - mode attribution; - control availability by phase; - timeline event rendering; - interactive live interrupt; - interactive replay interrupt without real mutation; - completed markdown, issue, and trace rendering; - missing deployment instructions in live mode. Live browser smoke covers: 1. run the console against `examples/lda_report_workflow/wf.config.json`; 2. start live presentation; 3. confirm autoplay stops at `issue_review`; 4. select one issue and continue; 5. confirm completion, issue output, trace, and evidence; 6. restart in replay mode with the RPC server unavailable; 7. confirm the same visible story completes with a replay label. ## Success Criteria The slice is complete when: 1. one primary control starts the prepared live story; 2. Play, Pause, and Next deterministically control progress; 3. autoplay never approves or crosses the human interrupt; 4. live operations produce normalized timeline events with evidence; 5. one reviewed recording is committed and schema-validated; 6. replay works with the RPC server unavailable; 7. live and replay use the same report, issue, trace, and evidence components; 8. mode attribution prevents recorded behavior from appearing live; 9. the complete sequence can be demonstrated in under three minutes.