Skip to content
zit Timeline
Login

Timeline

10 most recent check-ins occurring on or after 2026-08-02 19:52:28.

More ↑
2026-08-04
13:31
logstore: memoize pickBase candidate reads

pickBase reconstructed each of its five candidates through the delta chain on every call, and the candidate window slides by one artifact per write, so consecutive calls re-read almost the same set. A store-owned memo cuts frame reads 49.7 percent on pack.

The memo is a LogStore field rather than a caller-owned cache threaded through pickBase and putArtifact. A memo keyed by frame offset goes stale when rollbackArtifactBatch truncates and later writes reuse those offsets, and only the store sees that happen -- a caller-owned cache would hand callers an obligation they cannot discharge. It also changes no signature.

assertBaseMemoBelowEnd enforces the invariant at all three post-open truncation sites. Removing the clear from rollbackArtifactBatch makes it fire from a general import-path test, not only its own regression test. check-in: c075a01cfa user: dmestas tags: sub-manifest

13:30
delta: replace the encoder's hash map with a fixed-capacity index

wapp import, user CPU: safe 1076s -> 540s, fast 95s -> 53s. Byte-identical output; the 14 pinned vectors are unchanged.

std.AutoHashMap(u64,u32) + Wyhash is gone. The window is now a comptime 16-byte array read through readInt, and the table is open-addressed with capacity from a bounded counter, so it is never more than half full -- that is the probe's termination argument, asserted rather than assumed.

Most of the safe-side win is not bounds checks. Wyhash.init declares a 48-byte buf as undefined, which runtime_safety poison-fills on every call, and the encoder hashed twice per position -- once for the window, once again inside the map on the resulting key. Removing the map removed both.

The probe's explicit bound sits inside `if (std.debug.runtime_safety)`: present in ReleaseSafe where it can trap, compiled out of ReleaseFast. Written unconditionally it cost 12.2% of the fast import; gated it costs nothing in either mode. check-in: bfb63af209 user: dmestas tags: sub-manifest

2026-08-03
18:34
import-git: serve the published generation and clone it

zit serve on the imported destination, zit clone over a real socket, then the clone compared to Git commit by commit out of its own store. Proves the packed generation is a usable repository, not merely a readable file.

The mirror fixture gains five validated pairs and an orphan look-alike, so gate 3 is exercised in both directions -- the look-alike's bytes must survive. Author and committer instants are now distinct: git's default made them equal, which left the --attribution author comparison inert, and a regression that always wrote the committer instant would have shipped green. storeHolds errors on an undecodable name instead of answering "absent" to a question it cannot ask; twenty drop-set assertions rested on that. check-in: 4a7c197abd user: dmestas tags: sub-manifest

18:34
import-git: drive every declared publication failure point

verify_store, verify_map and after_rename_no_replace had no test; five of the eight legacy aliases were inert selectors that PUBLISH instead of failing. The boundaries are now partitioned into named classes and a gate asserts the partition covers the enum exactly, so a new boundary cannot ship an undriven fault arm.

Cancellation and child failure are exercised while generation 1 is open, with the window proven from the export stream rather than asserted in a comment. Every case asserts launched, reaped, no destination, and a fail-closed no-staging-sibling. Post-rename is inverted: the destination must survive and hold exactly the publish manifest. check-in: 67269dd361 user: dmestas tags: sub-manifest

18:34
import-git: stop leaking a name per OOM drop-set capture

append(gpa, try dupe(...)) evaluates the dupe first, so a growth failure inside append strands it outside the errdefer. Split into dupe, errdefer, append. checkAllAllocationFailures over mirrorDropNames, with a non-empty drop set asserted so the probe cannot pass on a run that allocated nothing. check-in: ab9a72bd10 user: dmestas tags: sub-manifest

13:22
import-git: verify packed imports exhaustively

verifyFrozen checked headers and existence -- it opened the map without reading a byte. The ordered pipeline now proves the published generation is the raw one minus exactly the candidate-only drop set: both stores reconstructed and rehashed, name sets differenced, every commit resolved and its tree flattened, tree and provenance digests recomputed, parents and attribution compared, refs and tags resolved, and the repository view derived from scratch. Each mismatch returns its own error and blocks the rename.

Eleven corruption fixtures, one per invariant, and each clause is mutation-proven: disabling it reddens its own fixture and nothing else. A clause no fixture catches is a clause that does not exist.

The ledger's strings borrow the importer, which the freeze destroys before verification runs, so the pipeline captures what it needs before the freeze rather than reading dangling memory.

