Skip to content

fix(sync): make Edge on-demand synchronization point-in-time - #2045

Open
branarakic wants to merge 2 commits into
codex/rfc64-m1-clean-restartfrom
codex/rfc64-m1-on-demand-lifetime
Open

fix(sync): make Edge on-demand synchronization point-in-time#2045
branarakic wants to merge 2 commits into
codex/rfc64-m1-clean-restartfrom
codex/rfc64-m1-on-demand-lifetime

Conversation

@branarakic

Copy link
Copy Markdown
Contributor

Stack position

User impact

An Edge request with syncMode: on-demand now produces a point-in-time local snapshot:

  • VM and SWM catch-up complete as before.
  • The synchronized data remains queryable locally.
  • Live CG gossip subscriptions are detached when the serialized catch-up settles, so later publications are not downloaded without another user request.
  • If the same CG is promoted to always-on while catch-up is running, the live mode is re-read at settlement and the promotion wins.
  • If lifetime settlement fails, every coalesced catch-up view is marked failed instead of reporting a misleading successful point-in-time result.
  • The already-ready shortcut applies the same lifetime rule.

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 request
Loading

After

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 CG
Loading

Verification

  • pnpm --filter @origintrail-official/dkg run build
  • pnpm --filter @origintrail-official/dkg exec vitest run test/context-graph-catchup-coordinator.test.ts test/daemon-http-behavior-extra.test.ts
    • 2 files
    • 73 tests passed
  • Tests cover:
    • one settlement after serialized narrow/wide coalescing;
    • fail-visible settlement errors;
    • on-demand detachment with retained synchronized state;
    • concurrent promotion to always-on.

Remaining 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.

Comment thread packages/cli/src/daemon/context-graph-catchup-coordinator.ts Outdated
Comment thread packages/cli/src/daemon/context-graph-catchup-coordinator.ts Outdated
Comment thread packages/cli/src/daemon/routes/context-graph.ts Outdated
if (this.inFlightByContextGraph.get(coordinator.contextGraphId) === coordinator) {
this.inFlightByContextGraph.delete(coordinator.contextGraphId);
}
const settlement = this.runSettlementByContextGraph.get(coordinator.contextGraphId);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🟡 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.

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.

2 participants