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.