Skip to content
go-libfossil Check-in [6083693442]
Login

Check-in [6083693442]

Overview
Comment:fix(sync): keep a refused seed's bytes; record the refusal only (#471)

Final review of c077fa4. F.1 requires recording a refusal, not deleting the bytes, and every finding came from the deletion:

- A delta built on a swept seed lost its basis, was withheld forever, and could not be re-pushed, because it counted as held. - The end-of-sync sweep ran outside a transaction. - A refused hash pulled from an upstream was dropped, which is a receive-side behaviour F.1 does not have.

So a refused seed stays stored: - Its hash is recorded in seedrefused. - It leaves unclustered, so no inventory or cluster names it. - It is withheld from every reply, and the link step never links it. - Deltas on it stay readable and are judged like any other pending row. - Pulling the same hash stores nothing new: the row is held, and stays refused. - A re-push of it is refused on its bytes. Every form (full text, delta on a held basis, delta whose own row is held) expands to the seed.

Removed: - Sweep, sweepRow and its no-root marking. - Every Sweep call: handler request end, end of sync, check-in, tag and rebuild. - requestPhantoms' refused-hash filter (a refused seed is held, so never a phantom). - ownSeedReadable's refused-hash check and its Clear (the fill's JudgeFilled already clears a non-seed). - JudgeFilled's lazy re-root, which nothing reached: only full text fills a root JudgeFilled hears of.

Kept, each pinned by a test: - Pending rows keyed by chain root, with MarkPending moving the rows waiting on a rid when that rid is itself pushed as a dangling delta. Filling a phantom with a delta triggers no cascade, so this is the re-root that matters. - JudgeFilled examining only its subtree, and its hash-mismatch skip. - The link-step judge and the refused-hash check in it. - The push-time atomic refusal. - Withholding pending and refused rows, and Purge forgetting pending rows (rids are reused).

Only pushed dangling deltas are marked pending. A dangling delta stored by a pull onto a pending row's basis is not, so rows waiting on it stay withheld until the link step reaches them; a plain file among them stays withheld.

Tests: - The reviewer's probe, for a control artifact and a plain file built on a pending seed, pushed after it and before it: after the base arrives both are readable and not withheld, and the control artifact is linked and served. - A plain file after a forged delta is still cleared. - Every judge, filter and mark fails a test when disabled, except two performance mechanisms no behaviour shows: the per-transaction table cache and IsSeedBytes' byte prefilter.

Timelines: family | ancestors | descendants | both | main | trunk
Files: files | file ages | folders
SHA3-256: 60836934427c85fd3556ff9f0797aebe856a00eae2f33218f44170546072475a
User & Date: [email protected] on 2026-10-24 04:58:32.000
Other Links: branch diff | manifest | tags
Context
2026-10-24
05:58
fix(sync): never announce a withheld artifact from the client side (#471)

Review of 0e28b6a: when the origin pushed on to a peer, its own client announced artifacts its replies withhold. A pending seed went out from unclustered, and after verify.Rebuild (which marks every blob unclustered and unsent again) a refused seed went out too. The peer kept asking, loadFileCard withheld it, and the push ran out of rounds.

Fixed in the tables the client announces from, not in each reader: - MarkPending takes a pending row out of unclustered. - decide puts it back once judged not a seed; a seed stays out. - rebuildBookkeeping leaves withheld rids out of unclustered and unsent. - That also keeps them out of cluster minting, which reads unclustered. - sendPrivate and the private gimme answer, which read the private table, skip withheld rids. - sendUnclustered needs no filter of its own, and sendAllClusters lists only linked clusters, which a withheld row never is.

JudgeFilled now skips a row that does not expand (it stays pending) and judges the rest of the batch, instead of returning.

Tests: - A pending seed is not announced on a push-on, publicly or privately. - A peer asking for a private pending seed outside a private sync is not told it exists. - After a rebuild, a push-on converges and never names the refused seed. - A pending file, once judged, is announced again on a push-on. - An unreadable row does not stop the next fill in the batch being judged. - Every new mechanism fails a test when disabled. check-in: df78b8aee9 user: [email protected] tags: main, trunk

05:21
Merge feat/471-seed-refusal into feat/seed-receive-drop

Takes feat/471's no-delete model: a refused seed stays stored, refused by its hash, and is never linked or served. Its effect on the receive side:

- A seed a pull or clone can read on arrival is still never stored. - One completed in a later round is refused by the link step and kept, inert, rather than swept. The clone's sweep is gone. - Refusing a seed at the link step takes it out of unclustered, as a push-side refusal does. - A dropped seed's phantom row stays inert, since deleting it would free its rid under a receive pass. loadDBPhantoms skips refused hashes, so it is never asked for again. - The client's addPhantom filter is gone: a refused hash always has a row by then. - The handler keeps feat/471's recordStored, which refuses a re-push whose row is already held.

A test now pins the client's verified-delta refusal (a seed sent as a delta on a held basis), and seeds dropped on receipt are asserted not stored (assertDropped). check-in: 5b43d4fe38 user: [email protected] tags: feat/horizon-dfloor-receive, main, trunk

04:58
fix(sync): keep a refused seed's bytes; record the refusal only (#471)

Final review of c077fa4. F.1 requires recording a refusal, not deleting the bytes, and every finding came from the deletion:

- A delta built on a swept seed lost its basis, was withheld forever, and could not be re-pushed, because it counted as held. - The end-of-sync sweep ran outside a transaction. - A refused hash pulled from an upstream was dropped, which is a receive-side behaviour F.1 does not have.

So a refused seed stays stored: - Its hash is recorded in seedrefused. - It leaves unclustered, so no inventory or cluster names it. - It is withheld from every reply, and the link step never links it. - Deltas on it stay readable and are judged like any other pending row. - Pulling the same hash stores nothing new: the row is held, and stays refused. - A re-push of it is refused on its bytes. Every form (full text, delta on a held basis, delta whose own row is held) expands to the seed.

Removed: - Sweep, sweepRow and its no-root marking. - Every Sweep call: handler request end, end of sync, check-in, tag and rebuild. - requestPhantoms' refused-hash filter (a refused seed is held, so never a phantom). - ownSeedReadable's refused-hash check and its Clear (the fill's JudgeFilled already clears a non-seed). - JudgeFilled's lazy re-root, which nothing reached: only full text fills a root JudgeFilled hears of.

Kept, each pinned by a test: - Pending rows keyed by chain root, with MarkPending moving the rows waiting on a rid when that rid is itself pushed as a dangling delta. Filling a phantom with a delta triggers no cascade, so this is the re-root that matters. - JudgeFilled examining only its subtree, and its hash-mismatch skip. - The link-step judge and the refused-hash check in it. - The push-time atomic refusal. - Withholding pending and refused rows, and Purge forgetting pending rows (rids are reused).

Only pushed dangling deltas are marked pending. A dangling delta stored by a pull onto a pending row's basis is not, so rows waiting on it stay withheld until the link step reaches them; a plain file among them stays withheld.

Tests: - The reviewer's probe, for a control artifact and a plain file built on a pending seed, pushed after it and before it: after the base arrives both are readable and not withheld, and the control artifact is linked and served. - A plain file after a forged delta is still cleared. - Every judge, filter and mark fails a test when disabled, except two performance mechanisms no behaviour shows: the per-transaction table cache and IsSeedBytes' byte prefilter. check-in: 6083693442 user: [email protected] tags: main, trunk

03:44
fix(sync): refuse pushed seeds by hash; judge only what a fill grounds (#471)

Horizon F.1, push side, and the fourth review's blocking finding.

Refusal by hash: - seedcheck keeps two store-local tables, carried by no config group: seedpending(rid, root) and seedrefused(uuid). - A seed judged after the request that pushed it, because a later push, pull, check-in or tag filled its basis, has its hash refused. Its rows are deleted by Sweep, and only where no receive pass holds rids: end of request, end of sync, check-in, tag and rebuild. A deleted rid is reused by the next row, and a pass's caches are keyed by rid. - Until swept, such a row is withheld and never linked. A delta on a refused seed is left unreadable and pending under no root. - requestPhantoms never asks for a refused hash. ownSeedReadable refuses a re-push of one, and a full-text re-push is refused on its bytes. - A purge still forgets pending rows (ForgetDeleted). Refusals are keyed by hash, so rid reuse cannot pass them on.

Judging only what a fill grounds: - JudgeReadable rescanned every pending row on each fill. A pusher could plant dangling deltas without bound and make every later fill pay for all of them. - Pending rows now record the root of their basis chain, re-rooted when that root is itself stored as a dangling delta. - JudgeFilled judges only the rows pending on the rids just stored, and reports how many it examined. - Callers: AfterDephantomize and its batch, manifest.Checkin (its files and manifest) and tag.AddTag. linkArtifact still judges a pending row it reaches, caching which tables exist per transaction. - The sweep no longer judges.

A seed-shaped artifact received by pull or clone stays ordinary, per the 0.7.1 B.2. Docs and the HZ-5.2-4 scope note cite F.1.

Tests: - A seed package test: one fill examines its own row, not 200 planted elsewhere. Run as a rescan mutant, it fails. - A tag whose control artifact is a seed's basis refuses the seed. - The link step refuses a seed made readable by a raw store. - A re-push refused by hash, and an igot of the hash not gimme'd. - A delta on a refused seed withheld. - A purged pending rid reused by an innocent blob. - Disabling any judge call (cascade, check-in, tag, link) or any Sweep call (request, sync, check-in, tag) fails a test. check-in: ed5e3ce5b5 user: [email protected] tags: main, trunk

Changes