Files
lda-wf/docs/historical/superpowers/plans/2026-07-12-presentation-live-replay-activation.md
T

18 KiB
Raw Blame History

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.

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:

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:

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
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:

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
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
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:

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
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
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
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:

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
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
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:

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
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:

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
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.