Skip to content

fix(follow): an Auto follow into a cue on another node starts it (869fc8ytz) - #25

Merged
ibiltari merged 7 commits into
rc_1from
fix/869fc8ytz-cross-node-follow
Oct 8, 2026
Merged

ibiltari merged 7 commits into
rc_1from
fix/869fc8ytz-cross-node-follow

Conversation

@ibiltari

@ibiltari ibiltari commented Oct 6, 2026

Copy link
Copy Markdown
Member

Problem. A cue with Auto follow (post_go = go_at_end) fired its target only on the nodes where the cue itself plays. A target on another node never started, and nothing was logged. This affected every engine line. ClickUp 869fc8ytz.

Scope. Only AudioCue and VideoCue sources were affected. ActionCue, FadeCue, CueList and DmxCue are local on every node, so every node already fired their follow; this was checked on test2 before any code was written.

Fix.

  • The node that owns the source announces the follow instant when the cue starts, or at the start of its last loop. It never announces for an endless loop.
  • The controller only relays the announcement to every node.
  • The other nodes pre-arm what they would start, wait for the instant on MTC and dispatch it there.
  • If the follow doesn't happen, the owner sends a cancel: stop (stop, pause or fade-out action), restart (the source was started again) or error.
  • A cancel that reaches a target already on stage stops it only within 2 s, and only for stop/restart.
  • A run counter in every GO keeps a follow from starting anything after a STOP.

Design and reviews: cuems-RELATIONS Plans/2026-10-05-engine-cross-node-auto-follow.md (two Opus plan reviews, one Opus code review; all findings fixed).

Verified on test2 (plan §11):

Deploy note. The whole cluster must run this engine. With an old controller, nothing is announced (the bug stays). An old node logs Command follow not found for each relayed message.

Merge note. PR #23 (869fa89uh) edits the same last-loop branch of loop_videoCue. If its duration repair rewrites _end_mtc after a single-loop cue has revealed, the instant announced to the other nodes and the one the owner fires on differ. The owner logs an ERROR with both values. Check this when both PRs are merged.

…z) (red)

Every GO carries run_seq; STOP, load and unload bump it before they
forward anything; a node's follow message is relayed to every node only
while the show runs and only with the current run_seq. The four tests that
pinned the GO value now expect it to be a dict in every case.
…869fc8ytz)

The controller keeps a run counter, started from the clock. STOP, load
and unload bump it before forwarding; every GO ships it to the nodes. A
node's 'follow' status message (announce or cancel) is forwarded to every
node only while running and only with the current value; the check and
the forward share one lock with the bump, so a relayed follow is always
queued ahead of the STOP that ends its run. The controller keeps no
follow state.

The GO value is now always a dict. Nodes read nothing from it but the
anchor (_resolve_go_anchor), so the caller's own value is no longer
passed on.

Plan: cuems-RELATIONS Plans/2026-10-05-engine-cross-node-auto-follow.md
§3.3-3.4.
The receive hook (bus order, never the command lock), the receiving
node's follow table (announce, pre-arm, wait for the instant, dispatch
through a commit gate in go(), cancel by entry state, the 2 s rule), and
the owning node's announce/cancel (at the reveal or the last loop; one
cancel per announcement; the restart cancel from go()). Includes the
tests for the seven findings of the code review of the implementation.
…fc8ytz)

An Auto follow (post_go='go_at_end') fired its target only on the nodes
where the source is local, so a target on another node never started,
silently, on every engine line.

The node that owns an AudioCue or VideoCue source now announces the
follow instant when the cue reveals (or at the start of its last loop;
never for an endless loop), through the controller. Every other node
pre-arms what it would start, waits for the instant on MTC and
dispatches it there through go_from. ActionCue, FadeCue, CueList and
DmxCue sources are local on every node and keep firing their own follow.

If the follow does not happen the owner cancels: 'stop' (stop, pause or
fade-out action), 'restart' (sent from go()'s commit section when the
source is started again), 'error'. A STOP, a load or the yield send
nothing; every node handles those itself. A cancel that reaches a target
already on stage stops it only within 2 s and only for stop/restart.

- cues/FollowRelay.py: both halves of the node side; one entry per
  source, its own lock, never calls into CueHandler while holding it.
- NodeCommunications: a receive hook in bus order, before any command
  thread; send_operation_nowait.
- NodeEngine: run_seq recorded and pending follows dropped at receipt;
  follow messages applied there, never under the command lock; STOP and
  load marked applied after they run.
- CueHandler: go() gets a commit_gate (refusals, gate, restart cancel,
  assignments); go_from passes arm_epoch/require_stop_epoch/commit_gate
  through; stop_dispatched_follow and disarm_if_unchanged (guarded disarm
  under the cue's arm claim); the reveal stamp; go_threaded wraps the
  pass so every exit decides its cancel once.
- loop_cue: on_last_loop callback for audio and video.

Verified on test2 (suite 1109 passed; 40 live runs, 0 LATE, 0 fallback;
old-node smoke test; PR #22 drills and a Medina sala1 replay unchanged).
Plan: cuems-RELATIONS Plans/2026-10-05-engine-cross-node-auto-follow.md.
@ibiltari
ibiltari requested a review from backenv as a code owner October 6, 2026 18:16
@ibiltari
ibiltari merged commit f68eaee into rc_1 Oct 8, 2026
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant