fix(sync): make Edge on-demand synchronization point-in-time - #2045
fix(sync): make Edge on-demand synchronization point-in-time#2045branarakic wants to merge 2 commits into
Conversation
| if (this.inFlightByContextGraph.get(coordinator.contextGraphId) === coordinator) { | ||
| this.inFlightByContextGraph.delete(coordinator.contextGraphId); | ||
| } | ||
| const settlement = this.runSettlementByContextGraph.get(coordinator.contextGraphId); |
There was a problem hiding this comment.
🟡 Issue: Settlement state is mirrored outside the coordinator without the same identity guard
What's wrong
The PR introduces a second lifecycle registry that must stay perfectly synchronized with inFlightByContextGraph, but it does not use the same coordinator identity check. That creates temporal coupling between two maps and makes the service harder to reason about: readers now have to prove no stale worker can touch a newer settlement entry.
Example
If start() is ever called again for the same context graph before an older worker's finally runs, the older worker can delete and resolve the newer run's settlement promise because the settlement map is keyed only by context graph. Even if callers currently avoid that, the coordinator API does not make the invariant explicit.
Suggested direction
Make settlement part of the coordinator lifecycle instead of a second context-graph-keyed map, or guard the settlement lookup/delete by the exact coordinator instance. That removes the parallel state model and makes the ownership rule obvious.
For Agents
In ContextGraphCatchupCoordinatorService, attach the settlement promise/resolve pair to the CatchupCoordinator object, or store it in a map value that includes the coordinator identity and resolve/delete it only under the same identity guard as inFlightByContextGraph. Preserve whenCurrentRunSettles behavior and add a focused test for stale coordinator finalization not settling a newer run.
Stack position
mainortestnet-canaryduring RFC-64 validation.User impact
An Edge request with
syncMode: on-demandnow produces a point-in-time local snapshot:always-onwhile catch-up is running, the live mode is re-read at settlement and the promotion wins.The live M1 gate exposed the prior behavior: the selected on-demand graph advanced from 24 to 48 VM and SWM triples after a later publication wave, before the second explicit request.
Before
sequenceDiagram participant U as Edge user participant E as Edge node participant C as Catch-up coordinator participant P as Publisher U->>E: Subscribe CG (on-demand) E->>C: Start VM and SWM catch-up C->>P: Fetch selected snapshot P-->>C: Snapshot data C-->>E: Catch-up done E-->>U: status=done Note over E: Live CG topics remain subscribed P-->>E: Later VM/SWM publication Note over E: Snapshot advances without another user requestAfter
sequenceDiagram participant U as Edge user participant E as Edge node participant C as Catch-up coordinator participant P as Publisher U->>E: Subscribe CG (on-demand) E->>C: Start VM and SWM catch-up C->>P: Fetch selected snapshot P-->>C: Snapshot data C-->>E: Catch-up settled E->>E: Re-read current subscription mode alt Still on-demand E->>E: Detach live CG topics Note over E: Local VM/SWM snapshot is retained else Promoted to always-on E->>E: Keep live CG topics attached end E-->>U: status=done P--xE: Later publication is not delivered to on-demand CGVerification
pnpm --filter @origintrail-official/dkg run buildpnpm --filter @origintrail-official/dkg exec vitest run test/context-graph-catchup-coordinator.test.ts test/daemon-http-behavior-extra.test.tsRemaining release evidence
The complete five-cell M1 gate will be rerun from fresh state on the exact stacked head. That gate remains the authority for live/cold VM+SWM behavior and is not replaced by these focused tests.