Real corpus unchanged by the added checks: 7,010,160 bytes published, 0 faults, fixpoint byte-identical.

test 3484/3484, gate, e2e 716/716, git-import-e2e 11/11, git-import-cli 247/247, dst 6x1000. check-in: 60d9edab68 user: dmestas tags: sub-manifest

09:08
import-git: pack a second generation before publish

Translation writes a raw generation, packing writes a second through the classifier's candidate-only drop set, verification reopens both, and only the packed one is published. Stages are one-way and publish rejects every earlier state.

Measured end to end on the wapp mirror at k=5: a fresh import publishes 7.0 MB where the unpacked baseline was 63 MB. zit verify on the result reports 944 artifacts and 0 faults, a second pack is a no-op, and a second import into the same destination refuses with DEST byte-identical.

The catastrophic mode this task introduces -- the 66 MB raw generation riding the whole-directory rename into DEST -- was guarded only by statement order, since .verified is both the pre- and post-discard stage. Two guards now: publish refuses while a raw generation is present, and a staging manifest requires exactly {.zit, .zit-checkout, .zit-import-map.jsonl, refs} immediately before the sync.

Also fixes a use-after-free introduced with the S-tree hints: the partition-key map borrowed an S card's filename, which every frame below the root frees on return. The key is owned now, pinned by a pack-level test that segfaults without it.

test 3471/3471, gate, e2e 716/716, git-import-e2e 11/11, git-import-cli 234/234, dst 6x1000. check-in: 8530aa1440 user: dmestas tags: sub-manifest

06:20
pack: derive semantic hints through S-card trees

deriveCandidateHints read only a check-in's F run, and an S-bearing check-in keeps its files in sub-manifests -- so a tree-shaped repository got no same-path hints at all and every blob fell back to the chronological window. Pack was almost inert on exactly the shape sub-manifests produce.

Measured on the wapp mirror imported at k=5: 66.0 MB packs to 63.9 MB before (508 re-deltaed), 7.2 MB after (1028). 89% instead of 3%. zit verify on the result: 1518 artifacts, 0 faults. Fixpoint holds -- a second pack is byte-identical.

Two lanes, per design §6: blobs take earlier same-path versions nearest-first from the shared map, unchanged in shape and now fed by the full recursive enumeration; S nodes take the PRIMARY PARENT's node under an exactly equal canonical partition key, which needs both trees walked and cannot be an offset-nearest pool -- that is the heuristic the design forbids. Check-in lineage is untouched.

Recursion is bounded by max_tree_depth; a seen-set collapses diamonds and rejects cycles. A referenced S node that will not resolve is corruption and aborts, as the design requires.

test 3461/3461, gate, e2e 716/716, git-import-e2e 11/11, dst 6x1000. check-in: 6e2fc456f8 user: dmestas tags: sub-manifest

03:08
import-git: classify Fossil mirror metadata as droppable

A Fossil->Git mirror carries root manifest/manifest.uuid/manifest.tags that are not project content: wapp has 252 distinct manifest blobs across 252 commits while its native history tracks no such file. The classifier proves per commit that those bytes are reconstructible from what the import already holds, so packing can drop them.

Fail-closed and cryptographic: stripped-manifest hash equals the uuid, F cards equal the tree, P cards equal validated parents, D card equals the committer instant. A real top-level file named manifest cannot satisfy that by chance, and any inconclusive signal retains every present candidate -- never a strict subset.

Three corrections to the design, all established against the corpus: presence is per-repository (fossil's manifest setting emits each of the three independently), gate 6 must exclude every present candidate since F cards list none of them, and gate 3 must strip the sterilizer line the export appends or the uuid never matches. update.zig's manifest_paths and sterilizer are now public rather than duplicated -- it already had all three names and the exact trailer.

Measured on the pinned wapp corpus: 252/252 commits validate, drop set 512 = 252 manifest + 252 manifest.uuid + 8 distinct manifest.tags.

test 3461/3461, gate, e2e 716/716, git-import-e2e 11/11, dst 6x1000. check-in: d189b0da1d user: dmestas tags: sub-manifest

2026-08-02
19:52
repostate: note that control-carried tags make crosslink order observable

applyDirect's >= skip means a tag on a separate control artifact, rather than on the check-in manifest, depends on crosslink order; names emitBranchControls as the dependent. Also corrects branchValueOf's doc, left stale by c8c7c2696e. check-in: 941ddcb981 user: dmestas tags: sub-manifest

More ↓