Cover addresses derived from the bytes, and a store surface that replaces a Cover #150
Reference in New Issue
Block a user
Delete Branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Parent
Spec #135.
What to build
A Cover address is currently the hash of the source URL, and the store only ever fills a blank one. So a Site that re-arts a Series behind the same URL is invisible twice over: the file is not written, the row is not updated, and every Reader holds the old bytes for a week under an immutable cache header. Nothing in the system can tell.
This ticket changes the derivation and adds the surface that uses it. New Cover writes are addressed by the SHA-256 of the bytes. That is what makes a re-art visible at all — the address itself changes, so the wire URL changes, so the Reader's cache is bypassed without touching a cache header — and it makes "the Site is serving the same artwork" an honest, detectable no-op instead of a silent double failure.
Legacy rows are not rehashed, ever. Existing URL-derived addresses stay as they are; the two derivations coexist, and a legacy row heals the first time its Cover is replaced.
Alongside the existing fill-if-blank setter, add a replacing store method taking the same arguments. Both go through the same cover installer and differ only in the update predicate; the existing method keeps its empty-address guard and the existing "a Cover is not overwritten" test must keep passing unchanged — it is the guard proving this change did not leak into the routine path. Rejected: a
force boolparameter — three existing callers would passfalsefor ever, and the first caller passingtruemoves the fill-versus-overwrite decision out of the store and into a call site. The forced update writes the source URL as well as the address, because a re-art may sit behind a new URL.Write the ADR with this implementation. The rule is hard to reverse once data exists under both derivations, and it contradicts the URL-hash statements in the cover-address migration and in the store's own doc comment, which this change must rewrite.
Acceptance criteria
Blocked by
Landed on
spec-135as merge commit forticket/150-cover-bytes-addressing(b9fc832).Implemented:
putCovernow addresses by the SHA-256 of the bytes and returns that address;SetSeriesCoverkeeps itscover_address = ''predicate and is otherwise unchanged;ReplaceSeriesCover(site, seriesID, sourceURL, body, contentType) (previous, current string, err error)differs from it only in that predicate and writes the source URL too; exportedCoverAddress(sourceURL)is gone, replaced byCoverAddressForBytes(body); unexportedcoverSourceAddressstays so legacy URL-derived rows keep resolving throughGetCoverand nothing rehashes them; the0009_series_cover_address.sqlcomment and the store doc comments no longer claim URL derivation; ADR-0014 records the rule, its cost and why it is hard to reverse.The
coverAddressRepath-traversal guard is byte-identical (^[0-9a-f]{64}$) — a byte-derived address has the same shape, so it was not widened.One noted deviation: the pre-existing "a Cover is not overwritten" test could not stay byte-for-byte identical, because deleting the exported
CoverAddressforces one identifier swap in it. The guard it proves is unchanged and the assertion is the same; the two instructions in the brief were literally incompatible. Judged non-blocking.Superseded cover bytes are not reclaimed here — that is #154, and it is documented as out of scope in ADR-0014.
Backend suite green on the merged base: 562 tests, 10 packages.