44 check-ins that include changes to files matching '.github/*'
|
2026-10-13
| ||
| 02:40 | Release workflow: Zig 0.17.0 check-in: 5e81556352 user: dmestas tags: trunk, version-0.4.0 | |
| 00:49 | CI: thirty-minute ceiling; the arm64 battery takes about twenty-two under Zig 0.17 check-in: c3c6d43ad2 user: dmestas tags: trunk | |
|
2026-10-12
| ||
| 23:50 | Build with Zig 0.17.0; version 0.4.0 and its release notes check-in: 7ad0e1704f user: dmestas tags: trunk | |
|
2026-10-05
| ||
| 23:38 | release workflow: clear the macOS package directory before extracting into it check-in: 24836c1902 user: dmestas tags: trunk | |
|
2026-09-26
| ||
| 18:22 |
soak: print its log into the job, and let the artifact upload fail alone
The artifact quota is shared with every repository on the account; a full one failed the log upload on two nights the soak passed. The log is now printed into the job output, which is kept 90 days outside the quota, and the upload no longer decides the run. check-in: 96b430bb17 user: dmestas tags: trunk | |
|
2026-09-25
| ||
| 21:14 |
release workflow: keep its artifacts three days, not thirty
Every artifact it uploads is transit, published to the uv store and a GitHub release the day it is built. Thirty days kept ~40 MB per release against the 500 MB storage quota, and a full quota failed the soak's log upload. check-in: 29f07577ae user: dmestas tags: trunk | |
|
2026-09-24
| ||
| 01:02 |
soak: record a finding and keep sweeping; CI: cap the runners' zig cache
A failing fuzz target is abandoned and the sweep moves on, so one recurring crash no longer hides the targets after it; the exit lists every finding. Each workflow clears its zig-local cache whole at job start once it passes 8 GB, since Zig never prunes it. check-in: 8ebc3e4dd8 user: dmestas tags: trunk | |
|
2026-09-18
| ||
| 12:31 |
soak: a nightly deep DST and fuzz run
scripts/soak.sh passed the optimize flag this Zig rejects, so it has been unrunnable since the toolchain moved; it takes --release=safe now. A scheduled workflow runs it at 03:00 UTC on the x64 runner with the script's nightly budgets, overridable from the dispatch form, keeps the log and any reproduce line as an artifact, and is pinned to the ci toolchain by ci_test like the other workflows. check-in: fa78377389 user: dmestas tags: trunk | |
| 12:31 |
fuzz: reach the igot accept arm again, and sweep at the floor in ci
The adversarial token generator capped fields at 24 bytes, and the igot parser has required a 40- or 64-byte name since the shun work, so the parser's accept arm was unreachable and the harness's own reachability floor said so at 256 rounds. CI swept 100 rounds, below the floor, and never saw it; the first bounded soak did. One field in four is now hash-shaped, and ci sweeps every target at the floor's round count. check-in: af292c3091 user: dmestas tags: trunk | |
|
2026-09-17
| ||
| 17:04 |
Remove issue-tracker numbers from source and documents
The tracker is disposable and does not live with the source, so a number is a dangling pointer. Every zit#NN and #NN reference in code comments, test names, help text, smoke labels, identifiers, release notes and errata is replaced by enough context to stand alone, or by a short plain statement of the problem. No behaviour changed. check-in: 9033c13f21 user: dmestas tags: trunk | |
| 00:08 |
ci: run on every branch push, still never on a tag
A push trigger naming only a tag filter runs for tag pushes alone, so the tags-ignore alone ran nothing for two commits. branches: ['**'] restores every branch; the pin now allows exactly that form. check-in: c004c81e03 user: dmestas tags: trunk | |
|
2026-09-16
| ||
| 23:09 |
ci: keep the Zig cache and toolchain across runs
The runners are self-hosted but checkout cleaned the workspace, so every step recompiled the same sources into its own binary from nothing; unit tests and e2e were 6 and 4 minutes on the ARM64 critical path, mostly compile. Zig's cache is content-addressed, so it now lives outside the workspace and warms across runs, in both workflows. The toolchain tarball is kept the same way and still verified against the pinned digest every run. Also pins the tag-ignore trigger the previous commit introduced, which the workflow pin test had rejected. check-in: 3106100d42 user: dmestas tags: trunk | |
| 22:58 |
ci: do not run the battery a second time on a tag push
A release tag lands on a commit whose branch push already ran ci, and the tag push ran it again on the same three single-slot runners the release workflow was queueing for. Every branch push still runs; the release workflow, triggered by the tag, gates what it ships. check-in: e9e2ba4431 user: dmestas tags: trunk | |
|
2026-08-29
| ||
| 00:59 |
fossil-muscle-memory: a differential transcript through fossil and zit
zig build muscle runs one scripted session — 27 lines a Fossil user types most — through the real fossil and the real zit against local servers, and compares each step's exit-status class and whether the verb spoke at all. Deliberate divergences carry their reason, known gaps carry their issue; anything else fails the run. CI runs it beside smoke. Closes zit#162. check-in: 741f6d428b user: dmestas tags: trunk | |
|
2026-08-18
| ||
| 19:05 | release: qualify portable packaged binaries check-in: 4290d14e17 user: dmestas tags: trunk | |
|
2026-08-17
| ||
| 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: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: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 | |
| 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 | |
|
2026-08-16
| ||
| 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 | |
|
2026-08-14
| ||
| 20:17 | merge trunk check-in: 8b3d577ec1 user: dmestas tags: sparse-cone | |
| 18:27 | import-git: refuse git older than 2.45 by name before any work — first preflight query, without --no-lazy-fetch; CI verifies the minimum beside fossil (#62) check-in: 43efce758a user: dmestas tags: trunk | |
| 12:21 | merge sub-manifest: delta-manifest dialect, R cards, diff hunks, server identity, stash, open/log resolution, clone remote+progress, timestamp names; e2e expectations updated check-in: 8a45a021f9 user: dmestas tags: trunk | |
|
2026-08-13
| ||
| 16:09 | bench: qualify incremental git export scaling — memo parse bound pinned; althttpd/wapp export clean (fsck 0), phantom-parent lineage refuses naming both hashes check-in: 75fb61025d user: dmestas tags: sub-manifest | |
|
2026-08-06
| ||
| 17:33 |
citations: zero broken; CI gates on zero
The last 42 fell in two passes and a hand-repair. Round 6 fed each refuted proposal its reviewer's own objection back -- "off by one at the start", "range too short for a biconditional, it must decide both directions", "evidence stops on the dangling No". Those objections were near-solutions, and 38 of 40 second attempts passed review where the first had failed. Eight were then read and repaired by hand. Seven were ordinary rot. The eighth was not: the reviewer had refused a proposal for the nine-cause fatal-error set because the cited lines "neither state nor deny the claim", and it was right -- no passage states such a set, because it is zit's own enumeration and its members each carry their own citation. It now names the governing section with NO range. Inventing a span to satisfy the checker is the defect this sweep exists to remove. One of the nine was the checker's fault rather than the code's. sync.zig:4599 cites artifact-format section 3.2 L347-350, byte-exact and correct, but the words "artifact-format" sat on the previous line and the draft-name detection only reads the current one. Reflowed the comment. 964 citations, 964 resolving. The baseline is deleted: this started as a two-way ratchet over a 512-citation backlog, and with the backlog gone an allowance is only somewhere for new rot to hide. A citation whose lines fall outside the section it names now fails the build. test 3506/3506, fmt. Issue #18. check-in: 8d3c0927be user: dmestas tags: sub-manifest | |
| 16:37 |
citations: 126 repairs, most of them impossible to propose before
Every earlier pass asked only for a line re-key WITHIN the cited section. A citation whose claim lived in a different section could therefore only come back "unresolved" -- even when the agent had already found the right answer and written it into its reason field. Four rounds of that read as "these need a human", when what it actually showed was that the prompt never permitted the fix. This pass let each repair choose its own kind. Of 126 confirmed: 63 changed the SECTION, 41 re-keyed a range, and 22 rewrote a claim the drafts no longer make. 104 of those were structurally unproposable before. Unresolved fell from 67 to 5. The claim rewrites are the ones worth reading. reply_body_cap was documented as an RFC-mandated 512 MiB ceiling; section 4 L830-833 states the opposite, that Fossil imposes no such entity limit, so it is now documented as a zit-local cap -- the same repair the 65535-byte line bound got earlier. A verbatim quotation attributed to the draft, "The card MUST consist of exactly two whitespace-separated tokens", does not appear in it; section 10.2 L2118 says "Exactly two visible tokens are required." 42 review verdicts were refused and are not applied. Two refute agents died mid-run; their proposals were left unapplied rather than taken on trust, and one refute agent lost to a stalled stream cost 14 repairs that the completed run later recovered. Broken 172 to 42, 96 percent of citations resolving, 0 unresolvable sections. test 3500/3500, fmt. Issue #18. check-in: 76a2905055 user: dmestas tags: sub-manifest | |
| 14:27 |
citations: anchor appendix citations written as words
The section sign was not the only anchor in the tree. `App. A.2 L3102-3114` and `ABNF App. B.7 L3566-3575` name an appendix by word, so their ranges bound to whatever section sat further left, or to nothing. Three citations were invisible or misattributed for that reason. All three are repaired. The decoded version-gate text is App. A.2 L2547, "Decoded error: Fossil version 2.0 or later required." The transcribed gate-fire response body is L2542-2545 -- the error, clone_seqno, push and timestamp lines. The third keeps no range at all, deliberately. It cites "the seven pragma SP NAME LF productions that carry no argument", and Appendix B is flat now, with those seven SCATTERED through it: uv-push-ok at L2907, send-private at L2925, req-clusters at L2935. No single range covers them, so it names the appendix and stops. A span invented to satisfy the checker would be the same defect this sweep exists to remove, wearing a different costume. Surfacing three and repairing four takes broken from 173 to 172 while the checker gets strictly stronger. 947 citations, 775 correct, 0 unresolvable sections. test 3500/3500, fmt. Issue #18. check-in: 471255d369 user: dmestas tags: sub-manifest | |
| 12:33 |
citations: check every range under a section, not just the first
A citation may carry several ranges: `§9.3 L3278/L3320-3322`, `§7.7 L1465/L1469-1471`, `§4.6.4-m6 (L1014)`. Matching only `§SEC Lnnn` saw the first and was blind to the rest, so half a compound citation could be re-keyed while the other half stayed rotted -- on the same line, looking equally verified. An adversarial reviewer caught that during a re-key pass; the tool could not have. 342 line references sat outside the checker's view. Each now binds to the NEAREST PRECEDING section on its own line, which is the rule that makes this safe: a range written BEFORE any section on the line -- `L545-547 hands clone to §8.1-§8.4, and §8.2 L1504-1506` -- belongs to earlier prose, not to a section further right, and is left alone rather than misattributed. 122 became checkable; 12 leading ranges were correctly skipped. Ranges are never carried across a line break for the same reason. This also surfaced §10.5, which the old pattern could not see at all because a requirement id sits between section and range (`§10.5 r43 L2477-2479`). §10 is Error Handling with only §10.1; the send-private and client-url claims are §9.5 Private-Content and Discovery Pragmas (L1983-1985: "send-private enables private sync only with x ... Clients repeat it every private round"). Same -1 downshift already verified for §10.3 and §10.4. 21 sites renumbered, and not-a-section is back to 0. Baseline 80 to 173: 93 defects that were always there and are now visible. The population it measures grew 821 to 943. test 3495/3495, fmt. Issue #18. check-in: 43c89e841f user: dmestas tags: sub-manifest | |
| 01:33 |
citations: re-key 20 against the corrected sections
d4e0e0d6da fixed six renumbered sections and deliberately left their line ranges stale, on the reasoning that a correct section turns a stale range into an ordinary lookup. This is that lookup: 20 of 100 survived review. The 20 percent hit rate is lower than the pass before it, and the reason is worth recording. It is NOT that the renumbering targets were wrong -- §11.1 L2604-2607 reads identically before and after that commit, so those citations were never renumbered by it. What the pass found instead is a second, older layer of mis-sectioning underneath the first: claims about server-emitted error cards citing §11.1 Credential Replay when they belong in §10.1 Error Card. Fixing one generation of drift exposed the next. Two refusals name failure modes the applier could not have caught. One proposal re-keyed half of a COMPOUND citation -- sync.zig:961 carries `§9.3 L3278/L3320-3322` -- which would have welded a freshly verified range to a still-rotted one on the same line. Another submitted a quote that stopped mid-word, with the half of the claim it needed sitting in the sentence it cut off. The 66 unresolved are now the honest core: three independent adversarial passes have each declined to invent an answer for them. Baseline 100 to 80; 90 percent of citations now resolve. test 3495/3495, fmt. Issue #18. check-in: dcd6c48df9 user: dmestas tags: sub-manifest | |
| 00:26 |
citations: stop plan references wearing section notation
Issue #18: "a reference to a plan must not wear § notation." 80 sites across eight files cited `§E`, meaning item E of storage-track-plan.md -- a document that is not a draft and is not even in this repository. They read exactly like spec citations, including in test names. `§E` is unambiguous: no draft defines an Appendix E, and every site sits in the storage family (checkout, logstore, managed, pack) whose headers already name storage-track-plan.md. Bare references become `plan E`; where the filename is already present it reads `storage-track-plan.md item E`, which says the same thing without the false notation. One carried a line number, `§E L119`. A line cite into a file outside the repository cannot be verified by anything, so the range went with it. That was the last `not a section of any draft` in the tree: 1 to 0. Baseline 101 to 100. Deliberately NOT swept: §A and §B, which are real draft appendices -- §A alone has 162 subsection citations, and a blanket letter sweep would have destroyed every one. §C (78 sites) is left for a separate ruling: it looks like the same plan, but Appendix C exists in three drafts, so it needs the discriminator applied per site rather than in bulk. test 3495/3495, fmt. Issue #18. check-in: ee4e0e623a user: dmestas tags: sub-manifest | |
|
2026-08-05
| ||
| 23:53 |
citations: stop claiming spec authority for two local rules
Two comment families asserted RFC requirements the drafts do not make. The 65535-byte line bound is ours, not the protocol's. §5.2 L962 says "This protocol imposes no separate maximum line length" and §11.3 L2199 repeats it, yet four tests named it an RFC bound and max_line_len's doc cited §5.2 as its source. The constant stays -- a finite receive window is a resource limit, and §11.3 L2200-2201 leaves payload limits to "declared counts and storage policy" -- but it is now documented as local policy, including that a conforming peer may legally send a longer line and we refuse it. Issue #32 justified this constant as a principled tripwire on the strength of that citation; the doc now records that the justification was void. The §3.10 comments reasoned about a section that no longer exists -- "§3.10 restates §3.2 and defers to it, so introduces no new order rule". The argument had no subject. Each test asserts real behaviour and is kept; the citations now name the section that actually governs it, and r1 points at §3.2 L376-377, the rule the deleted section deferred TO. not-a-section 6 to 1. Baseline 106 to 101. test 3495 (1 environmental e2e skip, no failures), fmt. Issue #18. check-in: 8aa2cf2c35 user: dmestas tags: sub-manifest | |
| 22:47 |
citations: re-key 29 more, mostly worked-example appendix cites
Second pass over the leftovers after the bulk re-key. 29 of 107 survived review; 10 were refuted and 68 returned unresolved, which is the right shape for leftovers -- the easy ones were already gone. Most are Appendix A worked-example citations, now visible to the checker for the first time: the round-1/round-2 request and reply bodies, their igot and gimme runs, and the SHA3 gate response. An example transcript is often the better citation for a claim about concrete wire bytes than the prose that governs them. The refusals stay instructive. One proposal was rejected because the directional half of its claim lives in §3.2.1 Round Message Layout rather than §6.1, so it was mis-sectioned rather than mis-lined; another because its range stopped mid-sentence, dropping the words the claim rested on. Baseline 135 to 106. test 3495/3495, fmt. Issue #18. check-in: 22d23eae1c user: dmestas tags: sub-manifest | |
| 22:40 |
citations: repair six section renumberings the errata left behind
Six cited sections do not exist in the drafts at all, so no line range could ever have been right. Each was renumbered, and each successor is confirmed by subject match against the section's own text: §B.4 -> §B Appendix B flattened; cookie-card, url-login-value, pct-user, unreserved and pct-encoded all still exist, every citation exactly 1787 lines above its production §12.1 -> §11.1 Credential Replay §12.2 -> §11.2 Private-Content Exposure and Protected Transport §10.3 -> §9.3 Configuration Exchange -- "the server MUST require Admin; otherwise emit error not authorized to push" §10.4 -> §9.4 Commit-Lock Pragmas -- every site is ci-lock/ci-unlock §9.1.1 -> §8.5.1 Unversioned-File Synchronisation -- "status 3+MTIME-CMP (2 older, 3 identical, 4 newer)" The §B citations carry corrected line numbers, each asserted to land on the production its comment names. The rest carry the section only: once the section is right the stale range is an ordinary "outside" case, which the re-key pass already handles, and inventing a range here would be the clamping this work exists to prevent. Deliberately untouched, all needing a decision rather than a lookup: §12.3 (successor §11.3 states the OPPOSITE -- "This protocol defines no separate per-line byte limit before LF" -- while four tests assert a 65535-byte bound as an RFC requirement), §3.10 (removed; the comment reasons about the section's own existence), and §E (a reference to storage-track-plan.md wearing §-notation, which the issue forbids). not-a-section 36 to 6. Baseline 144 to 135. test 3495/3495, fmt. Issue #18. check-in: d4e0e0d6da user: dmestas tags: sub-manifest | |
| 22:19 |
citations: re-key 368 in the sync pair; cover appendices
Of 475 candidates, 368 were located by reading the draft text that supports what each comment claims and survived adversarial review; 23 were refuted and 84 returned unresolved. sync.zig and sync_test.zig now resolve at 86 percent, up from 39. The refusals are the useful part. Several proposals verified byte-for-byte and still pointed at the wrong sentence; one range truncated away the half that carried the claim; one cited text that REFUTES its comment. The unresolved are mostly mis-sectioned rather than mis-lined -- §9.1 cited for push/pull card grammar, §10.1 for the client-version pragma -- and one where an erratum inverted the claim, so the COMMENT is now wrong and no line range would fix it. Those are left for a section decision. The checker also could not see `§A.1`-style citations at all: 36 of them, unverified while looking checked, 25 of them broken. Appendix headers use `Appendix A. Title` and `A.1. Title`, so both forms are recognised now. Baseline 477 to 144, over a population that grew 781 to 817. test 3495/3495, fmt. Issue #18. check-in: 18e5fa7bbb user: dmestas tags: sub-manifest | |
| 21:55 |
citations: re-section four claims from §4.1 to sync §7.7
All four assert that the artifact hash names the uncompressed, fully reconstructed bytes, and cited §4.1 for it. No §4.1 in any of the five drafts says that -- §4.1 defines the container. The statement is sync §7.7 L1465, "Integrity hashes reconstructed artifact bytes, not wire payload." These were the ones the re-key pass returned unresolved rather than guessing at: wrong section, not drifted lines, so no line range would have been right. Naming the draft because §7.7 also exists in artifact-format. Baseline 481 to 477. test 3495/3495, fmt. Issue #18. check-in: f1e10128f8 user: dmestas tags: sub-manifest | |
| 21:16 |
citations: re-key 31 verified line ranges, baseline 512 to 481
Each was located by reading the draft text that supports what the code comment actually claims, then adversarially re-checked against a verbatim quote of those lines. Clamping a range into its section would satisfy the checker while pointing at the wrong paragraph, so the checker alone was never the gate. Of 33 proposals, 2 were refuted on exactly that ground -- the quote verified byte-for-byte and still did not state the claim -- and 4 came back unresolved because no draft's §4.1 states what those comments assert at all. Those are mis-sectioned, not line-rotted, and need a section decision rather than a re-key. Also restores container.zig's `decode` doc block, which 7d26a31305 left stranded above `decodePrefix` when that function was inserted between the comment and the function it describes. test 3495/3495, fmt. Issue #18. check-in: e5db38a6df user: dmestas tags: sub-manifest | |
| 19:41 |
citations: add a containment checker and pin it in CI
Most `§X.Y Lnnn` citations no longer point at the text they name. Line numbers rot each time a draft is re-rendered for an erratum, and a citation resolving to the wrong paragraph is worse than none: it looks authoritative. scripts/check-citations.py parses section spans out of each draft and asserts every cited range falls inside the section it names. It reproduces the survey in #18 -- 65 sections-not-found exactly, 674 vs 667 checked, the delta being citations a wider regex catches. It splits on "\n" rather than using splitlines(), because drafts carry form-feed page separators that splitlines() also breaks on, drifting every line number further the deeper into the draft it sits. That bug makes correct citations look broken, and it is why this is a script with a reproduced baseline rather than a one-off measurement. CI pins the count at 512 and fails in BOTH directions, so a new broken citation and a fixed-but-unlowered baseline are each caught. Re-keying the backlog stays separate work; this stops it rotting further. Also drops parseGimmeCard's assert restating the check above it. parseIgotCard's remaining assert tests a different property and stays. test 3495/3495, fmt. Issue #18. check-in: 0f80982412 user: dmestas tags: sub-manifest | |
|
2026-07-31
| ||
| 02:56 |
git-import: translate a Git fast-export stream into a zit history
`zit import-git SOURCE DEST` reads a Git repository through `git fast- export` and builds a zit repository from it. The destination appears only after the export, artifact creation and publication have all completed, so a failure anywhere leaves no half-published repository behind; this is the first caller of the logstore artifact batch added earlier on this branch, which is what makes a check-in's blobs arrive as a unit. Mode 160000 now parses as a gitlink instead of error.UnsupportedMode. That completes the split begun in the previous check-in: reporting what the stream said is the parser's job, and deciding that zit has no submodule concept is the importer's. The refusal still happens, but it now explains itself instead of failing as a parse error on input git legitimately emits. Signed commits are refused by default rather than imported with the signature silently dropped. Fossil has no signature card, so importing one is a loss the operator should choose; --drop-signatures accepts it explicitly. The sub-manifest parameter is analyzed, not guessed. Preflight measures the source's own name set and pins its measured optimum, and --submanifest-k overrides that only to match another repository's existing pin, which is what lets two repositories share sub-manifests by content address. On a 100,000 file corpus the analyzed value cuts the objective by roughly 3.4x against the fixed default it replaces. Correctness is checked differentially against real Git rather than against fixtures we wrote: every materialized tree is compared to `git ls-tree` for the corresponding commit, refs and annotated tags are compared to the source, and both comparators now assert that a gitlink is absent rather than merely tolerating it, so the omission is a contract instead of an accident. The rest is wiring. extras learns the import provenance names, root.zig exports the importer modules across the executable and library boundary, tidy and the CI step guard learn the new test roots and build steps, and the e2e help check learns that a verb spelled import_git in Zig is import-git on the command line. check-in: dc0aa2f858 user: dmestas tags: sub-manifest | |
|
2026-07-20
| ||
| 21:26 |
Release zit 0.1.1
Harden native Linux filesystem behavior, deterministic repository iteration, update and undo transitions, Fossil smoke-process containment, and the three-platform CI matrix. check-in: be97df0398 user: dmestas tags: trunk, version-0.1.1 | |
|
2026-07-19
| ||
| 15:25 |
CI: fuzz sweep at 100 rounds per target, not 2000
The sweep could not finish inside its own 10-minute step timeout. Per-target cost varies by roughly 276x across the corpus: artifact_parse_garbage runs 100 rounds in 0.18s, while logstore_pack_same_path_round_trip takes 49s because it performs a full store pack every round. At 2000 rounds that single target needs about 16 minutes on its own. The original figure was extrapolated from one cheap target on the assumption that per-round cost was roughly uniform. It is not, and the assumption was never measured. 100 rounds is measured: the full 27-target sweep takes about 76s. This makes CI fuzzing a regression check — every target still runs clean on every push — rather than a bug hunt. Finding new defects needs depth, and depth belongs in an out-of-band run with its own budget, where a long sweep costs nobody a merge. check-in: 861b223e3c user: dmestas tags: trunk | |
| 01:09 |
CI: sweep every seeded fuzz target, and bump checkout to v5
Two gaps the first green run exposed. The repository carried 26 fuzz targets that CI never fuzzed. They reached CI only through their fixed-seed smoke tests inside `zig build test`, so the fuzzing investment was half-realized: the targets compiled and ran a handful of canned cases, but nothing swept them. `zig build fuzz` runs one target per invocation, so the new step loops. The target list is read from the step's own usage output rather than kept in the workflow — a target added to fuzz_tests.zig is swept automatically, with no second list to drift. Seed and rounds are fixed (1, 2000), not random: a red run has to be reproducible verbatim on a developer machine. Bare `zig build fuzz` exits non-zero because it is a usage error, so the enumeration tolerates that; every real invocation still fails the job. Verified by running the identical block under bash — 26/26 targets pass at seed 1. src/ci_test.zig moves with it. `fuzz` leaves not_in_ci (its stated reason was that wiring a bounded CI invocation belonged to a later step — that step is this one) and a new ci_parameterized table records it as covered-in-a-loop, carrying the exact anchor that proves the invocation exists. The anchor is verified, not trusted: an entry whose anchor drifts out of the workflow fails like any other gap. That distinction is kept separate from not_in_ci on purpose — conflating 'runs, in a loop' with 'deliberately skipped' is how a step silently stops being covered while a stale excuse keeps the pin green. actions/checkout v4 -> v5 clears the Node 20 deprecation warning the green run reported. check-in: 394b4efa27 user: dmestas tags: trunk | |
| 00:29 |
CI: target the self-hosted macOS runner instead of a hosted Linux one
GitHub-hosted runners are unavailable to this repository: it is private, and private-repo Actions minutes are billed, so the first hosted run never started — 'recent account payments have failed or your spending limit needs to be increased', zero steps executed. The gate now runs on the self-hosted mac-mini-zit (macOS/ARM64), which is online and already carries the toolchain. - runs-on names the specific machine ([self-hosted, mac-mini-zit]) rather than bare self-hosted, so a second runner joining the account cannot silently pick this job up on a different OS or architecture. - The Zig tarball is the aarch64-macos build with its published digest from ziglang.org/download/index.json. macOS ships shasum rather than GNU sha256sum, so the digest is compared explicitly; a swapped tarball still fails the job rather than building silently. - fossil is verified rather than apt-installed — the runner is a developer machine carrying 2.28, newer than what apt ships. The step hard-fails when fossil is absent, because a skipped smoke step would silently stop exercising the conformance claim the release rests on. src/ci_test.zig moves in lockstep: the runs-on line, the digest constant, the digest-check form, and the fossil-guard pin all now assert the macOS shape. Pins and workflow must change together or zig build test goes red, which is the point of having them. check-in: c8cccb2723 user: dmestas tags: trunk | |
|
2026-07-18
| ||
| 22:03 |
Add CI: private git mirror + GitHub Actions running the release battery
CI is the multiplier for the rest of the 0.1.0 sequence: from here every fix lands with fmt/build/test/e2e/dst/smoke gated automatically, and the smoke conformance claim — zit artifacts and deltas validated against the real fossil binary — becomes continuously enforced instead of milestone-manual. - .github/workflows/ci.yml: single ubuntu-latest gating job on push and PR. Zig 0.16.0 pinned by version AND sha256 digest, verified with sha256sum --check --strict before use; fossil installed for smoke; the battery runs in order with a 20-minute job ceiling and a 5-minute bound on smoke, which is the one step that can hang without producing output. - src/ci_test.zig: structural pins over the workflow, so the file cannot drift from what it claims. Each pin is anchored to a whole line with its YAML indentation — a loose substring is satisfied by the phrase appearing in a comment while the block that governs the run says something else. The digest pin names ZIG_SHASUM and the tarball together, so the digest this repo pins is provably the digest the workflow checks. - src/packaging_test.zig: a negative-space pin asserting every path build.zig embeds is version-controlled. This diff is why: build.zig came to depend on .github/workflows/ci.yml while that file was untracked, and bare 'fossil extras' does not list dotfiles, so the commit-discipline check reported clean. The pin runs 'fossil ls' and fails on any embed that is not tracked. The mirror is PRIVATE (github.com/danmestas/zit) and stays private; publishing is a separate decision that is deliberately not part of this release. check-in: 8a003b2ada user: dmestas tags: trunk | |