Skip to content

fix(graph): a release killed between its two journal events no longer runs its node twice - #1014

Merged
drewstone merged 3 commits into
mainfrom
fix/graph-resume-double-execute
Aug 25, 2026
Merged

fix(graph): a release killed between its two journal events no longer runs its node twice#1014
drewstone merged 3 commits into
mainfrom
fix/graph-resume-double-execute

Conversation

@drewstone

Copy link
Copy Markdown
Contributor

Closes #1013.

The defect

release() journals the pinned envelope (node-inputs-resolved), then the wave consumption (join-state). A process killed between the two leaves the instance pinned while its gating edges still read satisfied, so the restart does BOTH:

  1. reenterAfterCrash re-enters the pinned instance (correct), and
  2. the catch-up tryRelease sees the unconsumed wave and releases a SECOND instance.

The node then executes twice in one resumed segment. Found by agent-dev-container's workflow scheduler, whose notify fired twice through this window on a 3-node chain (journal tail in #1013).

The fix

The fold records wave consumption on the instance (FoldInstance.waveConsumed, set by join-state). A restart that finds a released instance WITHOUT it journals the missing join-state before spawning, re-deriving the same decision the crashed process made — its gating edges are folded exactly as they were when it released, because their verdicts were journaled first, so decideJoin answers identically. Idempotent, and it makes the journal complete rather than papering over it at read time.

Proof

tests/graph/replay.test.ts gains a case that kills at the second node's envelope pin and asserts the node runs EXACTLY once on resume (it ran twice before). The existing every-boundary kill sweep and the 58-case tests/kernel/graph.test.ts compatibility bar are unchanged: 143/143 across graph + kernel + topologies. Release gates green (version-bump, docs:check, docs:freshness, testing fixture).

…s wave consumption

The scheduler journals the pinned envelope, then the wave consumption.
A process killed between the two left the instance pinned while its
gating edges still read satisfied.
The restart re-entered the pinned instance AND released a second one, so
the node executed twice in one resumed segment.

The fold now records wave consumption on the instance.
A restart completes a release that never journaled its consumption, from
the same decision the crashed process made.
The exported FoldInstance gains a field, so the change ships as a minor.

@tangletools tangletools left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Automated approval: author drewstone, all checks green.

@drewstone
drewstone merged commit e7cdc19 into main Aug 25, 2026
4 checks passed
@drewstone
drewstone deleted the fix/graph-resume-double-execute branch August 25, 2026 04:56
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.

graph: resume double-executes an instance killed between its envelope pin and its kernel spawn

2 participants