docs: plan presentation live replay activation

This commit is contained in:
lda
2026-07-12 10:44:05 +07:00 Verified
parent 95da886185
commit 1189c21ef7
2 changed files with 524 additions and 0 deletions
@@ -0,0 +1,406 @@
# Presentation Live/Replay Activation 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:** Restore an explicit Scene 10 live/replay launch surface and make the existing workflow RPC timeline reliable without changing authoring replay or the broader storyboard.
**Architecture:** Keep `callOperation` as the only browser operation path and keep `useDemoTimeline` as the only live/replay execution owner. Add a small target-status controller for health retry, expose an explicit run-mode method on `TimelineAgentController`, and render a scene-owned launch control on Scene 10's operation beat because Scenes 1012 hide chat. Direct deep links continue to prime replay; only an explicit launch starts live or replay.
**Tech Stack:** React 19, TypeScript, Valibot-backed RPC DTOs, Vitest/Testing Library, Vite, existing BEM/CSS presentation surfaces.
## Global Constraints
- Do not add a second transport or call `wf-rpc-server` directly from the browser.
- Preserve the existing path: browser `:5173` -> web server `:8787` -> workflow RPC `:8765/rpc`.
- Scenes 8 and 9 remain deterministic authoring replay and must not call workflow authoring RPC operations.
- Direct Scene 1012 hashes remain replay-backed until the presenter explicitly launches a mode.
- A live failure must remain visibly live/failed; replay fallback must be an explicit action.
- Do not touch the unrelated user edit in `web/apps/console/src/presentation/authoring/Scene8ChatEntry.tsx`.
- Use existing `callOperation`, `resolvePresentationTarget`, `usePresentationTargetStatus`, and `useDemoTimeline` seams rather than introducing a new store.
- Run scoped tests before broad verification; do not stage generated `.superpowers` scratch files.
---
### Task 1: Separate target health from replay playback and add retry
**Files:**
- Modify: `web/apps/console/src/presentation/presentation-target-status.ts`
- Modify: `web/apps/console/src/presentation/presentation-target-status.test.ts`
- Modify: `web/apps/console/src/presentation/usePresentationTargetStatus.ts`
- Modify: `web/apps/console/src/presentation/usePresentationTargetStatus.test.tsx`
- Modify: `web/apps/console/src/presentation/PresentationRoute.tsx`
- Modify: `web/apps/console/src/presentation/PresentationStage.tsx`
**Interfaces:**
- `usePresentationTargetStatus` must return the existing `status` plus `retryHealth: () => void` and `liveTargetReady: boolean`.
- `liveTargetReady` is true only when a configured HTTP target has a successful `workflow.health` probe. It remains true while replay is being displayed after a direct hash; replay playback must not erase target readiness.
- `retryHealth` must rerun the existing `callOperation("workflow.health", target, {})` request without changing the demo timeline mode.
- [ ] **Step 1: Write failing status tests**
Add tests that prove a healthy target remains available while replay is active and that retry causes a second health call after a failed probe. Assert the returned status and `liveTargetReady` separately; do not infer live readiness from the playback label.
```tsx
it("keeps live target readiness visible while direct replay is active", async () => {
const result = renderHook(() => usePresentationTargetStatus(
{ mode: "live", target: TARGET, source: "default" },
{ mode: "replay", phase: "paused", events: [], appliedCount: 0, autoplay: false, error: null },
));
await waitFor(() => expect(result.current.status.kind).toBe("replay"));
expect(result.current.liveTargetReady).toBe(true);
});
it("retries health without changing playback state", async () => {
mockedCallOperation
.mockResolvedValueOnce(failedHealthResponse)
.mockResolvedValueOnce(healthyResponse);
const result = renderHook(() => usePresentationTargetStatus(
{ mode: "live", target: TARGET, source: "default" },
replayState,
));
await waitFor(() => expect(result.current.status.kind).toBe("failed"));
act(() => result.current.retryHealth());
await waitFor(() => expect(result.current.liveTargetReady).toBe(true));
expect(result.current.status.kind).toBe("replay");
expect(mockedCallOperation).toHaveBeenCalledTimes(2);
});
```
- [ ] **Step 2: Run the focused tests and verify they fail**
Run:
```powershell
pnpm --dir web --filter @lda/console test -- src/presentation/presentation-target-status.test.ts src/presentation/usePresentationTargetStatus.test.tsx
```
Expected: FAIL because the hook does not expose `retryHealth` or `liveTargetReady` yet.
- [ ] **Step 3: Implement the health controller**
Add a probe-generation state/ref or equivalent stable retry key to `usePresentationTargetStatus`. On retry, set the probe to `checking`, clear the previous failure, and rerun the existing effect. Preserve the existing presentation status labels, but calculate `liveTargetReady` from the probe result independently of `demoState.mode`.
Keep the hook's cleanup guard so a response from an obsolete probe cannot overwrite newer status.
- [ ] **Step 4: Thread retry and readiness to the stage**
Extend `PresentationStage` props with:
```ts
readonly retryHealth: () => void;
readonly liveTargetReady: boolean;
```
Pass both values from `PresentationRoute`. Do not render UI in this task; the values are needed by the launch control task.
- [ ] **Step 5: Run the focused tests**
Run the same command from Step 2. Expected: all target-status tests pass.
- [ ] **Step 6: Commit**
```powershell
git add web/apps/console/src/presentation/presentation-target-status.ts web/apps/console/src/presentation/presentation-target-status.test.ts web/apps/console/src/presentation/usePresentationTargetStatus.ts web/apps/console/src/presentation/usePresentationTargetStatus.test.tsx web/apps/console/src/presentation/PresentationRoute.tsx web/apps/console/src/presentation/PresentationStage.tsx
git commit -m "feat: expose retryable presentation target health"
```
### Task 2: Allow the timeline agent to launch an explicit mode
**Files:**
- Modify: `web/apps/console/src/demo/agent/timelineAgent.ts`
- Modify: `web/apps/console/src/demo/agent/timelineAgent.test.tsx`
- Modify: `web/apps/console/src/presentation/OperatorChat.tsx`
- Modify: `web/apps/console/src/presentation/OperatorChat.test.tsx`
**Interfaces:**
- Change `runPreparedWorkflow` to accept `mode?: TimelineAgentMode`.
- Calling it without an argument preserves current behavior for the chat action.
- Calling `runPreparedWorkflow("live")` must call `demo.start("live")` even when the current direct-link state is replay, but only when the configured target is live-capable.
- Add `canRunLive: boolean` to `TimelineAgentController`, derived from the configured presentation mode and current status/readiness.
- [ ] **Step 1: Write failing tests for explicit live launch from replay**
Add a controller test with a healthy configured target and `demo.state.mode === "replay"`. Assert:
```tsx
await act(async () => result.current.runPreparedWorkflow("live"));
expect(start).toHaveBeenCalledWith("live");
```
Add a failed-target test asserting `canRunLive === false` and that the live launch does not call `start("live")`.
- [ ] **Step 2: Run the focused tests and verify they fail**
```powershell
pnpm --dir web --filter @lda/console test -- src/demo/agent/timelineAgent.test.tsx src/presentation/OperatorChat.test.tsx
```
Expected: FAIL because `runPreparedWorkflow` has no mode argument and the controller has no explicit live capability.
- [ ] **Step 3: Implement the explicit mode seam**
Use the existing `TimelineAgentOptions.mode` and target status/readiness to calculate `canRunLive`. Keep the normal chat action using its current derived mode. Make the explicit mode parameter choose the call to `demo.start`, while guarding against `inFlight` and non-ready phases.
Do not append a second or duplicate message chain for the Scene 10 control; the existing timeline-agent message projection remains the single chat history source.
- [ ] **Step 4: Update OperatorChat's existing action call**
Keep its current behavior and update the function call only as required by the widened signature. The hidden demo scenes must not regain a chat launch button.
- [ ] **Step 5: Run focused tests**
Expected: all timeline-agent and OperatorChat tests pass.
- [ ] **Step 6: Commit**
```powershell
git add web/apps/console/src/demo/agent/timelineAgent.ts web/apps/console/src/demo/agent/timelineAgent.test.tsx web/apps/console/src/presentation/OperatorChat.tsx web/apps/console/src/presentation/OperatorChat.test.tsx
git commit -m "feat: support explicit live demo launch"
```
### Task 3: Add the Scene 10 launch surface
**Files:**
- Create: `web/apps/console/src/presentation/DemoRunLaunchControl.tsx`
- Create: `web/apps/console/src/presentation/DemoRunLaunchControl.test.tsx`
- Modify: `web/apps/console/src/presentation/DemoWorkflowScene.tsx`
- Modify: `web/apps/console/src/presentation/DemoWorkflowScene.test.tsx`
- Modify: `web/apps/console/src/presentation/SceneBody.tsx`
- Modify: `web/apps/console/src/presentation/PresentationStage.tsx`
- Modify: `web/apps/console/src/presentation/styles/demo-workflow.css`
**Interfaces:**
```tsx
type DemoRunLaunchControlProps = {
readonly status: PresentationTargetHealth;
readonly liveTargetReady: boolean;
readonly demo: DemoTimelineController;
readonly timelineAgent: TimelineAgentController | undefined;
readonly retryHealth: () => void;
};
```
The control renders only when `scene.id === "run-from-deployment"` and
`beat.id === "operation"`. It must not appear on graph/input/Scene 11/Scene 12
beats.
- [ ] **Step 1: Write component tests**
Cover these exact states:
1. healthy target: `Run prepared workflow` is visible and enabled;
2. replay direct-link with healthy target: `Run prepared workflow` is still
visible and launches live, not replay;
3. checking: visible `Checking live service` state and no duplicate start;
4. failed target: visible `Play replay walkthrough` and `Retry live service`;
5. active/running: duplicate launch is disabled;
6. non-operation beat: component renders nothing.
- [ ] **Step 2: Run tests and verify they fail**
```powershell
pnpm --dir web --filter @lda/console test -- src/presentation/DemoRunLaunchControl.test.tsx src/presentation/DemoWorkflowScene.test.tsx
```
Expected: FAIL because the component and Scene 10 wiring do not exist.
- [ ] **Step 3: Implement the control**
Use explicit, factual labels. The primary action must call
`timelineAgent.runPreparedWorkflow("live")` when `liveTargetReady` is true.
When the target is unavailable, the primary action calls
`timelineAgent.runPreparedWorkflow("replay")`; this is explicitly labelled as
replay. `retryHealth` is a separate action and never starts a run.
The control should expose `role="status"` for the current target/run state and
stable accessible names for the actions. Keep the markup compact enough to sit
inside Scene 10's operation stage without opening the hidden chat rail.
- [ ] **Step 4: Thread props and mount only on Scene 10 operation**
Thread `timelineAgent`, `targetStatus`, `liveTargetReady`, and `retryHealth`
through `SceneBody` to `DemoWorkflowScene`. Mount the control beside the
operation block only for the operation beat. The rest of the existing graph,
approval, output, and trace layout remains unchanged.
- [ ] **Step 5: Add focused integration tests**
Assert direct `#scene/run-from-deployment/operation` includes the launch
control, while `#scene/run-from-deployment/graph` and
`#scene/typed-human-boundary/approval` do not. Assert that clicking the live
action calls the explicit live method and does not produce a replay start.
- [ ] **Step 6: Add scoped CSS**
Add only `.demo-run-launch-control` styles to
`styles/demo-workflow.css`. Use existing presentation tokens, avoid a new
theme, and make the live/replay distinction textual and structural rather than
relying only on color.
- [ ] **Step 7: Run focused tests**
```powershell
pnpm --dir web --filter @lda/console test -- src/presentation/DemoRunLaunchControl.test.tsx src/presentation/DemoWorkflowScene.test.tsx src/presentation/PresentationRoute.test.tsx
```
Expected: all focused tests pass.
- [ ] **Step 8: Commit**
```powershell
git add web/apps/console/src/presentation/DemoRunLaunchControl.tsx web/apps/console/src/presentation/DemoRunLaunchControl.test.tsx web/apps/console/src/presentation/DemoWorkflowScene.tsx web/apps/console/src/presentation/DemoWorkflowScene.test.tsx web/apps/console/src/presentation/SceneBody.tsx web/apps/console/src/presentation/PresentationStage.tsx web/apps/console/src/presentation/styles/demo-workflow.css
git commit -m "feat: expose Scene 10 live replay launch"
```
### Task 4: Preserve truthful mode transitions and fallback
**Files:**
- Modify: `web/apps/console/src/presentation/presentation-target-status.ts`
- Modify: `web/apps/console/src/presentation/presentation-target-status.test.ts`
- Modify: `web/apps/console/src/presentation/PresentationRoute.tsx`
- Modify: `web/apps/console/src/presentation/PresentationRoute.test.tsx`
- Modify: `web/apps/console/src/demo/useDemoTimeline.ts`
- Modify: `web/apps/console/src/demo/useDemoTimeline.test.tsx`
- [ ] **Step 1: Add route-level regression tests**
Test these flows with mocked `callOperation` responses:
```tsx
it("keeps a direct operation hash replay-backed until launch", async () => {
// Render direct operation hash; wait for replay facts; assert no live start call.
});
it("starts live only after the Scene 10 action", async () => {
// Render direct operation hash with healthy health response; click the launch.
// Assert deployment inspect is the first non-health operation.
});
it("offers explicit replay after health failure", async () => {
// Reject health; assert the replay action is visible and no run RPC occurred.
});
```
Add a timeline test proving that after a live event has been appended, replay
priming cannot replace the live event list while the live run is active.
- [ ] **Step 2: Run tests and verify any missing behavior**
```powershell
pnpm --dir web --filter @lda/console test -- src/presentation/PresentationRoute.test.tsx src/demo/useDemoTimeline.test.tsx
```
- [ ] **Step 3: Fix mode synchronization**
Ensure the existing ready-state synchronization does not switch an explicitly
started live timeline back to replay merely because the current URL requires a
replay stage. The synchronization may prime replay only while the timeline is
still `ready` and no explicit live launch has happened.
When replay is explicitly selected after a failed health check, set replay mode
and keep the status copy truthful. Do not synthesize live evidence from the
canonical recording.
- [ ] **Step 4: Verify approval and trace continuation**
Use the existing `submitSelectedIssues`, `requestRevision`, and trace-read
paths. Confirm live resume sends the typed payload and that trace evidence is
recorded after the live resume; do not introduce new resume logic in this task.
- [ ] **Step 5: Commit**
```powershell
git add web/apps/console/src/presentation/presentation-target-status.ts web/apps/console/src/presentation/presentation-target-status.test.ts web/apps/console/src/presentation/PresentationRoute.tsx web/apps/console/src/presentation/PresentationRoute.test.tsx web/apps/console/src/demo/useDemoTimeline.ts web/apps/console/src/demo/useDemoTimeline.test.tsx
git commit -m "test: preserve truthful live replay transitions"
```
### Task 5: Documentation and end-to-end verification
**Files:**
- Modify: `web/README.md`
- Modify: `docs/current_roadmap.md`
- Move: `docs/superpowers/plans/2026-07-12-presentation-live-replay-activation.md` -> `docs/historical/superpowers/plans/2026-07-12-presentation-live-replay-activation.md`
- [ ] **Step 1: Update user-facing run instructions**
Document the exact local process:
```powershell
pnpm --dir web dev
uv run wf-rpc-server --config examples/lda_report_workflow/wf.config.json --host 127.0.0.1 --port 8765
```
Explain that `5173` is the browser, `8787` is the console server, and
`8765/rpc` is the workflow server. Explain that Scene 10's operation beat has
the explicit live/replay launch and that direct links remain replay until
launch.
- [ ] **Step 2: Mark the roadmap item complete and archive the plan**
Replace the stale “Live/replay truth and run activation” entry with a link to
the historical plan. Do not alter the separate visual-pass or story-flow
items.
- [ ] **Step 3: Run repository verification**
```powershell
pnpm --dir web --filter @lda/console test
pnpm --dir web typecheck
pnpm --dir web --filter @lda/console build
git diff --check
git status --short
```
Expected: all console tests pass, typecheck/build pass with only the known Vite
chunk-size warning, diff check has no new whitespace errors, and the only
unrelated worktree change remains `Scene8ChatEntry.tsx`.
- [ ] **Step 4: Run live browser smoke**
With both servers running, open:
```text
http://127.0.0.1:5173/present#scene/run-from-deployment/operation
```
Verify the following sequence:
1. `Run prepared workflow` is visible.
2. Clicking it sends `workflow.deployments.inspect` through `/api/rpc`.
3. The run reaches the typed interrupt without replay replacement.
4. Scene 11 approval sends the selected typed payload through
`workflow.runs.resume`.
5. Scene 12 trace shows live trace evidence.
6. Stopping the workflow server produces a visible failure; clicking the
explicit replay action starts the canonical recording instead.
7. Starting the workflow server again and clicking `Retry live service`
restores the live-ready action without a page reload.
- [ ] **Step 5: Run the final review**
Run the repository's review workflow against the slice, inspect the diff for
accidental `.superpowers` files, and verify that the user-owned
`Scene8ChatEntry.tsx` edit is not staged.
- [ ] **Step 6: Commit documentation/archive changes**
```powershell
git add web/README.md docs/current_roadmap.md docs/historical/superpowers/plans/2026-07-12-presentation-live-replay-activation.md
git rm docs/superpowers/plans/2026-07-12-presentation-live-replay-activation.md
git commit -m "docs: complete presentation live replay activation"
```
## Self-Review
- The plan covers target retry, explicit live launch, replay fallback, direct
hash behavior, approval/trace continuation, documentation, and browser
verification.
- No task changes the Scene 1 merge, beat fact checking, or visual cleanup.
- All new interfaces are named consistently: `retryHealth`, `liveTargetReady`,
`runPreparedWorkflow(mode?)`, and `DemoRunLaunchControl`.
- The only intentionally external prerequisite is the already documented
`wf-rpc-server`; no new service or dependency is introduced.
@@ -0,0 +1,118 @@
# Presentation Live/Replay Activation Design
**Status:** Proposed
## Goal
Make the prepared workflow demo's live/replay boundary explicit and usable. A
presenter must be able to see the live-run action, start the real workflow
through the existing browser-to-console-to-RPC path, and fall back to the
reviewed recording only through an explicit replay action or an unavailable
service.
This slice does not change the Scene 1/2 story, fact-check storyboard content,
Scene 8/9 authoring recording, chat framework, or visual theme system.
## Current Problem
The live timeline controller and RPC executor already exist, but the visible
action is owned by `OperatorChat`. Scenes 1012 currently hide that chat, so a
prepared run can be implemented without a visible way to start it. Direct
Scene 1012 hashes also prime replay state by design, while the target health
status is only probed on mount. This makes the system look disconnected even
when `wf-rpc-server` is running.
## Design
### Launch surface
Expose a compact prepared-run control on Scene 10's `operation` beat. It is a
scene-owned control, not a reintroduced chat rail. It must remain visible while
the target is checking, ready, failed, or the live run is active.
The control communicates the current action:
| Target/timeline state | Visible action |
| --- | --- |
| Checking | `Checking live service` and a retry action when the check settles |
| Healthy, not started | `Run prepared workflow` |
| Live run active | Current operation/`Running live workflow`; duplicate start disabled |
| Health failed | `Play replay walkthrough` plus the failure reason |
| Replay active | `Replay walkthrough active` |
Only an explicit launch action changes the timeline from ready to running. A
direct deep link remains replay-backed when it has no active run context.
### Target status and retry
Reuse `resolvePresentationTarget()` and `usePresentationTargetStatus()`. Add a
retry mechanism at the presentation boundary rather than duplicating target
configuration or connection logic. Retrying must re-run `workflow.health`
against the same resolved target and update the visible status without a full
page reload.
The two local servers remain separate and documented:
```text
browser :5173 -> console server :8787 -> wf-rpc-server :8765/rpc
```
The browser never calls `wf-rpc-server` directly; all operation evidence still
comes from the existing `/api/rpc` proxy.
### Live execution
The launch action calls the existing `DemoTimelineController.start("live")`.
The existing live executor remains the source of truth for:
1. deployment inspection;
2. run start and typed interrupt payload;
3. submitted or revision-requested resume;
4. final trace read.
After the first live operation succeeds, the timeline must not be replaced by
canonical replay data. A live failure is shown as a failed live event with its
operation and error; it may offer an explicit `Play replay walkthrough` action,
but fallback must not happen silently.
### Scope of truth indicators
The live/replay status and live launch control belong to the prepared demo
surface. Non-demo narrative, architecture, evaluation, conclusion, and
discussion views should not gain a persistent live-service badge or run action.
## Acceptance Criteria
1. With `wf-rpc-server` and the web server running, Scene 10's operation beat
visibly exposes `Run prepared workflow` and starts a live timeline.
2. A live run records deployment inspection, run start, interrupt, resume, and
trace evidence using the existing `callOperation` path.
3. The typed approval form remains the only way to provide the resume decision;
both submitted and revision-requested outcomes are sent to the live server.
4. A direct Scene 1012 hash without an active run still shows the reviewed
replay, not a fabricated live state.
5. When health fails, the presenter sees the reason and an explicit replay
action; no live operation is silently represented as replay evidence.
6. Starting the live server after the presentation is already open can be
recovered with retry, without reloading the page or reconnecting manually.
7. The launch control cannot issue duplicate starts while an operation is in
flight or a live run is already active.
8. Scene 8 and Scene 9 remain deterministic authoring replay and do not call
workflow authoring RPC operations.
## Verification
Unit and component tests must cover target retry/status transitions, launch
visibility, duplicate-start protection, explicit replay fallback, and live
versus replay operation calls. A browser smoke run must verify:
```text
Scene 10 operation -> live start -> interrupt -> approval -> resume -> trace
Scene 10 direct hash with server unavailable -> explicit replay
server started after page load -> retry -> live-ready action
```
The final verification must include the existing scoped console tests,
typecheck, build, and `git diff --check`. The unrelated user edit in
`web/apps/console/src/presentation/authoring/Scene8ChatEntry.tsx` must remain
unstaged.