50 descendants and 50 ancestors of f37a155d45968cd7
|
2026-08-23
| ||
| 23:25 |
landing: correct the cost note, and stop naming a particular forge
The module doc claimed stores of this size read in milliseconds. They do not -- a 117MB mirror scanned for 62 seconds. The claim was carried over from a WRITE path taken once per ticket change onto a READ path taken per view, where neither the frequency nor the scale held. Removed rather than softened, with what the scan actually costs written down: still linear in store bytes, which the constant-factor fix did not change. The doc also named quarry throughout. zit provides the hooks; what is built on them is not zit's business, and naming one forge in the code reads as though it were. closed check-in: 78d56a2177 user: dmestas tags: landing-doc | |
| 22:46 | merge landing-scan: the fold stops parsing every blob check-in: 0ef543c27a user: dmestas tags: trunk | |
| 22:36 |
landing: stop parsing every blob to find the records
The fold called artifact.parse on every artifact in the store and let it fail on the content blobs, which are most of a real store's bytes -- and a parse attempt pays a whole-bytes Z checksum before rejecting one. It now applies the reject-fast pre-screen frames --structural already uses, and reuses one plaintext buffer instead of allocating per artifact. zit landing ls, release build: quarry 1.06s -> 0.15s, zit 2.19s -> 0.31s, libfossil 8.49s -> 0.91s. A forum-shaped store is unchanged, because there is nothing to reject there; it was never the slow case. The screen only rejects. Every accept still takes the full parse, so nothing the fold would refuse can be admitted by being shaped like a card. closed check-in: 8776c0e5bb user: dmestas tags: landing-scan | |
| 21:50 | merge landing-show: the check-ins being proposed, and the thread check-in: 54badbba6a user: dmestas tags: trunk | |
| 21:41 |
landing show: list the check-ins being proposed and the thread
A reviewer decides from the commits before they look at a diff, and a record whose discussion the display omits is one people hold somewhere else. Both were already computed: the fold walks every reply to derive state, and the ancestry query answers the rest -- so this prints what was read and thrown away. Ordering comes from ancestryLinearized, not mtime: two check-ins made in the same second must not invert, and the verb owns no ancestry ordering of its own. A state change renders as an event rather than a remark, so the on-the-wire marker never reaches a reader. Both lists are bounded and say when they cut. closed check-in: cff154d675 user: dmestas tags: landing-show | |
| 20:55 |
merge landing-bare-store: mergeability probe and bare-store accept
zit landing show reports whether a branch would land cleanly, read from the store without writing anything. zit landing accept -R lands one with no checkout, merging in a working area it creates and removes, and refuses a conflicting landing with exit 5 and nothing written. check-in: d2bbc4f459 user: dmestas tags: trunk | |
| 20:45 |
landing: accept against a bare store, refusing conflicts
accept -R merges in a working area it creates and removes, materializing only the files both sides changed -- sparse-cone 4.4 resolves the rest into the record without writing them. The check-in is minted by the same commit path a checkout uses, so its F, P and R cards match a dense merge exactly. A landing that does not merge cleanly exits 5 with the store byte-unchanged and each conflicting path named. A forge never holds a half-finished merge and never commits conflict markers; the author resolves it from their own checkout. runMergeIn/runCommitIn take the checkout as a parameter instead of reading the process's cwd, which is what makes an ephemeral tree addressable without mutating global state. closed check-in: 8951bc9d1e user: dmestas tags: landing-bare-store | |
| 19:41 |
landing: report mergeability from the store alone
show answers whether a branch would land cleanly with no working tree and without writing anything, so a forge can flag conflicts before anyone accepts. It calls merge3.classifyFile -- the same classifier the working-tree merge runs -- so the verdict agrees with the merge rather than being a second opinion. Content-level, because hash-level resolution refuses any file touched on both sides, which is every long-lived branch. check-in: dea1cc66d3 user: dmestas tags: landing-bare-store | |
| 13:30 |
landing: propose a branch for trunk as a reviewable record
A landing is a forum thread plus a raw tag on the check-in that lands, so a stock fossil clone reads all of it and no new artifact kind is minted. open/ls/show/comment/accept/reject; accept merges and commits first, then records the outcome, so a partial failure leaves an open landing over merged work rather than an accepted record over an unmerged branch. check-in: c9ff7a71ea user: dmestas tags: trunk | |
| 00:40 |
docs: catch the reference up with this line of changes
pack no longer re-encodes profitable deltas and answers immediately on an unchanged store -- which also means a no-op pack checks no integrity, so the reference now says verify is the pass that always reads everything. stat reports the user table; verify's untouched-row list includes user rows. caching.html gains the treestat and packed memos, and the log-scan index tier says what it actually carries now. check-in: 87b58a3943 user: dmestas tags: trunk | |
|
2026-08-22
| ||
| 21:07 |
config pull user: adopt a fossil peer's user table
The client can request /user and latch its rows, and 'zit config pull user --user=NAME --password=' applies them -- merging by default, --overwrite to replace, fossil's own two modes. Credentials are required in practice because fossil serves the area to an administrator alone; without them the peer sends nothing and the verb says why instead of reporting empty success. zit's own serve does not emit /user: it has no capability model to gate it with. Smoke S12 drives a real fossil server for both halves (#125). check-in: 2170688c47 user: dmestas tags: trunk | |
| 17:29 |
users: carry fossil's user table, and read it
A .user frame kind holds login/pw/cap/info/mtime rows, repo-local like shun and folded newest-mtime-wins; pack carries the folded table and the scan index (v2) carries it too. 'zit config import|merge FILE' applies a fossil configuration export -- whose cards are byte-identical to the sync protocol's -- so a repository's users cross with no SQLite reader and no server. 'zit users' prints them; the password hash needs --raw. Caps are opaque and verbatim (#124). check-in: 709c457d95 user: dmestas tags: trunk | |
| 14:34 |
pack: trust profitable at-rest deltas; no-op an unchanged log
Re-beating every existing delta paid best-of-N delta.create plus a zlib trial per artifact and never converged -- each run re-encoded ~100k frames again for ~16 bytes each (#122). A delta whose declared raw length clears pickBase's own 75% bar is copied verbatim; one that fails it (a peer's poor base) still re-encodes. A .zit-cache/packed generation key makes a repeat pack over an unchanged log a no-op. tk: 121.6s -> 76.7s first pack, 0.2s repeat. check-in: 27c4f30036 user: dmestas tags: trunk | |
| 13:25 |
stat: buffered census walk, indexed config read
The census pread 10 bytes per frame -- an O(frames) syscall floor, ~150k calls on tk (#121). A 64KB read-ahead window keeps the walk a measured physical pass at ~1/40th the syscalls, and the config/roll read adopts the scan index like every other cached verb. tk stat: 0.234s -> 0.026s, now ahead of fossil info. Output byte-identical. check-in: c17e0233ca user: dmestas tags: trunk | |
| 13:12 |
diff/status: adopt the scan index for the baseline open
CheckinBaseline and the extras-family config read each paid a full frame-header scan per invocation (#120). A shared openLogAdoptingIndex adopts the .zit-cache sidecar (#112's pattern), refreshing on miss; extras' path is read-only per B-extras-16. tk single-file diff: 0.220s -> 0.020s. check-in: be281ab760 user: dmestas tags: trunk | |
| 02:27 |
sync client: log-backed holdings replace the whole-store snapshot
pull/push/sync loaded every artifact into memory before the first wire byte -- 3.7GB resident on a 148k-artifact store, OOM beyond it (#119). The client now sits on LogIndexStore as serve does: names, held/phantom, shun mask, and hash-policy off the frame index, content materialized per-rid on demand, received artifacts in the appendix. No-op pull on tk: 3.7GB -> 88MB, 64s -> 13s. check-in: 46d49702f7 user: dmestas tags: trunk | |
|
2026-08-21
| ||
| 22:51 |
status/diff: stat memo skips re-hashing unchanged files
Record (mtime, size, hash) per tracked file in .zit-cache/treestat after each scan; a matching stat answers without reading. Fossil's vfile heuristic (D. Richard Hipp), race window included. Advisory tier rules: malformed parses as empty, deletion costs a re-hash, never an answer. check-in: f34bbc730a user: dmestas tags: trunk | |
| 21:08 |
merge materialize-perf into trunk (#114)
Both sides had made the repository-tier setting read adopt the scan index (#112); trunk's write-on-miss refresh is kept inside the shared helper the autosync read now also uses. check-in: 6f540c5d77 user: dmestas tags: trunk | |
| 20:56 |
settings: honour .fossil-settings, fossil's spelling winning (#117)
A repository cloned from a Fossil server carries its versioned policy in .fossil-settings/, which zit ignored entirely -- extras, addremove, and clean answered differently from fossil on the same checkout. Every versionable setting now reads fossil's spelling first and zit's as the fallback, through the one resolver funnel; the ignore seam and the shadow note follow. Settings zit does not model stay unread. check-in: 296f2e5cc2 user: dmestas tags: trunk | |
| 20:50 |
update/open: materialize 2x faster on large trees (#114)
Stage payloads once and rename them into place instead of writing them twice with an fsync each; keep the parent directory open across a path-sorted plan instead of re-walking it per file; read the repository setting through the persisted log index instead of a full frame scan; reuse the verb's open repository for the target's branch facts rather than deriving repo state a second (update: third) time. tk (146k artifacts, 1,079 files): open 1.1s -> 0.48s; fossil is 0.34s. Rename-into-place also closes the checkout_sweep H2 finding: no crash cut can leave a hybrid file, so the pinned test now asserts that. closed check-in: 244523acfa user: dmestas tags: materialize-perf | |
| 20:38 |
repository: read checkout state from the invoking root, not the store dir (#116)
open() resolves root_dir to the store's home, which for a detached worktree is another checkout's directory -- so currentCheckoutRid read THAT checkout's .zit-checkout, and leaves, branch, bare ls, and the update/merge plan base all took the other tree's branch as current while status told the truth. A new checkout_root field carries the invoking directory; checkout-state reads use it. Pinned by a two-checkout e2e asserting every verb stays in its own tree. check-in: 6f0ba0fd3d user: dmestas tags: trunk | |
| 20:12 |
settings: adopt the scan index for the repository-tier read (#112)
repositorySetting opened the store with a bare openReadOnly -- a full frame-header scan to reach one config frame, 5.6s on a 1.17M-artifact store, and blind to the .zit-cache scan index because it had no directory in hand. It now takes Repository.logStore's own fast path: adopt the persisted index, resume over the tail, refresh on miss, with the cache directory resolved through the checkout's store pointer so a worktree never grows a stray sidecar. 5.6s to 0.12s. check-in: cf01ef34ba user: dmestas tags: trunk | |
| 20:05 |
serve: read push policy from the snapshot, not a per-round log scan (#112)
serveAnswer resolved binary-glob and crlf-glob through a fresh LogStore.openReadOnly per setting per round -- a full frame-header scan each time, ~11s per round on a 1.17M-artifact store, turning a clone into half an hour of server CPU. The snapshot's per-build config read now carries the pair, like the submanifest facts beside it; freshness rides the same (size, mtime) key. Server CPU for the same 181-round clone: ~34 minutes to 15 seconds. check-in: 262ca49578 user: dmestas tags: trunk | |
| 16:51 |
timeline: --weekday, Ben Joffe's branchless day-of-week (#111)
An mtime divided by a day is already the day number Joffe's identity consumes (benjoffe.com/fast-day-of-week), so the UTC weekday falls out with no calendar decomposition. Flag-gated; unflagged output is byte-identical, pinned by test against a Sakamoto reference over 40,000 days. log --long's record grammar is now a stated contract: consumers parse it positionally and no display option will change it. The flag skips info, which prints no date line. check-in: 4652c5cc31 user: dmestas tags: trunk | |
| 02:31 |
crosslink: stream derived per-check-in records (#109)
The record body now captures the U-card user and the changed-file set, diffed against the primary parent at derive time where the parsed manifest is already in hand (layout 5, checkpoint 4; one-time rebuild per store). 'zit crosslink' replays it: identity, comment, tags, and file changes as a versioned text stream, cache-served under the same key discipline as every read verb, byte-identical to --no-cache. sqlite dumps 36,797 check-ins in 0.11s / 14.9MB against the 3.87GB structural stream. A parent the store does not hold reads as 'files unknown', never as an empty change set -- sqlite's corpus carries exactly one, a 2009 check-in with a genuinely absent parent. Mixed binary versions thrash across the layout bump: each side rejects the other's cache and re-derives. Update every binary that shares a store. check-in: 2a68d2fc79 user: dmestas tags: trunk | |
| 01:00 |
frames: --structural emits only what the crosslink links (#108)
Content blobs dominate a full stream (13.2GB on sqlite) and indexing consumers discard them. The gate is artifact.parseForImport on the reconstructed bytes -- never a cache -- behind an O(1) reject-fast screen that admits card shapes and signature wrappers; skipped frames are not hashed or emitted, and the cursor advances over them, so filtered and unfiltered polls compose against one cursor. sqlite 13.2GB -> 3.9GB, tcl -> 682MB. check-in: a2fb222e32 user: dmestas tags: trunk | |
|
2026-08-20
| ||
| 19:17 |
repostate: index the two tag lookups the cache serializer loops over
serialize called tagsOf per rid and resolveName per symbol, each a full tagxref scan -- O(rids x rows) and O(symbols x rows). A one-check-in append cost the next reader 42s on a 42K-check-in store (#107); now 0.8s, and a full derive drops 74s to 21s. tagsOf routes through the existing rid_tags index; resolveName gets the inverse tag_rids index, maintained by the same helper pair so the two stay precise together. Selection semantics unchanged. check-in: 0aa75ac13d user: dmestas tags: trunk | |
|
2026-08-19
| ||
| 17:45 |
release: put the manifest assembler in the tree
release-manifest.json is the authoritative record of a release -- checksums, targets, qualification -- and nothing that produced it was in the repository. Its source_tree_sha256 had to be reverse-engineered from published output to cut 0.2.0: sha256 over 'sha256 path' lines sorted BY LINE, so by hash, which is the sha256sum | sort | sha256sum idiom. The release whose manifest nobody could check is the one carrying false fields. 0.1.3 labelled a native build x86_64-linux-musl and claims 104 source files against a tarball holding 93. The script re-verifies every archive against its own metadata fragment before describing it as qualified, and reproduces the published 0.2.0 manifest exactly. check-in: 520e388d6a user: dmestas tags: trunk | |
| 16:58 |
release: prepare zit 0.2.0
The 0.1.x line grew from 38 dispatched verbs to 61, gained the sub-manifest dialect, and grew a layered derived-state cache. 0.1.4's notes mention sub-manifests once and never say cache, so the notes here cover that machinery rather than only what changed since 0.1.4. check-in: 09e80bc9ef user: dmestas tags: trunk, version-0.2.0 | |
| 02:50 |
reference: pin the flags, not just the verbs
An audit of all 57 verbs against their own --help found two gaps the existing pins could not see, because reference_test.zig checks that a verb HAS a section and #68 checks options-table flags against the usage block. pack --drop-elided was in neither. The flag is real -- it is the sparse spec's narrow-then-compact deletion, destructive and irreversible -- and it appeared in no help output at all, so nothing compared it to anything. Now documented, including that unlike shun it leaves no tombstone, so a later widening re-fetches from a peer. settings --global was accepted but missing from the reference. The new e2e pin holds every verb's real --help against its reference section both ways: a flag the verb accepts that the section never names, and a flag the section documents that --help never mentions. The second direction matters more than it looks -- an omission is discoverable from --help, a promise sends someone hunting for something that is not there. It lives in the e2e lane because it needs the help TEXT, and the help constants are in main.zig, outside the library test module. Conventions flags (-R, --no-cache) count as covered wherever they are documented once; matching is whole-token so --cone cannot satisfy --cone-not. Both directions were verified by breaking them deliberately. check-in: 03e606ea49 user: dmestas tags: trunk | |
|
2026-08-18
| ||
| 23:30 |
merge -X ours|theirs: resolve content conflicts wholesale (#91)
merge3 already resolved every region except one -- the case where both sides changed it differently. That branch now takes a side instead of always writing an envelope; the clean cases above it are untouched, which is the whole point. A region only one side changed is still that side's under either strategy, so -X is not 'discard their work', it is 'stop asking me'. The mechanical cases are what it is for: a regenerated lockfile, a vendored tree taken whole from upstream. Applies to content disagreements -- conflicting hunks, add-vs-add, and both-modified binaries. It deliberately does NOT resolve modify-vs-delete: that is a disagreement about whether the file should exist, not about its contents, and either reading silently destroys work. git declines it too. -X ours is not --backout, and the help says so outright: --backout reverses NAME's change, -X ours keeps your side where the two collide and reverses nothing. They read alike and do different things. Composes with precedent (#103) without either knowing about the other: precedent is a pass over the outcome set, so a strategy that leaves no conflict leaves it nothing to replay or record. Verified both ways -- -X records no precedent, and a plain merge still conflicts and records. A bad or missing -X value is a usage error rather than a silent fall back to markers, which would look exactly like a strategy that ran and found nothing. check-in: 52b8c57f4c user: dmestas tags: trunk | |
| 22:40 |
export-git: drop a verb-list line pasted into its usage block
'zit export-git --help' printed " \ import-fossil bring a fossil repository's history into zit" as its second line, backslash included. The verbatim help pins cover merge and stash only. The new pin checks shape instead: no rendered help line may start with a backslash, which catches the paste for every verb. The reference gains merge --no-precedent, which was documented under the wrong verb. check-in: 5fe0d9eafe user: dmestas tags: trunk | |
| 21:28 |
stash: reserve .zit-stash so it cannot be tracked
The sidecar was absent from the control-path registry, so extras listed it and add accepted it: an ordinary add put the contents of stashed work into a check-in and synced it to every peer, recoverable only by shunning. The stash help already promised "never an artifact"; nothing enforced it. Authoritative tier, matching what losing the file costs. check-in: 503637a369 user: dmestas tags: trunk | |
| 20:37 |
precedent: remember conflict resolutions and replay them
A merge records each conflict in the local .zit-precedent sidecar; commit attaches the resolution the operator settled on; a later merge whose conflicted bytes match replays it instead of writing markers. Keyed by path and conflicted bytes, exact because the diff3 envelope carries all three sides. Whole-file, so a file whose other conflict moved will not replay. Replay is named per file, never silent. --no-precedent declines for one merge without forgetting. 'zit precedent list|show|forget', alias rerere. The sidecar is a reserved advisory name, so it can never become an artifact. check-in: a9a24a857c user: dmestas tags: trunk | |
| 19:39 | fix(release): export tag-only mirror changes check-in: 91cc1528fc user: dmestas tags: trunk | |
| 19:22 | release: prepare zit 0.1.4 check-in: 4c2286bcb4 user: dmestas tags: trunk, version-0.1.4 | |
| 19:05 | release: qualify portable packaged binaries check-in: 4290d14e17 user: dmestas tags: trunk | |
| 17:53 |
reference: keep the index on narrow viewports
The sidebar was display:none below 800px, which is most of the time an embedded doc is read -- a panel, a phone, a split window. A 60-section reference with no navigation at all is worst exactly where scrolling costs most, and the rule was invisible because it only fires at widths you do not develop at. Below 800px the rail becomes a compact index above the content, links flowing as wrapped chips rather than one per line. It is collapsed by default there: expanded, it pushed the page title a full screen down, so a reader met a wall of verb names before learning what the page was. The disclosure is hidden entirely on the wide layout, where the rail is always open and the control would be noise. Verified in a real browser at 1400, 780 and 500 pixels: 70 index entries reachable at every width, against 1 before. check-in: 65fe7b9ec5 user: dmestas tags: trunk | |
| 16:04 |
www: embed the docs, pinned so the reference cannot drift
The command reference and the caching write-up move into the tree as embedded documentation, which quarry serves on fossil's own /doc/VERSION/PATH route, already implemented in its api/register.go. Fossil's www/embeddeddoc.wiki gives the reason to prefer this over the wiki, and it is the reason the pin below can exist: The documentation files are versioned together with the source code files so it is always clear what version of the documentation goes with a particular release. A wiki page cannot be pinned by a test; nothing ties it to a checkout. A tracked file can. reference_test.zig checks both directions -- a dispatched verb with no section, and a section naming no verb -- with a reviewed-exception list for sections that document an absence on purpose, which is what the export-fossil section is. The reference had drifted for weeks: grep, purge, config and trust all shipped undocumented, and nothing noticed because noticing needed a human to compare two lists. The caching page's ladder is corrected while embedding it. It reported 641.9s / 58.3s / 0.31s; those were Debug numbers. Measured in the mode that ships: 29.63s full derive, 0.34s record-body replay, 0.04s image. Having a cache is worth 87x and the image a further 8.5x -- both real, neither 188x. Its footnote about a separate environment reporting 0.38s 'unreconciled' is resolved: same machine, optimized build. info also moved to the image since it was written, leaving branch and tag on the record-body rung. check-in: 269e24b3c3 user: dmestas tags: trunk | |
| 15:13 |
branch and tag read the warm image: the resolved §5 tag surface (#98)
The image carries the active tag set now -- name, tagtype, optional value, per rid -- so tagsOf, branches, tagNamesMatching and activeTagValue all answer from either backing. Stored resolved: the derive has applied §5.1's cancel rule and §5.3's precedence before serialize runs, so nothing here re-judges either. That is what fossil does, where tagxref holds one row per (rid, tag) with propagation flattened in by tag_propagate at write time. It subsumes the §5.2 override columns from the previous change; those are gone rather than kept beside a general mechanism that already holds the same bytes. Two readers still went straight to tagxref and had to be routed: activeBranchValue and hasActiveClosed. Both answered plausibly on an image rather than refusing -- the first as a null-deref in a postcondition under safety and, with that assert compiled out, a different branch listing; the second as a listing quietly missing every (closed) marker. tagNamesMatching keeps a per-backing arm on purpose. The derive holds a (rid, tag) map with no per-rid index, so walking rids and asking each for its tags is O(rids x map) there; sharing one loop cost 42s where two cost 0.37s. check-in: ce9e7a1cd4 user: dmestas tags: trunk | |
| 14:12 |
build: how to measure this project without being lied to
Every rule was paid for by a wrong answer that looked convincing: an issue filed against this repository proposed an architectural change on the strength of a 58s figure that was a bare-zig-build Debug number; the ~25x pack figure this file carried came from a --release=fast that was quietly building ReleaseSafe; a single run reported a 20% regression for a change that was a 3.7x improvement; and an optimization that won by 50x was erroring out on the first artifact and printing nothing. It sits beside the build rather than in the wiki because each rule is only true of THIS build script -- bare zig build means Debug because of the line above it, and every --release resolves to the preference for the same reason. A page elsewhere would rot the day one of those changes and nothing would fail. check-in: 4e355fbfd1 user: dmestas tags: trunk | |
| 13:34 |
log: walk the derived adjacency, the way fossil does
The default listing prints an ID per check-in and reads nothing else, yet it reconstructed and re-parsed every reachable manifest to find P cards. On a repository whose manifests inline the whole file set -- sqlite's are 2,219 F cards and 186KB each -- that is 6GB of text decompressed and parsed to answer a question that reads none of it. Fossil does not do this. manifest_crosslink parses a manifest once and writes the edge to a persistent table: CREATE TABLE plink(pid, cid, isprim, mtime, baseid, UNIQUE(pid,cid)); CREATE INDEX plink_i2 ON plink(cid,pid); Every later traversal is an indexed read of it -- timeline.c's ancestor walk is a recursive CTE over plink and touches no manifest. The tell is fossil's parsed-manifest cache: MX_MANIFEST_CACHE is 6, and its comment says it exists for rebuild. Nothing on the read path needed more. zit's derive holds the same relation, with the is_primary flag plink's isprim carries, and since #98 the zero-parse image carries it too, so this walk reads no artifact content at all. --long still needs D/U/C, so it stays on the reading walk. log tip, min of repeated runs, warm, ReleaseFast: libfossil 1.28s -> 0.02s libspatialite 4.81s -> 0.00s fossil 20.40s -> 0.11s tcl 40.05s -> 0.65s sqlite 62.90s -> 0.51s Output byte-identical across all seven corpora. WHAT MOVED: the reading walk re-hashed and re-validated every reachable artifact, so log doubled as an integrity check. It no longer does -- which is fossil's split, where validation happens at crosslink and test-integrity re-checks. zit verify is that verb, and the wrong-full-ID test now asserts verify catches what log stopped catching, so the coverage cannot go missing unnoticed. check-in: cc996c1af2 user: dmestas tags: trunk | |
| 13:07 |
info reads the warm image too: §5.2 overrides, resolved (#98)
info was the last of the three verbs held on the record-body rung, and it was held by one thing: amend --comment stores its override as a tag, and tagxref is empty on an image backing, so the fast path printed the manifest's original comment. On tcl that cost 58s to print one check-in. The image carries the three display overrides now -- comment, user, date -- RESOLVED. The derive has already applied the cancel rule and mtime precedence by the time they are written, so the image stores the answer and never re-judges it, the same trade branch_id already makes with the §6.1 fallback. That is also what fossil does: tagxref holds one row per (rid, tag) with propagation flattened in by tag_propagate at write time, so its readers do group-bys rather than precedence. activeTagValue now asserts derived backing instead of answering null there. It returned null for a caller that had a tag, which is the silent shape of wrong this whole issue is about; displayOverride is the image-safe door for the subset the image actually carries. check-in: 8c3abc36c7 user: dmestas tags: trunk | |
| 02:18 |
ls, cat and bisect read the warm image instead of re-deriving (#98)
On tcl's tip zit ls drops from 58.3s to 0.31s: the derived state it needed was already in the cache, in the zero-parse image, and the verb asked for the record-body replay anyway. Adjacency arrived in the image last change; what remained was every read that went around the accessors. events, its count, and store.count are all empty on an image backing, so a bound taken from them read zero and a branch value read from tagxref fell back to main_branch for every rid -- the section 8.4 walk then never left the branch and answered start:/root: with a check-in from somewhere else, exit 0 and no diagnostic. Those reads route through hasEvent, mtimeOf, hashOf, ridCount and eventsIter now, which name one enumeration order for both backings rather than inheriting insertion order from one of them. info stays on the full derive: amend --comment stores its override as a tag, and the image carries no tag tables, so on the fast path info printed the manifest's original comment. An existing test caught that the moment the verb moved; branch and tag stay behind for the same reason. check-in: 2772dd3942 user: dmestas tags: trunk | |
| 02:10 |
log: memoize the delta bases the walk re-resolves
walkLog reconstructed every manifest with log.content, which walks the delta chain from scratch. Check-in manifests are routinely stored as deltas against their neighbours, so the same base chain was re-resolved once per check-in -- work that grows with the history behind a node rather than with the node. Per-check-in cost climbed with repository size (0.5ms on libfossil against 2.2ms on sqlite) instead of staying flat. Every other read path in this file already took #84's PlaintextCache; this one did not. Output is byte-identical -- contentCached is the same bytes, only less work. log tip, min of repeated runs, warm, ReleaseFast: pikchr 0.14s -> 0.04s wapp 0.05s -> 0.01s libfossil 1.26s -> 0.34s libspatialite 4.69s -> 2.28s fossil 19.36s -> 7.24s sqlite 59.70s -> 23.81s tcl 38.29s -> 5.99scheck-in: 55b176c8dd user: dmestas tags: trunk | |
| 00:02 |
repoimage: carry adjacency, and read it through one door (#98)
The image held mtimes, hashes, branch ids and the symbol table but not the parent/child relation, which is why every verb needing ancestry re-derived the whole log on open. Six CSR columns carry it now, both directions with the primary flag -- primary-parent selection and merge-edge skipping both read it, so an image without it answers wrongly rather than refusing. The two backings hold the same relation in different shapes, so edgesOf hides which one a caller got, and the equivalence test compares them in order with a guard that fails when the corpus has no edges to compare. Behaviour-neutral: no assert relaxed, no verb moved. Freeing those needs an events equivalent as well -- resolve reads self.events directly, and that table is empty on an image backing. check-in: 82de6f83d0 user: dmestas tags: trunk | |
|
2026-08-17
| ||
| 22:54 |
build: the ~25x pack figure does not reproduce; record what does
Measured on wapp warm, modes interleaved and repeated: pack is ~8.5s fast, ~9.5s safe, ~10.7s small, and the read path is 0.31/0.33/0.36 with leaves at 0.01s in all three. So fast buys ~12% on writes and nothing measurable on reads. The '599ms fast against 15.2s safe, ~25x' this file carried reproduces in neither the ratio nor the absolutes; the delta encoder's search was reworked since it was taken. Left uncorrected it would have justified almost any correctness cost, which is the expensive kind of stale number to leave in a comment that decides the shipped build. check-in: 6181a57e0c user: dmestas tags: trunk | |
| 22:37 |
build: ship ReleaseFast
zit ships ReleaseFast, so --release names it. This reverses the ReleaseSafe pin; pack runs ~25x faster and read verbs are indistinguishable between the modes (ls over tcl's 300MB store is 0.31s fast against 0.33s safe, leaves 0.01s in both). The cost is real: over a thousand std.debug.assert calls compile to nothing, so release_assert_test.zig stops being belt-and-braces and becomes the thing keeping an assert from standing in for validation of untrusted input. Its rationale is rewritten around that. Stating a preference makes it the ONLY reachable release mode, which the old comment got wrong: it documented --release=fast as an escape hatch from the ReleaseSafe pin, and that flag produced a ReleaseSafe binary anyway -- safety strings present to prove it. Anything benchmarked through it before now measured the checked build. The ci pin moves from 'keeps assertions alive' to 'the mode is stated, not left to whoever packages', which is the part that survives the reversal. check-in: da904fbe1c user: dmestas tags: trunk | |
| 21:30 |
CI: assert fossil's version, do not only print it (#100)
smoke is the only lane that runs a real fossil against zit, so runners on different fossils make the conformance claim mean a different thing on each -- a regression against the newer can pass on the older, and newer-only behaviour reddens the older for something that is not a defect. Both are silent, because the version is printed hundreds of log lines before the failure it explains. fossil sqlite3 quoting its output on 2.26 but not 2.28 cost three round trips that way. The printed line stays; the release notes still need it. check-in: ab3a1983cb user: dmestas tags: trunk | |
| 20:25 |
grep -S and -G: the pickaxe over history (#92)
-S reports the check-ins where the occurrence count of PATTERN changed, -G those where a changed line mentions it. A string added once and deleted later is two lines, where plain grep reports every version that held it in between. The issue expected this to need a diff per revision. It does not: a path whose blob hash matches its parent's cannot have moved either answer, so the walk compares F-card hashes and reads only the blobs that changed. Deletions are picked up from the parent side, which a walk over the child's files alone would miss. Merges compare against the primary parent, as git log -S does; counting against every parent reports one appearance twice. check-in: 28c48bb3f7 user: dmestas tags: trunk | |
| 20:00 |
archive differential: fossil sqlite3 quotes its output on 2.26 (#95)
The runner on 2.26 returns the check-in uuid wrapped in single quotes where 2.28 returns it bare, so the name handed to fossil tarball could not resolve -- and the failure named the hash, which reads like a missing check-in rather than a quoting difference. The harness strips to hex and then insists on a 40- or 64-character hash, so a future output change is refused by name here instead of failing two commands later. The CI step no longer runs its own copy of the query; one place for this to go wrong is enough. check-in: f37a155d45 user: dmestas tags: trunk | |
| 19:42 |
archive differential: fossil's documented argument order, and print its errors (#95)
One runner carries fossil 2.26 where the others carry 2.28, and 2.26 refuses options before the positionals -- fossil documents 'tarball VERSION OUTPUTFILE [OPTIONS]' and 2.28 merely tolerated the other order. The reason that took a full CI round trip to find is the second change: a failure said 'see $WORK/x.log' on a machine nobody can log into. It now prints the log, so fossil's own sentence reaches the build output. check-in: e492130985 user: dmestas tags: trunk | |
| 19:28 |
-R: say which mistake was made, not 'no checkout here' (#97)
The discovery message tells the operator to name the repository with -R. For a -R failure that advises supplying the flag already supplied, and it reads the same whether the path was mistyped or simply holds no repository -- so a stale path in a script looked like a bug in the verb. Reported against tarball; every -R verb had it. One reporter, routed through all nine resolveRoot call sites; the no-flag discovery message is unchanged. check-in: 10a1236a7a user: dmestas tags: trunk | |
| 19:28 |
archive differential: unpack zips without an unzip binary (#95)
The runners have no unzip, so the harness exited 2 for a missing tool and the step failed on its own coverage guard instead. Adding a third tool to the battery for one step is the worse trade: python3 is already required here, and ZipFile.extractall drops modes and symlinks -- the exact defect this differential exists to catch -- so the external attributes are applied explicitly rather than trusted to an extractor. check-in: 2420f6fb88 user: dmestas tags: trunk | |
| 19:16 |
ci: the archive differential asserts on output it never captured
archive-fossil-diff.sh writes the per-format comparison to stderr, but the step piped only stdout into diff.log, so the 'kinds compared' line the coverage guard greps for was never in the file -- it counted 0 on every platform and the step could not pass. pipefail with it: the pipeline was taking tee's status, so the differential's own failure, including its exit 2 for a missing tool, was swallowed and only the guard could ever fail this step. check-in: 98243aad6a user: dmestas tags: trunk | |
| 19:00 |
zit purge: recoverable removal of leaf check-ins (#87)
Removes a check-in and the blobs only it introduced, into a local graveyard that purge undo restores and purge obliterate empties. The drop set is (seed + reachable(seed)) minus reachable(everything retained), so a blob a surviving check-in still names is never freed -- dropping it would create a phantom rather than reclaim anything. Only leaves go, judged over the whole named set: purging a parent alongside its child is accepted, purging it alone is refused. Reuses shun's rewriteAndSwap and records no tombstone: a purge is local, so content that already synced comes back from a peer. The help says so and points at shun. check-in: 8536f59fba user: dmestas tags: trunk | |
| 18:41 |
ls: size from the frame header, full names through the index (#96)
A blob's plaintext length is already in every frame kind -- a zlib container states it outright, a delta carries it in its own header -- so --size reports one without resolving a chain. The binary verdict cannot be had that way (a NUL may be the last byte), so --long still reads every blob and keeps its exact columns. The larger cost was neither: resolveHexPrefix scanned the whole store per file to resolve a full-width name the digest index answers outright, which slowed plain ls too. On tcl's tip --long drops 167s to 93s and --size costs what plain ls costs. check-in: 3b7e8c7569 user: dmestas tags: trunk | |
| 17:10 |
smoke S11: a stock fossil push refused by the content policy (#88)
Unlike S10, which its own comment notes survives deleting the server's admission check, this one genuinely exercises the decision: the violating file is ordinary new content fossil pushes because it is new. Records the limitation in the header: fossil exits 0 after printing the refusal, so the assertions are on the store and on fossil's output rather than its exit status. zit push exits 1. check-in: abc41cae82 user: dmestas tags: trunk | |
| 16:41 |
serve refuses a push that violates its content policy (#88)
#81's predicates bind a checkout; nothing stopped a push from a clone that never adopted them, or from stock fossil. Fossil cannot close this at all -- its hooks are before-commit (client-side) and after-receive. The policy is the SERVER's, resolved per round from its own settings, never from the artifacts arriving: a push carrying its own policy would otherwise arrive bearing permission to land. Only inspecting rules cross the wire; before-commit has no server-side counterpart. A push sends artifacts by hash and only a manifest names paths, so the walk is manifest-first -- the policy gates what becomes REACHABLE, not what bytes transit. Decided before ingest, so a refusal stores nothing, and the whole round is refused rather than half of it. Verified against fossil 2.28: it prints the refusal text verbatim and lands nothing. Note it exits 0 while doing so, where zit push exits 1. closed check-in: e091f2ac80 user: dmestas tags: push-policy | |
| 16:41 |
grep: search a file's history for a literal (#92)
zit had no content search at all -- not git's pickaxe, not fossil grep's file-scoped walk. This is the latter: each named file's stored versions newest-first, every version whose content contains PATTERN reported with line numbers. A run of check-ins that never touched the file shares one blob and reports once. PATTERN is literal; zit carries no regex engine and git log -S is literal for the same reason. Working-tree search stays ripgrep's job -- what only zit can do is read content that is not on disk. A version the store cannot produce is counted and reported on stderr, not skipped: a search that quietly examined less looks like one that found nothing. Exit 0 found, 1 not found. check-in: a0ab591180 user: dmestas tags: trunk | |
| 16:20 |
zip: the same archive in the format Windows reads (#95)
Selection, top-level directory, reproducibility, and the refusal to write a partial archive are shared with tarball whole; only the container differs. Zip's header carries the size and CRC, so a member is compressed before its header is written, and deflate is used only when it makes the member smaller. Modes live in the central directory's external attributes, which is where a reader looks and where an executable silently becomes 0644 if you get it wrong -- so the fossil differential compares both formats now. check-in: c454de8df9 user: dmestas tags: trunk | |
| 15:32 |
tarball: check zit's archives against fossil's, modes included (#95)
Comparing zit to itself cannot catch a wrong permission bit -- writer and reader agree in the same direction. Fossil is an independent witness that already gets it right, so the harness unpacks both archives and compares member set, modes, and contents. It found one: -R was dropped at dispatch, which every test missed by running inside a checkout. Fossil versions symlinks where zit add still refuses them, so an imported corpus is also what finally exercises the l-perm path. check-in: 96a6d46d19 user: dmestas tags: trunk | |
| 14:41 |
tarball: write a check-in's files as a reproducible gzip tar (#95)
The effective-set walk already resolves sub-manifests and delta chains in canonical order, so this is that walk with an archive writer. Members carry the F card's permission, so an executable stays executable. Two runs of one version are byte-identical: history supplies the order and the timestamps, and no uid, gid, or build host is recorded. A file the store does not hold is refused by name -- a partial archive looks exactly like success. check-in: 580d0691c7 user: dmestas tags: trunk | |
| 14:29 |
Merge tag-list release filtering to trunk
Plain tag listing now reflects only singleton symbolic release names, while raw listing preserves complete repository tag state. Refs ticket a679b1197b61127930f7e2a6478145c561dd54cc. check-in: 24356ebb90 user: dmestas tags: trunk | |
| 14:28 |
Fix tag list release-name filtering
Plain tag list now shows only singleton symbolic names globally and on a check-in, excluding branch machinery, raw properties, and propagating tags. Raw listing preserves every active tag, stored prefix, and value. Add a tag-type-filtered repository-state query and end-to-end coverage for mixed singleton, propagating, raw, and branch tags. Refs ticket a679b1197b61127930f7e2a6478145c561dd54cc. closed check-in: 41852f0a9e user: dmestas tags: fix-tag-list-release-filter | |
| 13:58 | Create new branch named "fix-tag-list-release-filter" check-in: cf62ee5ea1 user: dmestas tags: fix-tag-list-release-filter | |
| 13:33 | merge quarry-support: ls --long and log --long (#96, #93) check-in: 15fa309b01 user: dmestas tags: trunk | |
| 13:27 |
ls --long and log --long: one invocation answers a page (#96, #93)
ls --long adds size, the F card's permission token, and text/binary. The permission is the fact no other verb can supply -- it lives in the manifest, and the blob it names does not know it is executable -- so a listing could not mark executables and mis-rendered symlinks. --under bounds the listing to one directory, which is also what makes --long's per-file content read affordable. log --long adds each check-in's timestamp, user and comment. log CHECKIN PATH already selected the check-ins touching PATH; emitting only hashes meant a file-history page cost a subprocess per row. Both are opt-in; the default outputs are byte-identical. A blob the store cannot produce reports 'absent' rather than a zero-byte text file. closed check-in: f01eb5b6a0 user: dmestas tags: quarry-support | |
| 11:06 | merge sparse-cone: classify paths against the cone and name the deciding member (#79) check-in: 187ad81e8c user: dmestas tags: trunk | |
| 01:05 |
cone test: classify paths against the cone and name the deciding member (#79)
Longest match across both polarities decides coverage, so the verdict alone cannot say why a file is missing. The member prints bare so it pastes into zit cone remove. Exits 0 when every path is covered, 1 when any is elided. closed check-in: 3cb62134a3 user: dmestas tags: sparse-cone | |
| 00:14 | Create new branch named "sparse-cone" check-in: a620c24254 user: dmestas tags: sparse-cone | |
|
2026-08-16
| ||
| 21:47 |
binary-glob and crlf-glob: declarative content policy (#81)
Both are opt-in: an unset glob is no opinion, not refuse-everything, so a repository that never set one commits exactly as before. Set binary-glob to *.png and PNGs are the binaries the project expects, so a stray .o is refused. Neither runs code, so unlike before-commit they bind a fresh clone with no consent step -- that asymmetry is why they exist. The check gates what a commit INTRODUCES: an all-files commit re-captures every managed path, so classifying unchanged content would make declaring a glob block every later commit until the whole tree already conformed. Comparing against the parent's blob grandfathers inherited files, per content rather than per path, so editing one re-checks it. --no-checks waives it, the same flag that waives before-commit. check-in: 2711adbb03 user: dmestas tags: trunk | |
| 21:28 | merge project-code: read a repository's identity without a checkout (#85) check-in: 1fc9e67f22 user: dmestas tags: trunk | |
| 21:18 |
stat: read a repository's identity without a checkout (#85)
project-code and hash-policy join stat's fields, and stat joins the -R family it was refused for a plumbing reason rather than a policy one. A consumer needing the project code had to find a free port, spawn zit serve, poll for readiness, POST a clone container and scrape the push card -- a subprocess and a listening socket per repository -- because the value was reachable only by speaking the sync protocol to a server you started yourself. The no-checkout diagnostic said 'no .zit found' in directories where .zit was sitting right there; discovery looks for .zit-checkout, so it now says that and names -R as the way in. closed check-in: cd748a57c2 user: dmestas tags: project-code | |
| 19:28 | Create new branch named "project-code" check-in: e77f1e196a user: dmestas tags: project-code | |
| 18:57 |
settings: keep-glob, manifest, before-commit consent, config pull (#82, #83)
keep-glob protects files clean must never delete while extras still lists them; manifest becomes a project setting driving build stamps. Both resolve across the tier chain, so a tracked .zit-settings file clones with the code. before-commit splits declaration from consent (#82): the project ships the check as a tracked file, zit trust records the SHA-256 this checkout agreed to run. Cloning executes nothing, and an edit revokes consent by itself because the record pins content, not a flag. Non-zero exit aborts before anything is minted. zit config pull shun is fossil's explicit request side; carriage stays clone-time otherwise so no peer can suppress content here on its own say-so. Storage gains one keyed-string block: unknown keys are skipped by length, so the next string setting needs a key rather than another shape tag. Existing frames are unchanged. clean-glob is deliberately absent: it means never-prompt and no-undo, which zit clean already is. check-in: 998dbf9d43 user: dmestas tags: trunk | |
| 18:45 | merge empty-trailing-token: C and J accept an empty trailing token (#86) check-in: b4893b3fee user: dmestas tags: trunk | |
| 18:30 |
C and J accept an empty trailing token (#86)
Fossil distinguishes an absent token from an empty one positionally: `C` LF has none and is rejected, `C` SP LF has one empty token and is a present, empty comment. zit implemented §4.2's `token = 1*tchar` literally and rejected the empty form on C and J, which drops the whole artifact — every ticket change with an empty field, every check-in with an empty comment. T and U were already right. Two tests asserted the old behaviour and now assert the corrected one; a third pins the writer, since --submanifest re-mints every manifest. The RFC text is unchanged; docs/rfcs/erratum/ carries the correction. closed check-in: 5ccb138e6d user: dmestas tags: empty-trailing-token | |
| 18:04 | settings: hazard-tiered model with a global tier and provenance (#83) check-in: 6677853a3d user: dmestas tags: trunk | |
| 17:44 |
smoke S9: require the carried shun row, not a phantom (#73)
Carrying the /shun config group means a fossil clone records the artifact as suppressed instead of holding a phantom for it. That is the better end state: a phantom is something fossil goes looking for, so the next sync with any third party would undo the takedown. S9 asked for the phantom specifically and so went red. It now asks fossil's own shun table and REQUIRES the row — tolerating the phantom would leave the scenario unable to notice the carriage regressing. The phantom probe stays as a diagnostic that tells a lost row apart from a served artifact. check-in: 7c656e4718 user: dmestas tags: trunk | |
| 17:33 | merge frames-parallel: verify a batch across workers (#84) check-in: c36e9952c2 user: dmestas tags: trunk | |
| 17:28 |
frames verifies a batch across workers (#84)
Profiling the post-memo stream put 59% in artifact.verify and 38% in reconstruction, so the parallel target is verification, not reconstruction: reconstruction is what the plaintext memo made sequential-friendly, and fanning it out would contend on that cache. Reconstruction stays single-threaded; a bounded batch is hashed by workers walking disjoint strides, then emitted in order. Workers touch no store, no cache and no allocator, so there is no queue, lock or atomic — join supplies the ordering. libfossil 2.35s -> 1.37s wall, byte-identical output. closed check-in: 4b2fceeacd user: dmestas tags: frames-parallel | |
| 16:30 |
local signing settings, and the shun table over the /shun config group (#72, #73, #83)
clearsign and pgp-command join zit settings as repo-local values fossil never propagates: pgp-command is a command line this machine executes, which fossil classifies sensitive for that reason. Config frame gains shape v4 for the command string; clearsign rides the existing flags byte. Precedence is ZIT_PGP_COMMAND, then the setting, then fossil's default. zit serve now answers reqconfig for a named group, and clone requests and adopts /shun. A clone that dropped the table would re-accept content its origin was ordered to remove on the next sync with any peer; the #73 scale run also traced 12 spurious phantoms to the same loss. Verified both directions against stock fossil 2.28. scripts/roundtrip-fossil.sh is the fossil -> zit -> fossil differential, run in CI on a built corpus: zig build e2e can only spawn zit, so nothing in the Zig suite can assert a stock fossil accepts what zit served. check-in: 57a9e0fc6b user: dmestas tags: trunk | |
| 16:00 | merge frames-memo: stream through the plaintext memo (#84) check-in: 6759506ea8 user: dmestas tags: trunk | |
| 16:00 |
frames streams through the plaintext memo (#84)
frames was the one bulk reader still calling the uncached content(), so a delta base was reconstructed once per referencing delta rather than once per run — superlinear work against a linear stream, on corpora that are ~90% deltas after a fossil import. libfossil (17,449 artifacts): 12.4s -> 2.34s real, 12.0s -> 2.25s user. Output byte-identical, peak RSS down 362MB -> 314MB. memoize_undeltaed stays off: the sweep reads each artifact once, and bases are memoized regardless of their own frame kind, which is the win. closed check-in: 2e3bb0b9db user: dmestas tags: frames-memo | |
| 15:04 | merge record-cards: technote C/T cards and forum N card (#77) check-in: 5bea261864 user: dmestas tags: trunk | |
| 14:46 |
technote and forum verbs write the cards the format allows (#77)
technote add gains --comment (the C card a timeline renders as the note's title, unattachable by any later verb) and repeatable --tag, minting T +sym-NAME * sorted ascending as fossil writes it. forum post and reply gain --mimetype, matching wiki commit's spelling. Empty titles, repeated tags, and whitespace tag names are refused. First CLI-level coverage for either verb: #76 shipped them tested only at the artifact layer. closed check-in: 21f6efdd1c user: dmestas tags: record-cards | |
| 13:52 | merge tag-cards: raw-namespace tags, values, and cancel (#80) check-in: 6b4ac926e6 user: dmestas tags: trunk | |
| 13:26 |
tag: raw-namespace tags, values, and cancel (#80)
tag add could mint only T +sym-NAME, so a verdict like ci-pass became a symbolic check-in name and the second check-in carrying it made that name ambiguous. add and cancel now take fossil's --raw namespace choice, an optional VALUE, and --propagate; list --raw reads raw tags back, since they are deliberately outside the name space. Names under wiki-, tkt-, or event- are refused, as fossil refuses them. The composer's arguments moved into a TagCardSpec, which hides its two precondition asserts from the release-assert scanner (a struct field is not slice-shaped), so its baseline entry goes; the asserts stay. closed check-in: 3fa532af3a user: dmestas tags: tag-cards | |
| 12:49 |
commit --sign: clearsigned manifests, fossil's contract (#72)
The signed text is the stored artifact, so readers strip the wrapper to reach the cards and the signature travels wherever the check-in syncs. Signer invocation matches fossil's clearsign.c: COMMAND OUTFILE INFILE, default 'gpg --clearsign -o ', ssh* basenames take the detached path with assembled armor, 'off' disables. A signer that fails or alters the manifest refuses the commit rather than storing it unsigned. artifact reports signature presence; carry is pinned through pack, verify, and the sync wire. check-in: eb2f1adeda user: dmestas tags: trunk | |
| 05:35 | wiki and ticket leave the top-level command list: the four authoring verbs are machine verbs by the #76 posture (another process is their audience), each still fully documented by its own help check-in: d18a3b5589 user: dmestas tags: trunk | |
| 05:17 | merge worktree-registry: the store-side worktree list and the worktrees verb (#70); verb-set union with forum/technote (#76) check-in: d88bdc5e67 user: dmestas tags: trunk | |
| 05:01 | worktree registry (#70): every open and update stamps the tree's absolute path into the store's .zit-cache/worktrees (advisory tier, best-effort, self-healing — the store-cone discipline); 'zit worktrees' lists the registered trees with branch and check-in, verifies membership by the store file's inode, lazily prunes dead or re-pointed entries, and answers bare via -R closed check-in: 9d079f3009 user: dmestas tags: worktree-registry | |
| 05:01 |
forum and technote authoring verbs (#76)
zit forum post/reply and zit technote add record forum-thread and event artifacts under the #71 machine-verb contract: bare store via -R, hash-only stdout, exact exit codes, unlisted in the human help. A reply resolves its thread root transitively; a technote edit reuses its event id. check-in: 4317c47ed5 user: dmestas tags: trunk | |
| 04:27 |
D cards carry milliseconds (#75)
Every artifact-minting path writes the .SSS writer form fossil itself emits, so same-second writes keep a real-time order for latest-wins consumers. SOURCE_DATE_EPOCH pins .000; display surfaces unchanged. check-in: 0ff8e58ce8 user: dmestas tags: trunk | |
| 04:05 | e2e: the import-fossil residue scans iterate a dedicated .iterate handle scoped to the scan — Linux dirRead re-seeks the fd per batch, so iterating a handle owned elsewhere is BADF there (macOS tolerates it) check-in: 68506d0e3e user: dmestas tags: trunk | |
| 02:37 |
sync: per-round credentials over the cookie channel (#74)
An authenticated exchange now carries a fresh credential on every round: body login until the peer's gate is known, then an x-f-l-c cookie whose nonce covers that round's own body. The transport attaches it; serve now verifies it (and refuses tampered, replayed, and duplicate logins) instead of ignoring the header its own announced version solicits. check-in: 1fd84b4327 user: dmestas tags: trunk | |
| 02:21 | zig fmt check-in: f7b57a5869 user: dmestas tags: trunk | |
| 01:51 | merge import-fossil: fossil history import with the compatibility consequence chosen at import time (#61) — --compatible preserves the corpus byte-exact in the origin's sync world; --submanifest re-mints history as sub-manifest trees under a new project code, gated by whole-history replay, proven file-set-equivalent pair by pair, published atomically, with the old->new provenance map shipped as a technote. Merge resolution unions the verb set with wiki/ticket (#71) and completes their top-help listing check-in: 3f1f2a257d user: dmestas tags: trunk | |
|
2026-08-15
| ||
| 23:20 | conversion under the staged-publication discipline plus the provenance technote (#61): the bundle's atomic rename is the only commit point (a failed run leaves a sweepable .zit-tmp-*, never a half-repo), and the old->new map ships as a deterministic event artifact inside the converted repository — sorted pairs, source-tip timestamp, project-code-derived id — so external hash citations resolve by lookup closed check-in: cbbc910841 user: dmestas tags: import-fossil | |
| 23:20 |
wiki and ticket authoring verbs (#71)
zit wiki commit and zit ticket new/set record wiki and ticket-change artifacts on a bare store via -R, under the store write lock. Machine verbs: hash-only stdout, exact exit codes, unlisted in the human help but fully documented via their own --help. check-in: fe8d14d754 user: dmestas tags: trunk | |