Repository navigation
feat(sync): seal clamps the HLC physical candidate to server date + MAX_DRIFT (T13314) - #1969
Merged
Merged
Conversation
…AX_DRIFT (T13314)
Spec §1.3 "Local clock ahead": sealing clamps the physical candidate to
min(at_ms, serverDate + MAX_DRIFT) when a server date from the last 24 h
is known. Captures stamped while the clock ran ahead used to seal with
future HLCs once push resumed.
- push records sync.server_clock {offsetMs, atMs} whenever it has a
server offset (setPushMeta replaces markClockAhead; gate 28 count
unchanged).
- clock-store sealWallCeiling(db, now): now + offset + MAX_DRIFT, or null
when the observation is missing, unreadable or older than 24 h.
- the sealer computes the ceiling once per seal transaction and ticks
each op at min(at_ms or now, ceiling).
Tests: an hour-ahead capture after a fresh server date seals at or below
serverNow + MAX_DRIFT; with no observation or a stale one it seals at its
at_ms. Mutations killed: no clamp, no TTL, no record, wrong margin.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…the stale offset (T13314 review LOW) When nowMs < atMs the clock was corrected after the observation; using the stale negative offset would stamp seals outside push up to |offset| in the past for a day. sealWallCeiling now returns null then. Test: an observation dated after now leaves sealing unclamped; removing the guard fails it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
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.
Task: T13314
Stacked on #1957 (feat/T12343-push). It is a leaf beside #1959, which only touches push.ts's precondition block.
Problem
Spec §1.3, "Local clock ahead": push pauses when this device's clock runs more than MAX_DRIFT ahead of the server's date. But the clamp the spec also requires, "sealing clamps the physical candidate to
min(at_ms, serverDate + MAX_DRIFT)when a server date from the last 24 h is known", was missing. Captures stamped while the clock was ahead sealed with future HLCs as soon as the clock was corrected and push resumed. A future HLC wins every LWW race on every replica.Change
push.ts: whenever push has a server offset, it recordssync.server_clock={offsetMs, atMs}before the drift check.markClockAheadbecomessetPushMeta(key, value|null), so the gate-28 count is unchanged (one INSERT, one DELETE).clock-store.ts:sealWallCeiling(db, nowMs, maxDriftMs)returnsnowMs + offsetMs + MAX_DRIFT. It returns null (unclamped) when the record is missing, fails the zod check, or is more than 24 h old (SERVER_CLOCK_TTL_MS).sealer.ts: it computes the ceiling once per seal transaction and ticks each op atmin(at_ms ?? now, ceiling).tickstill takesmax(last.phys, wall), so HLCs stay monotonic.Known limit, as the spec defines it: a recorded offset older than the current push, still under 24 h, can clamp captures after the device's clock is corrected. Push re-records the offset before every seal it runs, so this only affects seals outside push.
Tests (
push.test.ts)sync.server_clockis recorded, and every op's HLC phys is ≤ serverNow + MAX_DRIFT.at_ms(unclamped).Verification
nexus-vault.test.tspasses, 100 of 100.tscis clean, andpush.test.tstypechecks.--base origin/feat/T12343-push), 40 and 32 pass;lint-changesetspasses.🤖 Generated with Claude Code