Repository navigation
feat: stop writing the legacy manifest, add a one-shot migration - #59
Merged
Merged
Conversation
The legacy manifest.json held both producer halves in ONE file. That was unsafe two ways: the update is a read-modify-write, so two producers lose each other's entries, and a file-level sync between two stores resolves the file last-writer-wins and silently discards one side. The second one stopped being theoretical when the Windows producer moved from a redirected drive to a local store, making the two stores separate copies merged by a sync. Nothing writes manifest.json now. `cotdata-update --migrate-manifests` splits an existing one into the per-half files. It is idempotent, entries already in a half file win so a re-run cannot resurrect stale bookkeeping, and it never touches data. Until a store is migrated, a domain absent from the per-half files is still read from the aggregate, with a warning naming the domains and the command to run. THE FALLBACK IS PER DOMAIN, NOT PER HALF. Found by dry-running the migration against a copy of the real store: manifests/prices.json held `prices` but not `metadata`, because the price producer had run on the new code while the metadata producer had not. A per-half rule treated the whole prices half as migrated and hid `metadata` entirely. Verified before and after migration now show the same 241 entries. reconcile_manifest is rewritten to prune each manifest FILE in place rather than the merged view. It previously wrote its result to the legacy aggregate, which would now be a write to a file nothing reads. It prunes the aggregate too when present, so an un-migrated store can still be cleaned. Suite 134 passed. Co-Authored-By: Claude Opus 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.
Completes the second half of ADR-0007 step 1.
Why now rather than after a clean week
The legacy
manifest.jsonheld both producer halves in one file, which was unsafe two ways:The second stopped being theoretical when the Windows producer moved from a
\\tsclient\redirected drive to a local store. That turned one shared store into two copies merged by a sync, and a sync pushing amanifest.jsoncontaining only price entries would drop every COT entry on arrival.The per-half files are disjoint, so a file sync merges them correctly by construction.
What changed
Nothing writes
manifest.json._touch_manifestwrites only the owning half.cotdata-update --migrate-manifestssplits an existing aggregate into the per-half files. Idempotent, entries already in a half file win so a re-run cannot resurrect stale bookkeeping, and it never touches data.Until a store is migrated, a domain absent from the per-half files is still read from the aggregate, with a warning naming the domains and the command to run.
reconcile_manifestrewritten to prune each manifest file in place rather than the merged view. It previously wrote its result to the legacy aggregate, which would now be a write to a file nothing reads. It still prunes the aggregate when present, so an un-migrated store can be cleaned without migrating first.A bug caught by dry-running against the real store
My first version made the fallback per half. Dry-run on a copy of the live store:
manifests/prices.jsonheldpricesbut notmetadata, because the price producer had run on the new code while the metadata producer had not. A half file existing does not mean the half is complete, so treating it as migrated hidmetadataentirely until the migration ran.The fallback is now per domain. Same store, after the fix:
241 entries either side. There is a regression test for it.
Verification
Migration dry-run on a copy of the live store moved 147 entries (
cot +146,prices +1) with nothing lost. Suite 134 passed, ruff clean.Operator note
Run once per store after upgrading:
Two stores means running it on both.
manifest.jsoncan be deleted once every consumer of that store is on this version.