Problem
The catwalk scene currently tears down and rebuilds all Excalibur actors on every CanonicalRunStatus update. This causes visual snapping — the orchestrator teleports between stations, agents appear/disappear instantly, and gates toggle without transition. During a live run, this creates a jarring experience.
Proposed solution
Replace the tear-down-and-rebuild pattern in CatwalkScene.updateStatus() with diff-driven animation dispatch:
- Actor registry —
CatwalkScene maintains references to live actors keyed by identity (orchestrator singleton, agents by ID, gates by station pair, artifacts by composite key)
- Diff computation — On each status update, compute a
CatwalkDiff via the existing diffCatwalkConfig() (already implemented and tested)
- Animation dispatch — For each diff category, call targeted animation methods on the affected actors:
- Orchestrator walk:
actions.moveTo() slides along the catwalk rail
- Orchestrator working glow: sine-wave opacity pulse via
onPreUpdate
- Agent state transitions:
actions.fade() tweens between opacity levels; working agents pulse
- Agent deactivation: agents that complete their role fade to low opacity instead of being removed (preserves visual history)
- Gate open:
actions.scaleTo() shrinks gate height to zero over ~300ms
- Artifact appearance: new artifacts fade in from invisible
Key design decisions
deactivated agent state: new AgentAnimationState value. Agents that leave the config transition to deactivated (low opacity, no pulse) rather than being killed. This preserves a visual record of which agents participated.
- Sine-wave pulse via
onPreUpdate: avoids conflicting with Excalibur's sequential action queue, allowing pulse and movement to run simultaneously.
- Tunable constants: all animation values (
DEACTIVATED_OPACITY, pulse ranges, frequency) live in constants/animation.ts for easy visual tuning.
Acceptance criteria
Scope exclusions
- Station input/output visual layout (inputs left of divider, outputs right, divider line): the
slot: 'input' | 'output' field already exists in the data model and differ; visual positioning is deferred
- Chute animation: not in scope for this issue
Problem
The catwalk scene currently tears down and rebuilds all Excalibur actors on every
CanonicalRunStatusupdate. This causes visual snapping — the orchestrator teleports between stations, agents appear/disappear instantly, and gates toggle without transition. During a live run, this creates a jarring experience.Proposed solution
Replace the tear-down-and-rebuild pattern in
CatwalkScene.updateStatus()with diff-driven animation dispatch:CatwalkScenemaintains references to live actors keyed by identity (orchestrator singleton, agents by ID, gates by station pair, artifacts by composite key)CatwalkDiffvia the existingdiffCatwalkConfig()(already implemented and tested)actions.moveTo()slides along the catwalk railonPreUpdateactions.fade()tweens between opacity levels; working agents pulseactions.scaleTo()shrinks gate height to zero over ~300msKey design decisions
deactivatedagent state: newAgentAnimationStatevalue. Agents that leave the config transition to deactivated (low opacity, no pulse) rather than being killed. This preserves a visual record of which agents participated.onPreUpdate: avoids conflicting with Excalibur's sequential action queue, allowing pulse and movement to run simultaneously.DEACTIVATED_OPACITY, pulse ranges, frequency) live inconstants/animation.tsfor easy visual tuning.Acceptance criteria
CanonicalRunStatuschanges during a live runconstants/animation.tsScope exclusions
slot: 'input' | 'output'field already exists in the data model and differ; visual positioning is deferred