sync: owner-generation fencing for people_scan_dedupe (lever stays 0)
Summary
Make people_scan_dedupe usable for sync spelling hints without cross-request staleness. Every People mutation atomically increments an owner generation document; each cached hint read validates that document once. Fill snapshots capture the generation before scanning, and metadata recreation is fenced by document update time.
Wire the sync attempt opener and known_spellings._people_spellings only. Keep vocabulary and authority lookups live. Keep the lever at 0; no rollout/config/deploy change.
Writer audit includes CRUD, speech samples/profiles, pinning, learning/jobs, auto-enrollment, assignment evidence, chat and Review undo/redo, backfill, and recursive account deletion. Existing accounts without metadata remain live until their next People mutation initializes the generation. Generation maintenance adds one atomic write per People mutation even while reuse is off.
Validation
-
WQ20 fix r2: the failed-Undo canary retry exposed
target.parent.idin the new generic Undo generation check. The strict fixture lacks collection.id; the swallowed failure wasAttributeErrorat the undo stage, with no People generation document created. Carry the validated journal collection with the Undo row instead. The canary test and fixture remain unchanged; cleanup round-trip coverage now asserts exact People generation increments and no non-People bumps. -
R2 preflight:
make preflightandscripts/pr-preflight --pr-body-file body.mdeach passed all 38 selected checks; draft metadata passed all 4 checks. -
R2 regression: 749 passed across all 39
test_dream_*.pyfiles plustest_fsr_people_scan_dedupe.py,test_fsr_scope_opener.py, andtest_fsr_people_generation.py(42 isolated files; no skips/deselections). Canary: 12; other Dream: 665; scan dedupe: 39; scope opener: 20; generation writers: 13. SeeWQ20-fix-r2.mdfor mechanism and commands. -
Review: zcode glm-5.3 VERDICT SAFE, no P1/P2 (exec-leopard-title-logic-blur-merge-profit, 2026-10-26); writer-fence audit of all 23 write sites, reader-admission and cost checks independently verified; reviewer re-ran 91 core + 142 regression + 9 import-lightness via backend/.venv. P3s accepted: generation .get() under memo lock (latency nit), +1 write per People mutation while lever is 0 (standing cost until a separate measured enablement PR), same-uid People transaction serialization (Firestore retry absorbs).
-
Worktree-local Python 3.11 environment installed with
make setup. -
Full
make preflight: 38 checks passed; no check exceptions or skips. -
Core selected suite: 91 passed; existing chat-emulator suite: 4 passed.
-
Targeted
backend/test.shruns cover attempt opener/retry/cancellation, generation and cross-context invalidation, off-path reads, and all touched writer families. -
Local loopback Firestore emulator: 31 checks pass, including external writers, concurrent increments, failed atomic commits, and metadata recreation.
-
Counted reads: ten 100-document hint scans cost 1,000 reads off and 110 on, including generation lookups.
Product invariants affected
none
Failure class
Failure-Class: none
This is a new guarded optimization behind an existing default-off lever. The prior unsafe wiring attempt was closed without production rollout; this change supplies its required concurrency contract and regression proofs.
R2 fixes an internal journal/fixture API dependency exposed by the existing canary regression; no generation or memo leak was reproduced. Failure-Class: none remains applicable.