Skip to content

Enable playback of actual completed orchestrated runs #76

Description

@williamthorsen

Problem

The Factory visualization can only play back a hardcoded demo recording. Real orchestrated runs are viewable as static final-state snapshots, but there is no way to replay them as animations. This limits the system's usefulness for reviewing past runs and understanding how the orchestration pipeline progressed.

Context

  • The server already has GET /api/runs/:projectSlug/:runId which reads run-index.json + run-log.jsonl, folds events via foldEvents(), and returns the final CanonicalRunStatus
  • The client's PlaybackController accepts CanonicalRunStatus[] and plays them at fixed intervals — it's source-agnostic
  • The demo uses foldEvents(header, events.slice(0, idx + 1)) (fold-to-cursor) to generate intermediate snapshots from raw events — this same pattern works for any run
  • foldEvents, RunHeader, and RunEvent are already available in the client bundle via @codeassembly/run-core
  • v3 runs have event logs (run-log.jsonl); v1/v2 runs do not and cannot be replayed

Solution

Server: raw events endpoint

Add GET /api/runs/:projectSlug/:runId/events returning { header: RunHeader, events: RunEvent[] }. Extract the v3 read-and-parse logic from parseRunData() into a reusable parseRunRawData(runPath) that returns header + events without folding. Refactor parseRunData to call it then fold. v1/v2 runs return 404 with a descriptive error.

Client: player redesign (streaming-app mental model)

Redesign the player UI following a streaming-app model (like Amazon Prime / Apple TV with live channels):

  • Source selection is separate from transport controls. Selecting a demo recording is like choosing a movie; clicking a completed run's "Replay" is like selecting a title to watch. These are source selection actions, not player actions.
  • Player controls (play/pause/step/stop/speed) are their own pop-out panel (PlayerPanel, replacing the old DemoControlPanel). The player appears only when a source is active and contains only transport controls — no recording selector dropdown.
  • Live view is the default: clicking a live orchestration is like tuning into a live channel.

Add fetchRunEvents() to the client API. Create a shared generateSnapshots(header, events) utility producing one CanonicalRunStatus per event via fold-to-cursor. Create useRunPlayback hook that fetches events, generates snapshots, and feeds them into the existing PlaybackController.

Playback behavior

  • Clicking "Replay" loads the run into the player. If the player is already playing, the new source auto-plays. Otherwise it loads in stopped state.
  • If the run is already loaded, "Replay" is a no-op.
  • The transport "Stop" button exits playback mode entirely (returns to live/static view). There is no intermediate "stopped but source loaded" state.
  • Starting replay stops demo mode; starting demo stops replay.

Acceptance criteria

  • GET /api/runs/:projectSlug/:runId/events returns header + events for v3 runs
  • parseRunData refactored to use parseRunRawData internally (no behavior change)
  • v1/v2 runs return 404 with descriptive message from the events endpoint
  • Client can trigger replay of any selected v3 run
  • Replay uses the same transport controls as demo playback (play/pause/step/speed)
  • Player panel pops out as its own entity only when a playback source is active
  • Source selection (demo selector, replay button) is separate from player transport controls
  • "Stop" exits playback mode entirely and returns to live/static view
  • Demo mode still works identically
  • Selecting a different run while replaying stops the current replay

Key files

  • packages/run-core/src/parsers/run-data-parser.tsparseRunData, new parseRunRawData
  • packages/run-core/src/run-data-parse-error.ts — extend RunDataParseErrorCategory with 'no_event_log'
  • packages/factory/src/server/routes/runs.ts — new events endpoint
  • packages/factory/src/client/api/client.tsfetchRunEvents
  • packages/factory/src/client/playback/playback-controller.ts — existing controller, new PlaybackSource type
  • packages/factory/src/client/playback/generate-snapshots.ts — shared snapshot generation
  • packages/factory/src/client/hooks/usePlayback.ts — generalize to accept PlaybackSource
  • packages/factory/src/client/hooks/useRunPlayback.ts — new hook
  • packages/factory/src/client/components/PlayerPanel.tsx — renamed from DemoControlPanel, transport only
  • packages/factory/src/client/App.tsx — merge playback sources, separate source selection from player

Metadata

Metadata

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions