50 descendants of 584c55d1fd945737
|
2026-10-26
| ||
| 00:59 |
test(conformance): hand each case its subtest's t; cancel stale Race runs
check runs each case as a subtest, but the suites passed the case the parent's t, captured in a closure. A fixture that skips or fails, as the fossil-built R-card fixture skips when fossil(1) is absent, then called SkipNow or FailNow on the parent from inside the subtest, which Go fails as "subtest may have called FailNow on a parent test". The Race workflow installs no fossil and failed this way in TestSparseCone. check now hands conforms the subtest's t, and all 150 call sites take it, so a case's skip or failure ends that case alone. TestCheckSkipEndsOnlyTheCase guards the harness. A Race run takes most of an hour and merges land faster, so a new run on a ref now cancels the older one. check-in: 43df2fd2c2 user: [email protected] tags: fix/conformance-subtest-t, main, trunk | |
| 00:54 |
Merge pull request #502 from danmestas/docs/sdk-reference-check
docs: regenerate the SDK reference and check it in CI check-in: 51d3b9cf5f user: [email protected] tags: main, trunk | |
| 00:46 |
docs: regenerate the SDK reference and check it in CI
The gomarkdoc SDK pages had drifted about a thousand lines from the code: make docs-gen-sdk was never part of docs-gen, so nothing regenerated them and the docs check did not compare them. ReadBlob, SetContentCache, ListFilesIn and OriginNamer were missing from the libfossil page. scripts/gen-sdk-docs.sh now renders them, running gomarkdoc v1.1.0 through go run so CI needs no install, then adds the frontmatter. It honours OUT_DIR, gen-docs.sh calls it, and check-docs-generated.sh compares the result, so a stale SDK page fails CI like any other generated input. make docs-gen-sdk calls the same script. llms.txt and llms-full.txt are regenerated from the refreshed pages. check-in: c12b02189f user: [email protected] tags: main, trunk | |
| 00:11 |
Merge pull request #501 from danmestas/perf/submanifest-node-memo
perf(manifest): memoize parsed sub-manifests by hash check-in: d82fddda6a user: [email protected] tags: main, trunk | |
| 00:02 |
perf(manifest): memoize parsed sub-manifests by hash
draft-fossil-submanifests-00 §7.4: enumeration results SHOULD be cached keyed by artifact name, so a rebuild parses each distinct sub-manifest once. The tree walk's loader now serves a resolved node from, and keeps it in, a new byte-bounded LRU memo on content.Cache (DerivedSized), keyed by the node's hash and charged its bytes, within a quarter of the cache's budget. The existing 32-entry Derived FIFO is sized for a few parsed check-ins; a tree's working set is every distinct node, which would thrash it and evict those check-ins. Only a node that resolved is kept, so a phantom is looked up afresh and fails the walk as before. Depth, cycle and node-count bounds stay per walk. The memo is dropped with Derived on Invalidate and Clear, and the Repo's cache is replaced at every write. Listing a tree that differs from one already read in one leaf now parses its manifest and that leaf; listing every check-in of zit's recorded tree history parses 48 sub-manifests instead of 109. SM-7.4-1 is covered. Tracked in #493. check-in: 94f61db228 user: [email protected] tags: main, trunk | |
| 00:01 |
Merge pull request #500 from danmestas/feat/list-files-in-cone
feat: list a check-in's files in a cone check-in: e90c972c40 user: [email protected] tags: main, trunk | |
|
2026-10-25
| ||
| 23:53 |
feat: list a check-in's files narrowed to a cone
Repo.ListFilesIn passes the public Cone to the enumeration seam as the range predicate (draft-fossil-sparse-cone-00 §4.1). On a tree manifest the walk skips sub-manifests the cone cannot reach (§4.5), so over a sparse store it succeeds where ListFiles fails on an elided node. It reads through the attached content cache, via a new internal manifest.ListFilesFilteredCached. Tracked in #491. check-in: 7156492179 user: [email protected] tags: main, trunk | |
| 23:44 |
Merge pull request #499 from danmestas/ci/race
ci: run the unit suite under -race; give packages 10m check-in: 612977a2d6 user: [email protected] tags: main, trunk | |
| 23:37 |
ci: run the unit suite under -race; give packages 10m
A Race workflow runs every unit package under the race detector after each merge to main, nightly, and on demand. The full suite passes under -race with no data race reported; it takes several times as long as without, so it does not gate pull requests, which already must be up to date with main to merge. Fossil and zit are not installed there; their interop tests skip, and the Tests workflow runs them. The unit steps' -timeout is per package binary, and conformance alone takes 130-255s on a hosted runner, so 300s failed it on runner variance. It is now 10m. The dst steps keep 300s. check-in: fdd361b5a9 user: [email protected] tags: main, trunk | |
| 23:36 |
Merge pull request #498 from danmestas/fix/horizon-cursor-embedder-transport
fix(sync): export OriginNamer so an embedder's transport keeps a horizon cursor check-in: a52911df25 user: [email protected] tags: main, trunk | |
| 23:29 |
fix(sync): export OriginNamer so an embedder's transport keeps a horizon cursor
Sync and Clone keyed the horizon cursor (draft-fossil-horizon-00 §6.2.6) on an unexported syncOrigin method, which only the HTTP transport could satisfy. Any other Transport kept no cursor and announced its whole holding with igot on every pull. OriginNamer is an optional interface, discovered by type assertion like FramedTransport, with SyncOrigin() string. The HTTP transport implements it in place of the unexported method, so one assertion serves both. The name must identify the peer stably and carry no credential. Tracked in #488. check-in: 8d16b7289c user: [email protected] tags: main, trunk | |
| 23:28 |
Merge pull request #496 from danmestas/feat/read-blob-content-cache
feat: read an artifact by hash and cache content on Repo check-in: 5e882bf1f1 user: [email protected] tags: main, trunk | |
| 23:10 |
feat: read an artifact by hash and cache content on Repo
Repo.ReadBlob expands one artifact by its full hash after the availability check ReadFile applies, and names an unknown hash with ErrArtifactNotFound. Repo.SetContentCache attaches a byte-bounded content.Cache that ReadFile, ReadFileAt, ReadBlob, ListFiles, Diff and Annotate share; check-in parses are kept in its derived memo, so reads at one check-in parse the manifest once. Default off. A read that finds the DB's finished-write count moved swaps in an empty cache, so a purged, compacted or elided artifact, and a rid SQLite reuses after a purge, is never served from it. Writes by another process are not seen; the method's doc says so. Tracked in #492. leaf check-in: d367012009 user: [email protected] tags: feat/read-blob-content-cache, trunk | |
| 23:10 |
feat(db): count finished writes
DB.Writes counts pool Exec calls that returned and WithTx transactions that ended. Counting at the end is what lets a reader holding state across calls treat an unchanged count as no write having landed in between. check-in: 51be37dc1a user: [email protected] tags: feat/read-blob-content-cache, trunk | |
| 23:10 |
Merge pull request #497 from danmestas/ci/gofmt
ci: check gofmt; format the tree check-in: 969c8f1fe4 user: [email protected] tags: main, trunk | |
| 23:02 |
ci: check gofmt; format the tree
Sixteen files had drifted from gofmt: struct and comment alignment, one-line if bodies in auth_test.go, trailing blank lines, and a blank comment line gofmt now puts before an indented block in two delta.go doc comments. The test job now fails on any tracked .go file gofmt would change, and lists it. check-in: b2dcbb7130 user: [email protected] tags: main, trunk | |
| 22:36 |
Merge pull request #495 from danmestas/ci/zit-0.7.3
ci: pin zit interop to 0.7.3 check-in: 95610ffc23 user: [email protected] tags: main, trunk | |
| 22:27 |
ci: pin zit interop to 0.7.3
zit 0.7.3 (version-0.7.3, check-in f18e3cc041) carries draft check-in 39546186f4, the minted seed floor of horizon §5.2.2 that go-libfossil implements since #485, so the pinned interop zit and the pinned draft copy agree again. Every zit interop test passes against it, none skipped, with REQUIRE_ZIT_BIN and REQUIRE_ZIT_SEED_DATE set. check-in: 10737ceaec user: [email protected] tags: main, trunk | |
| 22:23 |
Merge pull request #494 from danmestas/feat/carried-uuid-default-login
feat(commit): carry files by UUID; fix Create's default login check-in: d0abc1b3fa user: [email protected] tags: main, trunk | |
| 21:08 |
fix: validate carried hashes and check FOSSIL_USER first
A carried file outside a sparse store's cone skips the held check, so any caller text of 40 bytes or more reached the F card verbatim, including a newline that writes a card of its own into the check-in. checkCarried now refuses a UUID that is not a lowercase SHA1 or SHA3-256 hash, which covers FileToCommit.UUID and the sparse checkout path alike (tracked in #486). The default login follows fossil's db_create_default_users, which checks FOSSIL_USER before USER, LOGNAME and USERNAME (tracked in #487). leaf check-in: 2237a3781a user: [email protected] tags: feat/carried-uuid-default-login, main, trunk | |
| 21:05 |
fix(create): seed fossil's fallback login when no user is configured
Create with an empty CreateOpts.User read only $USER and then panicked in db.SeedUser on the empty login whenever that variable was unset, as it is in many containers and CI runners. The default login is now chosen as fossil(1)'s db_initial_setup chooses it: the first of USER, LOGNAME and USERNAME that is set, else "root". check-in: c15a6c60e9 user: [email protected] tags: feat/carried-uuid-default-login, main, trunk | |
| 21:05 |
feat(commit): carry a file into a check-in by UUID without its content
FileToCommit gains a UUID field. When set, the F card names that artifact verbatim and nothing is read or stored, reaching the internal manifest.File.UUID path that sparse checkouts already use. A caller that lists a parent's files can now commit with PartialManifest, dropping files, by naming the unchanged part of the tree instead of re-reading and resubmitting every byte of it. Both UUID and Content on one file is refused; so is a UUID the store does not hold outside an eliding cone. check-in: 37bba3f7b9 user: [email protected] tags: feat/carried-uuid-default-login, main, trunk | |
| 21:04 |
fix(create): seed fossil's fallback login when no user is configured
Create with an empty CreateOpts.User read only $USER and then panicked in db.SeedUser on the empty login whenever that variable was unset, as it is in many containers and CI runners. The default login is now chosen as fossil(1)'s db_initial_setup chooses it: the first of USER, LOGNAME and USERNAME that is set, else "root". leaf check-in: 229c02160d user: [email protected] tags: fix/create-default-login, main, trunk | |
| 21:00 |
feat(commit): carry a file into a check-in by UUID without its content
FileToCommit gains a UUID field. When set, the F card names that artifact verbatim and nothing is read or stored, reaching the internal manifest.File.UUID path that sparse checkouts already use. A caller that lists a parent's files can now commit with PartialManifest, dropping files, by naming the unchanged part of the tree instead of re-reading and resubmitting every byte of it. Both UUID and Content on one file is refused; so is a UUID the store does not hold outside an eliding cone. leaf check-in: 481ac23eaf user: [email protected] tags: feat/commit-carried-by-uuid, main, trunk | |
|
2026-10-24
| ||
| 19:19 |
Merge pull request #485 from danmestas/feat/horizon-seed-floor
fix(horizon): date a seed no earlier than the origin's minted floor check-in: d47958493b user: [email protected] tags: main, trunk | |
| 19:04 | fix(horizon): read a NULL minted floor as none and replace it leaf check-in: 387b9de549 user: [email protected] tags: feat/horizon-seed-floor, trunk | |
| 19:03 |
fix(horizon): keep the minted floor best effort
A mint wrote the floor on every structural pull and failed the request when it could not, so a read-only mirror or a store whose write lock was held no longer served the structural horizon. The floor is now written only when D moves later, and a failed write is ignored: the mark is a SHOULD, and without it the next seed is dated from the tag rows as before. A stored value that is not a canonical integer, which an overflowing cast would rank above every D, is replaced. check-in: 8356d3d19e user: [email protected] tags: feat/horizon-seed-floor, trunk | |
| 18:56 |
fix(horizon): date a seed no earlier than the origin's minted floor
Shunning and purging the latest tag event removed the tagxref rows a seed's D was dated from, so the next seed was earlier than the one every structural clone held. Each clone's D floor refused it as a replay and it fell back to the content horizon until the repository's tags caught up with the purged tag's time; a far-future tag held it there indefinitely. The draft (zit 39546186f4, §5.2.2) now has an origin date a seed no earlier than the latest D it has minted and keep that D across restarts. Mint reads the mark from the store's horizon-seed-floor config row, dates the seed at the later of it and the latest tag event, and raises the row after minting with a conditional upsert, so concurrent mints never lower it. No config group transfers the row, so it is never sent. Re-pins draft-fossil-horizon-00 at 39546186f4 and adds the conformance case horizon.5.2.2.seed-date-shun, which fails without the floor. check-in: 1fd0dfdbc1 user: [email protected] tags: feat/horizon-seed-floor, trunk | |
| 18:36 |
Merge pull request #484 from danmestas/release/v0.16.1
chore(release): v0.16.1 check-in: 4ac6baf2ea user: [email protected] tags: main, trunk, v0.16.1 | |
| 18:28 | chore(release): v0.16.1 changelog check-in: 08d681d6c3 user: [email protected] tags: main, trunk | |
| 16:47 |
Merge pull request #482 from danmestas/feat/horizon-dfloor-receive
feat(horizon): re-pin to zit caeef421ee: repository-wide seed D and the receive rule check-in: 7b6f43f1dc user: [email protected] tags: main, trunk | |
| 16:39 |
ci: pin zit interop to 0.7.2 and require the seed-date test
zit 0.7.2 (tag version-0.7.2, check-in 1dcb2d4a29) dates a seed at the repository's latest tag event, as go-libfossil now does. Its horizon draft is the caeef421ee text pinned here (artifact ab18fa7f89). - ZIT_VERSION is 0.7.2, and ZIT_SHA256 is the checksum of zit-0.7.2-linux-x86_64.tar.gz, checked against the release's SHA256SUMS. - REQUIRE_ZIT_SEED_DATE=1 sits next to REQUIRE_ZIT_BIN, so TestHorizonSeedDateZit cannot skip in CI. - The README, horizons.md, CHANGELOG and test comments now say interop is pinned to zit 0.7.2. check-in: 0c30e0d32e user: [email protected] tags: feat/horizon-dfloor-receive, main, trunk | |
| 14:45 |
test(conformance): seed byte-identity with zit over a cancelled tag
TestHorizonSeedDateZit has zit mint the seed for a repository whose newest tag event is a cancel, and requires it to match go-libfossil's byte for byte. Both now date a seed at the repository's latest tag event (§5.2.2). No string in the binary tells a zit from before caeef421ee apart: its version still reads 0.7.1. So the test recognises an older zit by an earlier D in an otherwise identical seed, and skips it unless REQUIRE_ZIT_SEED_DATE=1. CI's pinned zit 0.7.1 skips. A zit built at caeef421ee passes with the variable set. leaf check-in: ad82b4a46d user: [email protected] tags: feat/horizon-dfloor-receive, main, trunk | |
| 14:34 |
Merge feat/seed-receive-drop; re-pin horizon draft to zit caeef421ee
Re-pins draft-fossil-horizon-00 to zit trunk check-in caeef421ee (file artifact ab18fa7f89), which changes §5.2.2, §5.2.3 and §6.2.3 and adds two Appendix C bullets. This brings in the local feat/seed-receive-drop branch, which implements the receive rule, and resolves it against main (#477 chain-walk bounds, #478 seed binding). §5.2.2, the seed's D (tracked in #479): - Mint dates a seed at max(tagxref.mtime) over every row in the repository, cancels and propagated rows included, as zit's latestTagEvent does. It was the latest of the rows the seed summarizes. - A cancel of the newest tag, or a reopened closed branch, therefore no longer mints a seed earlier than the one a clone holds, which the clone's D floor refused as a replay. §5.2.3, the receive rule: - A pull, clone or sync drops a seed by this store's label or by a binding card when it can read it, and the link step refuses one completed later. - The draft also requires that a delta kept without its base, pushed or received, is not announced or served while it cannot be expanded. feat/seed-receive-drop only withheld pushed ones. The pending mark moves into storeDeltaContent, so a pull marks too, and a clone marks what is still unreadable when it ends (seedcheck.WithholdUnreadable). That avoids judging every dangling file delta of a clone a second time. - Since #478 minted seeds carry a binding card. The seed-shaped-ordinary cases therefore strip it, and a new test checks that a seed with the card is never linked. Conformance rows: - HZ-5.2-2 takes the new D text. - HZ-5.2-4 loses the old ordinary-artifact sentence. - HZ-6.2-6 says the D is fixed by the repository's tag events. - New rows: HZ-5.2-14 (the receive-path drop; PARTIAL, since a seed completed in a later round is kept inert, and was counted when its delta arrived), HZ-5.2-15 (the dropped-hash record) and HZ-5.2-16 (withholding unexpandable deltas). - New cases: two D cases mirroring zit's "horizon seed date" regression, a cancel on C and a cancel at an ancestor below it, and five receive cases. - The deepen-floor case now replays an older seed, since an honest origin no longer mints one. check-in: 5d0ef566aa user: [email protected] tags: feat/horizon-dfloor-receive, main, trunk | |
| 13:58 |
Merge pull request #481 from danmestas/release/v0.16.0
chore(release): v0.16.0 check-in: 2f1e5f1e3d user: [email protected] tags: main, trunk, v0.16.0 | |
| 13:50 | chore(release): v0.16.0 changelog check-in: 3126bf8e03 user: [email protected] tags: main, trunk | |
| 13:50 |
Merge pull request #480 from danmestas/ci/zit-interop
ci: install pinned zit 0.7.1 so the zit interop tests run check-in: ff2929c770 user: [email protected] tags: main, trunk | |
| 13:42 |
ci: install pinned zit 0.7.1 so the zit interop tests run
The zit interop and horizon-seed tests compared bytes with a real zit only on machines that had one; CI installed none, so every one of them skipped there. The modernc job now installs zit's released 0.7.1 Linux tarball, pinned by its SHA256SUMS entry as fossil is, and runs the unit tests with REQUIRE_ZIT_BIN=1 so a missing or outdated zit fails the run. check-in: 4f2abff4ec user: [email protected] tags: main, trunk | |
| 13:29 |
Merge pull request #478 from danmestas/feat/horizon-seed-binding
feat(horizon): bind each seed to its horizon and sign seeds apart check-in: b35d577a54 user: [email protected] tags: main, trunk | |
| 13:16 | Merge branch 'main' into feat/horizon-seed-binding check-in: 707b42538c user: [email protected] tags: main, trunk | |
| 13:16 |
feat(horizon): bind each seed to its horizon and sign seeds apart
Every seed an origin mints now carries T +horizon-seed <C> <rule> (draft-fossil-horizon-00 §5.2.1), inside the signed bytes. The card is not counted as a tag in force, Mint never derives a horizon-seed row into a seed, and Apply skips the card. Before a clone holds a seed, signed or not, it checks the seed against its store cone (§5.2.4). The seed must carry exactly one binding card naming the store's C and rule, and its D must be no earlier than the held seed's. A seed that fails is refused as a wrongly signed one and the clone falls back to the content horizon. A deepen discards the held seed first, so the floor restarts at the new C. A held seed that is no longer bound is discarded when it is next applied. Seeds are signed with their own horizon-seed-command and are no longer signed by clearsign and pgp-command. SSH seed signatures use the fossil-horizon-seed namespace. clearsign.Key.Verify now takes the namespace, and check-in verification keeps fossilscm. Adds 9 conformance cases and flips HZ-5.2-8, HZ-5.2-11, HZ-5.2-12 and HZ-5.2-13 to covered. The zit interop gate moves to zit 0.7.1, whose seeds are byte-identical with ours. check-in: 451d368554 user: [email protected] tags: main, trunk | |
| 13:16 |
Merge pull request #477 from danmestas/fix/bound-chain-walks
fix(sync): bound basis-chain walks at the read depth check-in: 8d3b1ef28a user: [email protected] tags: main, trunk | |
| 13:08 |
fix(sync): bound basis-chain walks at the read depth
Any pusher could hold the write lock about 30s per push with a two-delta cycle. The receive pass's basis-chain walks, and the horizon seed check's pending-row walk, ran to 1<<24 steps before giving up. All of them now stop at content.MaxDeltaChainDepth, the depth at which content.Expand stops, so no walk outlasts what a read could ever use. What crossing the bound means at each site: - chainEnd (rootOf, for a dangling delta being stored) keeps a visited set. A chain that loops is refused, as it was, but at the revisit rather than after 1<<24 steps. A chain longer than the bound returns errChainTooDeep. storeDeltaContent then stores the delta untracked: the push succeeds, as a push onto a chain between 16k and 16M deep always did, and since no basis can make the delta readable there is nothing to check against its name later. Storing matches the current behaviour, and an honest transfer never fails for a deep chain it did not build. - verifyFrom's cascade carries each row's depth from the basis that arrived, its own chain included. It does not check, or descend past, a row deeper than the bound: no read can produce those bytes, so the pass owes them no check. A delta that does not hash to its name past the bound no longer aborts the pass. - pathRows loses its step counter. Its visited marks already stop it at a row on a path, so it visits each row once, and the cascade's depth check bounds what it verifies. - seedcheck's chainRoot gives a chain that loops or runs past the bound no root, consistent with ErrChainUnresolvable: the row stays pending, withheld, and is never judged. Tests: - Closing a two-delta cycle is refused in under 5s (it took 27s). - A push onto a chain one past the bound is stored. - chainEnd stops exactly at its limit. - A dangling delta with a wrong name one node past the bound does not abort the push, whether the basis that arrives is full text or a delta deep on a held chain, while one within the bound is still checked and readable. - MarkPending gives an over-deep chain no root, so filling its far end examines nothing, and returns at once on a cycle. - TestHorizonOverDeepPendingChainDoesNotBlockFill still passes. - Removing each bound, the cycle check, the untracked store or the root depth fails a test. - chainDepth's own early stop is an equivalent mutant: the cascade's root has just been expanded, so its chain is within the bound unless the store is damaged. check-in: eac16cf7ed user: [email protected] tags: main, trunk | |
| 12:32 |
Merge pull request #476 from danmestas/feat/horizon-names-seed-refusal
feat(horizon): named horizons with depth, and origin seed refusal check-in: 5a7c2ba12e user: [email protected] tags: main, trunk | |
| 12:11 | docs(conformance): note the horizon pin matches zit's version-0.7.1 tag check-in: cc09488fc6 user: [email protected] tags: main, trunk | |
| 11:42 | Merge branch 'feat/471-seed-refusal' into feat/horizon-names-seed-refusal check-in: 928524bd8c user: [email protected] tags: main, trunk | |
| 11:10 | Merge branch 'feat/471-seed-refusal' into feat/seed-receive-drop check-in: b6e785b914 user: [email protected] tags: feat/horizon-dfloor-receive, main, trunk | |
| 11:10 |
fix(sync): an unresolvable pending chain no longer blocks a fill (#471)
Review of a067c6e. JudgeFilled treated content.Expand's chain-bound error as a store fault and returned it, and DeltaStored failed storeDeltaContent with it. A pusher could plant a dangling chain longer than maxDeltaChainDepth under a basis R, building it by re-rooting. After that, every honest push, pull or clone that supplied R as a delta failed for good. Fix: - content now wraps its cycle and depth errors in ErrChainUnresolvable, and exports MaxDeltaChainDepth. - seedcheck counts ErrChainUnresolvable as unreadable, alongside a hash mismatch and a delta that does not apply or fails its checksum. Such a row is skipped and stays pending, so it stays withheld. - Real faults are still returned, such as content that does not decompress. - The classification uses errors.Is on the sentinel, so the expansion JudgeFilled already performs is not repeated. Tests: - TestHorizonOverDeepPendingChainDoesNotBlockFill plants a pending row one node past the bound under R, then pushes R as a delta on a held basis. The push succeeds, R is stored and readable, and the over-deep row stays withheld. - TestUnresolvableChainSentinel pins the sentinel for a cycle and for a chain past the bound. - Dropping the classification, or either wrap, fails a test. A cycle never reaches seedcheck: the receive pass's own basis-chain walk (receive_pass.go chainEnd) refuses it first. check-in: 8b946de415 user: [email protected] tags: main, trunk | |
| 07:10 | Merge branch 'feat/471-seed-refusal' into feat/horizon-names-seed-refusal check-in: 0b3bc67376 user: [email protected] tags: main, trunk | |
| 06:59 | Merge branch 'feat/471-seed-refusal' into feat/seed-receive-drop check-in: 68cf05d9ea user: [email protected] tags: feat/horizon-dfloor-receive, main, trunk | |
| 06:59 |
fix(sync): judge pending rows when a received delta fills their basis (#471)
Two review nits. Liveness. A pushed artifact pending on a basis P stayed withheld for good when a pull supplied P as a delta: - Only MarkPending re-rooted, and only for a pushed delta. - Filling a phantom with a delta runs no cascade, so nothing judged the waiting rows if the delta was readable, or moved them to P's own root if it was not. A later fill of that root never reached them. The fix is one hook at the choke point every received delta passes, storeDeltaContent: seedcheck.DeltaStored. - If the stored delta is readable, the rows pending on it are judged. - If not, they move to its chain root, so whatever finally resolves the chain judges them. - It replaces MarkPending's push-only re-root. Error surfacing. JudgeFilled still skips a row whose own bytes cannot be read (a delta that does not apply or fails its checksum, or bytes that are not the artifact named) and judges the rest of the batch. Any other failure is now returned, a broken chain to the filled root included: check-in and tag return it, and the cascade logs it, as it already did for JudgeFilled errors. The separate availability skip went with it. Tests: - A pushed plain file and control artifact pending on a basis that a pull supplies as a delta, on a held basis or on one that arrives in the next round. Both become readable and stop being withheld, are announced on a push-on, and the control artifact is linked. - A row whose content does not decompress is reported. - A checksum-failing row is skipped like an invalid one. - Each hook branch and each error class fails a test when changed. check-in: 584c55d1fd user: [email protected] tags: main, trunk | |