fix(ownership): update syncs when a same-date snapshot is rebuilt

A snapshot republished for the same as-of date (the TRUE ticker fix)
kept the date, latest release, and release count, so sync saw nothing
to do and existing installs kept the bad data without --force.

The manifest gains an optional snapshot.replaces_sqlite_sha256 listing
earlier builds for that date. Queries never write to a synced file, so
a local database byte-identical to a listed build is replaced; one that
matches nothing (local imports or edits) still needs --force. The
publisher fills the list on a same-date rebuild. Older clients ignore
the field.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
hermes 2026-10-14 18:52:13 +00:00
commit ec17a1bd05
5 changed files with 191 additions and 2 deletions

View file

@ -79,7 +79,9 @@ The publisher treats the published snapshot as current only when the manifest's
`download_url` names this repo and tag and the referenced asset matches its
SHA-256 and size. A missing or corrupt asset, a mismatched URL, an unreadable
manifest, or any GitHub auth/network error means "publish", never "up to date".
`--force` uploads regardless.
`--force` uploads regardless. A forced rebuild for the already-published as-of
date adds the replaced build's `sqlite_sha256` to `replaces_sqlite_sha256`
(keeping the last 20), so clients still holding it update on their next sync.
## Run Result

View file

@ -48,7 +48,8 @@ Current schema version: `1`
"latest_as_of_date": "2026-02-27",
"latest_release_sha256": "<latest ownership_releases.sha256>",
"latest_row_count": 7261,
"ticker_count": 955
"ticker_count": 955,
"replaces_sqlite_sha256": ["<sqlite_sha256 of an earlier build for this date>"]
}
}
```
@ -62,6 +63,9 @@ Semantics:
follow `download_url` and validate it with `sqlite_sha256` and `size_bytes`.
- `sqlite_sha256` and `size_bytes` are validated before install.
- `latest_*` and `release_count` are validated against the downloaded SQLite contents before replacement.
- `replaces_sqlite_sha256` (optional, omitted when empty) lists earlier builds
published for the same `latest_as_of_date` that this snapshot supersedes, for
example after a parser fix. Clients up to v0.2.x ignore it.
## Replacement Rules
@ -70,6 +74,9 @@ Without `--force`, sync behaves conservatively:
- no local DB: install snapshot
- empty local DB: replace with snapshot
- local latest release older than snapshot: replace with snapshot
- local DB file is byte-identical to `sqlite_sha256`: no-op
- local DB file is byte-identical to a build in `replaces_sqlite_sha256`: replace
(queries never write to a synced file, so this means it was not modified)
- local latest release/date/count matches snapshot: no-op
- local latest release matches but has fewer releases than snapshot: replace to fill missing history
- local DB newer than snapshot: no-op and keep local data