Skip to content

Harden the reconcile gate + retire shadow-recovery scaffolding (DEPLOY-GATED)#151

Open
Stvad wants to merge 1 commit into
masterfrom
claude/reconcile-gate-hardening
Open

Harden the reconcile gate + retire shadow-recovery scaffolding (DEPLOY-GATED)#151
Stvad wants to merge 1 commit into
masterfrom
claude/reconcile-gate-hardening

Conversation

@Stvad

@Stvad Stvad commented Jun 14, 2026

Copy link
Copy Markdown
Owner

Warning

DO NOT MERGE YET — deploy is order-gated. Merging auto-deploys the client bundle. The hardened gate must NOT ship until the system: zeroing cleanup has run and converged on every device (incl. Weave/e2ee and mobile). See the checklist below.

What this does (the "fully move" off Layout-B recovery scaffolding)

Now safe to do: shadows can no longer form (mints are 0-stamped) and the legacy population was reconciled by the recovery rollout (validated ~0 strictly-newer shadows fleet-wide via the bridge).

  • decideStagingRow: restore newer-OR-equal nonzero-local skip-stale (was equal-only). Under server-enforced updated_at monotonicity an older-or-equal staging stamp is a stale delivery, so a nonzero local row at-or-above it is authoritative — protect it. This kills the transient disk clobber too (not just the cache flash that the prior fix addressed): a strictly-newer local edit facing a stale rescan replay is no longer reverted on disk. The !== 0 exemption keeps the deterministic-id heal + cross-device convergence (0-stamped yields). Restores the protection e7fc79b2 had to relax when speculative defaults were minted with a real now stamp — the 0-sentinel makes it unnecessary now.
  • Retire the recovery machinery: scheduleReconcileRescan / runReconcileRescan / awaitReconcileRescans, the reconcile_rescan_v1: marker (+ test-DB cleanup), and the App.tsx on-open call. The rescan only healed shadows via the indiscriminate gate; with the gate hardened it can't heal nonzero shadows anyway, and there are none left — so it's dead weight (and a per-workspace first-open re-scan cost). drainSyncWorkspace stays (e2ee-unlock re-materialization).
  • Tests flipped to the protected behavior (reconcile / materialize / observer / the ack-to-echo freeze canary); removed the rescan lifecycle suite. yarn run check green (3209 tests).

Why it's deploy-gated

  1. Ordering vs the system: cleanup. That cleanup heals by zeroing (updated_at = 0); a 0-stamped server row only lands on a client whose local copy is nonzero when the gate is indiscriminate. The hardened gate here would skip-stale it (local_nonzero >= 0) → never heals. So the zeroing must run + fully propagate before this merges. (787 system: rows server-side, 779 in the e2ee Weave workspace.)
  2. Unseen devices. A hardened gate permanently strands any still-unhealed nonzero shadow on a device we couldn't inspect (e.g. mobile). Confirm convergence first.

Merge checklist

  • system: zeroing cleanup executed (clamp trigger disabled so 0 lands; e2ee_plaintext_risk = 0 confirmed)
  • All devices drained (ps_crud → 0) and the 787 zeroed rows converged everywhere (incl. Weave + mobile)
  • A final diverged_strictly_newer check on each reachable device returns 0
  • Coordinated drain window scheduled for the deploy

🤖 Generated with Claude Code

… scaffolding

The "fully move" off the Layout-B recovery machinery, now that it's safe:
shadows can no longer form (mints are 0-stamped) and the legacy population was
reconciled by the recovery rollout (validated ~0 fleet-wide).

- decideStagingRow: restore newer-OR-equal nonzero-local skip-stale (was
  equal-only). Under server-enforced updated_at monotonicity an older-or-equal
  staging stamp is a stale delivery, so a nonzero local row at-or-above it is
  authoritative — protect it. This kills the transient DISK clobber too (not
  just the cache flash that 4e4037fd fixed): a strictly-newer local edit facing
  a stale rescan replay is no longer reverted on disk. The `!== 0` exemption
  keeps the deterministic-id heal + cross-device convergence (0-stamped yields).
  This restores the protection e7fc79b had to relax when speculative defaults
  were minted with a real `now` stamp — the 0-sentinel makes it unnecessary now.

- Retire scheduleReconcileRescan / runReconcileRescan / awaitReconcileRescans +
  the reconcile_rescan_v1: marker (clientSchema + createTestDb cleanup) + the
  App.tsx on-open call. The rescan only healed shadows via the INDISCRIMINATE
  gate; with the gate hardened it can't heal nonzero shadows anyway, and there
  are none left — so it's dead weight (and a per-workspace first-open re-scan
  cost). drainSyncWorkspace stays (e2ee-unlock re-materialization).

- Tests flipped to the protected behavior (reconcile / materialize / observer /
  the ack-to-echo freeze canary); removed the rescan lifecycle suite.

DEPLOY-GATED: this is the irreversible-ish step (a hardened gate strands any
still-unhealed nonzero shadow on a device we couldn't inspect, e.g. mobile, and
is incompatible with a future *zeroing* system: cleanup). Land only after a
coordinated drain + confirming every device has converged on the new bundle.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: a38e8e5f82

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

if (
local.localUpdatedAt !== null &&
local.localUpdatedAt === stagingUpdatedAt &&
local.localUpdatedAt >= stagingUpdatedAt &&

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Allow server-clamped echoes to replace fast-clock stamps

When the editing device's clock is ahead, the upload echo can come back with a lower updated_at than the local row: local txs stamp from Date.now(), while blocks_clamp_updated_at future-clamps NEW.updated_at to server time before flooring/bumping (supabase/migrations/20260612000000_add_user_updated_at_monotonic_clamp.sql lines 31-37). With this >= check, that authoritative echo is consumed as skip-stale, leaving the local row pinned at the future stamp; subsequent collaborator/server edits whose server stamps are below that future value will also be ignored until wall-clock time catches up.

Useful? React with 👍 / 👎.

Stvad added a commit that referenced this pull request Jun 17, 2026
One-time §7 cleanup: strip the system: author prefix and re-stamp 787 legacy
pre-c0d606f6 mint rows to the updated_at=0 pristine sentinel, so they
re-replicate and converge every device (incl. shadows on uninspectable clients)
via the gate's stamp-0 exemption. Transactional (clamp+history triggers toggled
off so the 0 lands); backup table for rollback. Must run + converge BEFORE PR
#151 merges. Held for a drain window.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
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.

1 participant