fix(sync): batched exact VM reconciliation (#1871) + lazy on-chain probe (#1852) - #1876
Merged
Merged
Conversation
…ated CGs
The must-vs-may refactor turned the probe unconditional: every SWM gossip
receive awaited isContextGraphProvenPublicOnChain (a live chain read through
resolveOnChainAccessPolicyState with its timeout machinery) even when the CG
has no agent gate — the case where resolveWorkspaceEncryptionRequirement never
reads the answer, because both requires and supports collapse to
hasPrivateAccessPolicy.
Measured on a 6-node devnet, that took SWM receive-apply from ~3ms to ~33ms
and turned the public-CG sync gate red in BOTH public cells: the author's next
convergence poll slipped one 3s cycle, the VM publish therefore landed AFTER
the live receiver's subscribe-triggered catch-up had already run, and — since
chain-promote defers without transaction provenance and the snapshot
materializer lane is separately parser-blocked ("ambiguous publishedAt") — the
live receiver starved until the ~5-minute periodic sweep. Symptom in the gate:
"LIVE receiver has VM content — TIMEOUT ... reconcile pending watermark=0/1"
at 150s, while the LATE receiver (fresh catch-up, runs post-publish by
construction) converged in ~3s.
Restore the pre-refactor evaluation order — probe only when
agentGateAddresses !== null — while keeping the must-vs-may split intact.
For a gated CG behaviour is unchanged.
Tests drive the REAL handler with a counting oracle, in the unit include list:
- ungated CG: write applies AND the probe is never invoked. Mutation-tested:
re-introducing the unconditional await fails exactly this test.
- gated CG: the probe IS invoked and a public proof admits plaintext, so
laziness cannot regress into "never".
Note: the underlying exposure is pre-existing and NOT closed here — a LIVE
subscriber whose catch-up ran before the publish has no event-driven VM
delivery lane (finalization broadcast schedules no durable sync; chain-promote
requires provenance it never gets; the SWM materializer heal lane dies on
"ambiguous publishedAt" fleet-wide). The baseline passes the gate only because
its receive path is fast enough that the publish beats the queued catch-up.
Deserves its own issue.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…the agent-to-handler oracle wiring Two review follow-ups on #1843: - resolveWorkspaceEncryptionRequirement (and its must-vs-may rationale) moves from the 2.3k-line workspace-handler.ts into workspace-encryption-policy.ts; the handler keeps only the local lazy-probe sequencing note, and the policy tests import the focused module instead of the orchestration file. - New agent-level regression builds SharedMemoryHandler through the REAL getOrCreateSharedMemoryHandler and delivers a signed plaintext write on an agent-gated CG with a counting isContextGraphPublicOnChain: the write must apply and the probe must be consulted. Removing the publicAccessPolicyOnChainOracle wiring from dkg-agent-swm-substrate.ts fails this test with the exact production drop it prevents (mutation-verified). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…ive recovery Two review fixes for the batched exact VM reconciliation path: - recoverVmReconcileBatch now requests at most MAX_EXACT_SYNC_ASSETS UALs per peer. A scan batch configured above the protocol cap (DKG_VM_RECONCILE_BATCH_SIZE > 10) previously produced a filter that requireExactAssetUals rejects, so every exact fetch threw and recovery was silently dead. Over-cap targets stay in `remaining` for a later peer or pass; fetch telemetry and revalidation (which costs chain reads per ordinal) are restricted to the requested slice. - The batch fetch is now gated on the per-CG active-fetch cooldown. The batched path deliberately skips the per-UAL negative cache (consulting it primes connections to every discovered agent), which left no damper at all: a CG with permanently unavailable KAs ran a 3-peer, priority-1000 exact sync on every reconcile pass. An unproductive batch now costs one bounded fetch per sweep interval. Mirroring the inline path, progress or an unreachable network clears the cooldown, so a draining backlog still proceeds slice after slice. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…t-sync test coverage Review blockers (lupuszr): - Re-check the captured local-CG -> on-chain-CG binding immediately after recoverPendingOrdinals resolves. Recovery is the longest await in a reconcile pass; a rebind landing during it could let outcomes recovered under the old binding advance or persist cursor state for the rebound CG. Staleness detected there is now treated exactly like staleness during ordinal work: outcomes discarded, no watermark move, scan cursor reset. - Gate every exact-recovery peer through ensurePeerAdmittedForRecovery before sending. Curator hints and getConnections() both predate the network-identity probe, so a merely-connected peer is not necessarily admitted; the batch path could previously send authenticated exact requests to unverified or rejected-network peers. - Contain ordinal-worker failures. The pool used fail-fast Promise.all, so one rejecting ordinal left sibling workers running past the pass's lifetime, overlapping their network/store side effects with the caller's retry. The first error now stops dispatch across all workers, in-flight ordinals drain, and only then does the pass reject. Coverage (review-bot asks): - runDurableSync integration: exactAssetUalsFor reaches both fetch phases and an old-responder full-CG payload is filtered before the verification worker sees it. - Page-fetch coalescing + durable single-flight identity now covered for assetUals (different batches never share a run; identical batches do). - parseSyncRequest wire tests for both formats: valid filters survive, present-but-invalid fail closed to [], absent stays undefined. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This was referenced Jul 21, 2026
Contributor
Author
|
Updated: absorbed #1871's latest head 6c4343f (three reconcile race/boundary fixes + widened exact-sync coverage) — zero merge conflicts. Full re-gate on the new head: build clean, verify-fixes 8/10 (excepted knowns only), proof 32/34 (watermark-only). This PR now carries #1871 complete and current. Clear to merge — use a MERGE COMMIT so #1871/#1852 auto-close. |
This was referenced Jul 21, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What ships (step 1 of 2)
Two changes that pair to deliver full VM sync on public Context Graphs:
Base: current
origin/testnet-canary. Merging this supersedes #1852 and carries #1871. The big-CG pair (#1868 64k_metaceiling + #1842 catch-up materialization — canonical onmain) follows as step 2.Gate evidence (6-node devnet, full public-CG sync suite, run from this exact head)
verify-fixes.mjs — 8/10, both failures pre-sanctioned known issues:
proof.mjs run 1 — 33/35, failures limited to the two known watermark checks:
proof.mjs run 2 (stability) — 32/34, identical failure set: watermarks only.
Known issues, stated plainly
🤖 Generated with Claude Code