Skip to content
zit Artifact [9fa561b89d]
Login

Artifact [9fa561b89d]

Artifact 9fa561b89d20b3b1fc102cdad2dfb351ef30e178a291492a2a59b38933f6afa1:


zit 0.1.0 — Release Plan

STATUS: FINAL (armed — the release workflow may run. The workflow preflight refuses to run against a DRAFT plan. Flipping to FINAL also ratifies the two decisions embedded here: the BSD-2-Clause license choice and the creation of a private GitHub mirror named zit for CI. (Operator decision, 2026-07-18: BSD-2-Clause confirmed; the mirror is PRIVATE — publishing the repository publicly is a separate, later, explicitly-operator- gated decision. Nothing in this plan may make the code public.)

Source of truth for findings: docs/PRE-RELEASE-READINESS.md (F1–F7, S1–S7, N1–N4). Executor: /Users/dmestas/zit-plans/release-0.1.0/release.workflow.js.


0. Release thesis

Ship 0.1.0 as a library-first release with an honest-caveats CLI in roughly one focused week. Do not hold for fossil parity. The claim we release on: an independent, conformance-checked implementation of the four Fossil RFCs with an embeddable Zig API — carried by the smoke step running in CI against the real fossil binary. Seven blockers stand between here and that tag; nothing else does. The main.zig split is explicitly not on the path (post-release, N1) — the architecture is otherwise genuinely deep-module and a pre-release reorg buys users nothing and invites merge churn against concurrent sessions.

One framing correction carried into this plan: zit status not reporting edited files is a documented, deliberate deferral ("honestly deferred to commit/diff (B-status-16), stated in --help", main.zig:1080-1086) — not a silent lie. It stays a blocker because users don't read --help before running status, but it is a UX/completeness gap, and the fix is framed as completing the verb, not correcting dishonesty.


1. What "0.1.0 released" concretely means

All of the following, in this order of finality:

  1. All seven blockers (F1–F7) closed on the release-0.1 branch, each with its required tests/fuzz/e2e in tree.
  2. Full local battery green on the converged branch: zig fmt --check . · zig build · zig build test · zig build e2e · zig build dst · zig build smoke.
  3. release-0.1 merged to trunk; CI green on the mirror for the merge commit.
  4. build.zig.zon version 0.0.0 → 0.1.0; LICENSE present and listed in .paths.
  5. README.md carries an honest caveats table (see §3, P5b).
  6. docs/RELEASE-NOTES-0.1.0.md: what it is, what's proven (incl. the exact fossil version smoke passed against — S7), what's deliberately NOT done.
  7. Tag v0.1.0 on the trunk merge commit (fossil tag + git tag on the mirror), release notes written to docs/RELEASE-NOTES-0.1.0.md and attached to a release on the private mirror. NOTE: "released" here means tagged + noted on the private mirror — NOT a public announcement. Going public is a separate operator decision, deliberately out of scope.
  8. Cleanup: release worktrees removed, scratch files gone, mirror in sync.

The definition-of-done checklist in §8 is the mechanical form of this list.


2. Phase plan and ordering rationale

P1 License & hygiene (hours)
 └► P2 CI bootstrap (~1 day)
     ├────────────► P3 CLI cliffs        ┐  three parallel worktrees:
     │              F3 clone→open (1–2d) │  F3 ∥ F4 ∥ F5
     │              F4 add/rm rec. (~1d) │
     │              F5 status edits (½–1d)┘
     └────────────► P4 Fuzz hardening    ─  one worktree, F6 → F7 serialized
                    F6 transport/serve (~1d), F7 merge3 (½d)
     P3 + P4 both complete ─► P5 Release surface (help audit → docs/version)
                              └► P6 Converge & ship

Why this order:


3. Per-change specifications

P1 — License & packaging hygiene (F1 + S6) — hours, trivial risk

Scope Add LICENSE (BSD-2-Clause, copyright Daniel Mestas 2026). zit is an independent reimplementation from the RFCs, not derived from fossil source, so any license is legally available; BSD-2-Clause matches the Fossil heritage and the systems-Zig ecosystem. (MIT would be equally defensible — this is the one taste call embedded in the FINAL flip.) Add LICENSE to build.zig.zon .paths. Delete commit-description-413190fb92be991f.txt and add an ignore-glob for the pattern (S6). Commit this plan doc.
Acceptance LICENSE at repo root; named in .paths; scratch file gone; ignore-glob in .fossil-settings/ignore-glob; battery green.
Tests Amended from "none new — battery only" (explicit scope ack, not silent drift). P1 adds src/packaging_test.zig: structural pins over the shipped distribution — LICENSE content, LICENSE named inside the manifest's brace-matched .paths block, a line-anchored ignore-glob pin, and a negative-space pin asserting every repo-root path build.zig embeds is named in .paths. Justification: the P1 acceptance criteria are all file-structure facts, which rot silently because nothing executes them; the battery alone cannot catch a .paths regression. Caveat recorded because this addition was itself the vector for a packaging blocker — the embeds must attach to the test-only module (build_unit_test_module), never to the exported zit module, or a consumer fetching the package fails on a file outside .paths. The negative-space pin exists to catch exactly that recurrence.
Risk / size Trivial / minutes–hours.

P2 — CI bootstrap (F2) — ~1 day, medium risk (infra unknowns)

Scope (a) Establish a PRIVATE git mirror of the fossil repo at github.com/<owner>/zit via fossil git export (create with gh repo create --private if absent — the --private flag is MANDATORY; a public mirror is out of scope for 0.1.0 and requires a separate operator decision), with a scripted export step so later phases can push deterministically. (b) .github/workflows/ci.yml: on push (all branches) + PR — pin Zig 0.16.0, install fossil (for smoke), run zig fmt --check ., zig build, zig build test, zig build e2e, zig build dst, zig build smoke. Single ubuntu-latest gating job for 0.1 (macOS job is 0.2 material). Do not change local step semantics — e2e staying compile-only under zig build test is a good inner-loop design; CI runs the steps explicitly.
Acceptance First fully green Actions run on the mirror for release-0.1, including smoke against a real installed fossil. Export step is one command, rerunnable.
Tests The pipeline is the test; green run required.
Risk / size Medium — runner environment (fossil version, Zig install, smoke's spawned-server behavior in CI) is the unknown; budget the day for iteration. ~1 day.

P3-F3 — Clone → working tree: zit open — 1–2 days, highest risk

Scope Recommended shape (final call belongs to this step's Ousterhout round): implement a zit open verb — fossil-parity (fossil clone + fossil open is exactly fossil's own UX) and the deeper module (opens any repo file, not just fresh clones). open takes a repo path + workdir, writes the checkout record and managed set, and materializes the tip of trunk reusing the existing checkout.zig/update.zig machinery. runClone stays repo-only. Alternative if review prefers it: clone writes the checkout bootstrap itself. Register the verb (cli.zig Verb enum + dispatch + help).
Acceptance e2e proof chain: clone (from zit serve) → open → files on disk byte-match the source tree → update works in the opened tree → a commit in the opened tree round-trips. Smoke scenario: clone from a real fossil-served repo → open → file bytes match. open appears in help with an e2e pin.
Tests The e2e chain above (this is the missing proof the assessment called out — the existing clone test asserts digest-set equality only, e2e.zig:10209), plus the smoke scenario.
Risk / size Highest of the release — whether update works against a fresh clone's absent checkout record is unproven; expect to fix what the new e2e exposes. 1–2 days.

P3-F4 — add/rm directory recursion — ~1 day, low-medium risk

Scope Implement recursion at main.zig:452 / :679, honoring the existing extras.sweep exclusion walk — the exact pattern addremove (f0dc5f4e2a) already established: one scan, mutations only through ManagedSet.insert/remove/write. No second walker. Replace the "directory recursion is not yet supported" diagnostics and retire their e2e pins (e2e.zig:645,1034,1164).
Acceptance zit add . adds every non-excluded extra under the tree; zit add <dir> scoped correctly; zit rm <dir> removes tracked files under dir; control files / ignore-glob matches never added; --dry-run semantics if present stay consistent with addremove.
Tests e2e: nested dirs, exclusion globs honored, add . at root and in subdir, rm of a partially-tracked dir; updated diagnostics tests.
Risk / size Low-medium (proven pattern to copy). ~1 day.

P3-F5 — status reports edited files — ½–1 day, medium risk

Scope Complete B-status-16: full edited-file detection — hash tracked files against the current check-in, reusing the dirty-scan machinery commit already has. Add an EDITED row class to the render loop (~main.zig:1155-1160). Update --help to drop the deferral text. Frame: completing a documented deferral, not fixing a lie.
Acceptance Edit a tracked file → status lists it as edited; unchanged files not listed; added/missing behavior unchanged; help text updated. No performance claims without a benchmark (rule 7) — if the notes want to say anything about status speed, cite a measurement or say nothing.
Tests e2e: edit→status shows; touch-without-change (mtime bump, same bytes) → not shown; interaction with missing and added states.
Fallback of record If the track overruns its budget: ship the minimum honest version — a trailing notice line ("edits not shown; see zit diff") — and move full detection to 0.2.0 top-of-list. The workflow treats this as a legitimate DONE_WITH_CONCERNS outcome, recorded in the release notes.
Risk / size Medium. ½–1 day.

P4-F6 — Transport/serve fuzz targets — ~1 day, low risk

Scope transport_frame_garbage and serve_request_garbage seeded fuzz targets for the only surfaces reachable by an untrusted remote peer (transport.zig 469 L, serve.zig 593 L). sync_parsers_adversarial covers the layer above; these hit the HTTP framing itself. Register in the fuzz_tests.zig:21-46 table exactly like the existing 24; replayable seeds.
Acceptance Targets run in the standard fuzz invocation and in CI; N-iteration seeded run clean (no crash/leak/UB); any bug found is fixed in this step with a regression test pinning the seed.
Risk / size Low for the targets; unknown for what they find (that's the point). ~1 day.

P4-F7 — merge3 property fuzz — ½ day, low risk

Scope merge3_properties target with the four algebraic oracles: merge(b,x,b)==x, merge(b,b,y)==y, merge(b,x,x)==x, and no-invented-lines (every output line ∈ some input). Silent merge corruption is the worst VCS failure mode; this is the cheapest insurance on the list. Serialized after F6 (shared fuzz_tests.zig table).
Acceptance Same bar as F6: registered, seeded, CI-run, clean at N iterations, findings fixed with pinned-seed regressions.
Risk / size Low. ½ day.

P5a — Help-text audit (S2) — hours, trivial risk

Scope Every dispatched verb appears in shipped help: name sync (currently a live dispatch arm deliberately absent from help, cli.zig:2355-2357 — fine mid-development, wrong in a release), and audit clone/pull/push/remote/pack/open.
Acceptance e2e test asserting each dispatched Verb-enum member appears in zit help output (a structural pin, so the audit can't rot).
Risk / size Trivial. Hours. Must run after F3 (open exists).

P5b — Version, README caveats, release notes — ½ day, low risk

Scope build.zig.zon 0.0.0 → 0.1.0. README honest-caveats table: status/diff limits (rev-vs-rev diff absent — diff is working-tree vs current check-in only; status fallback if F5 shipped minimal), placeholder sync identity, deferred R/T/U/W card semantics, verbs missing vs fossil (stash, grep, ui, settings, rev-diff). docs/RELEASE-NOTES-0.1.0.md in the honest-diagnostic voice the codebase already has (it's a release asset): what's implemented, what the conformance claim rests on (smoke in CI against the real fossil binary — name the exact fossil version, S7), what's deliberately deferred and where each deferral is documented. Any performance number quoted (e.g. the pack/re-deltification sizes) must cite its existing measurement (bab9303056) — no unbenchmarked claims.
Acceptance Version stamped; caveats table complete against the final shipped behavior; notes reviewed at the design tier for voice and honesty; battery green.
Risk / size Low. ½ day. Strictly after P3+P4+P5a.

P6 — Converge & ship — ½ day, medium risk (concurrency)

Scope (1) Re-merge trunk → release-0.1 (trunk advances from concurrent sessions; expect and absorb their commits). (2) Full local battery incl. smoke on the merged tree. (3) GO/NO-GO judgment against the §8 checklist. (4) Merge release-0.1 → trunk (SQLITE_BUSY retry; re-merge again if trunk moved). (5) Mirror export; wait for CI green on the merge commit. (6) Tag v0.1.0 (fossil tag on trunk tip + git tag on the private mirror), publish the release with its notes on the private mirror only — no public announcement, no visibility change; going public is a separate operator decision. (7) Cleanup: remove /Users/dmestas/zit-rel/* worktrees, verify fossil extras clean, mirror in sync.
Acceptance §8 checklist fully checked.
Risk / size Medium — merge races with concurrent sessions; the retry+re-merge loop is the mitigation. ½ day.

4. Dependency graph & parallelism

Serialize: P1 → P2. Fan out: P3 (three worktrees) ∥ P4 (one worktree). Join: P5a → P5b → P6.

Worktree map (house pattern: parallel tracks are separate worktrees on the SAME branch, converging on the branch, then one merge to trunk):

Worktree Track Files touched (conflict surface)
/Users/dmestas/zit (trunk checkout) P1, P2, P5, P6 root files, .github/, docs, build.zig.zon, cli.zig help
/Users/dmestas/zit-rel/open F3 main.zig ~:2537+, cli.zig Verb/help, checkout.zig, e2e.zig, smoke.zig
/Users/dmestas/zit-rel/addrm F4 main.zig :452/:679, extras.zig (read), e2e.zig
/Users/dmestas/zit-rel/status F5 main.zig ~:1080-1160, e2e.zig
/Users/dmestas/zit-rel/fuzz F6→F7 fuzz_tests.zig, new *_fuzz files, build.zig

P1/P2/P5/P6 run in the trunk-side checkout but commit to release-0.1 (sequential phases — no self-conflict). Every track fossil updates immediately before committing to absorb sibling commits; if the update pulls nontrivial changes, re-run zig build test before committing. Existing zit-storage / zit-nextverbs worktrees are left alone — they belong to other lines of work.


5. Branch strategy

Single fossil branch release-0.1, cut from trunk tip at preflight. The phases are interdependent (help audit and notes describe the post-fix verb set), so a coherent branch lets CI validate the set together.

Continuous integration to trunk (operator decision, 2026-07-18). Every increment lands on trunk as soon as it passes its own gates — not one big-bang merge at P6. After a step commits on release-0.1, runStep immediately runs a land-on-trunk gate:

  1. Driven from the trunk worktree (/Users/dmestas/zit), never from the step's worktree — so parallel P3/P4 tracks don't each steer trunk.
  2. fossil update trunk → require a clean tree (a dirty trunk carrying another session's WIP blocks; never revert or stash someone else's work).
  3. fossil merge release-0.1 → any conflict or <<<<<<< marker blocks for human resolution; no silent semantic merges.
  4. Re-verify the merged tree (fmt/build/test, plus smoke on interop-relevant steps). Green-on-the-branch does not imply green-on-the-merge, because trunk advances under us.
  5. Commit the merge (guard enforced, SQLITE_BUSY/guard-flake retries, never bypassed). If trunk moved mid-merge, restart the sequence (bounded, 3×).

Rationale: smaller, reviewable merges; minimal divergence from a trunk that concurrent sessions keep advancing; each increment independently landed and revertible; conflicts surface at one-increment size instead of accumulating to release-sized. A failed land does not lose work — the increment stays committed on release-0.1 and the step reports landed: false.

By P6 trunk should already contain every increment; P6's merge becomes a formality (plus the final re-verify, GO/NO-GO, and tag). Tag goes on the trunk tip.

Concurrency rules (standing): SQLITE_BUSY on any fossil op → bounded retry with backoff; never inherit rule-violating files from concurrent sessions across a merge (inspect what the re-merge brings in); the zit-commit-guard pre-commit hook builds the main checkout and runs test/dst — it may flake under concurrent writers; retry, never bypass, never --no-verify.


6. Scope decisions — SHOULD-FIX / NICE-TO-HAVE disposition

Item In 0.1.0? Rationale
S2 help-text audit (sync et al.) Yes (P5a) Cheap; a hidden verb is wrong in a release.
S6 scratch-file removal Yes (P1) Minutes.
S7 fossil version in notes Yes (P5b) The conformance claim needs it.
Version stamp + tag + caveats table Yes (P5b/P6) Is the release.
S1 working-tree crash-consistency DST sweep 0.2 — first item Biggest post-release safety win (this is where a user's power-loss lands) and the harness (sim/faulty_file.zig) already exists — but no user-visible cliff, and the week budget belongs to the seven cliffs. Deliberately top of 0.2.
Delta/repo-state RFC vectors 0.2 Smoke-in-CI (P2) covers the conformance substance with stronger evidence (validation against fossil's own code); vectors are defense-in-depth that runs without a fossil binary.
S3 seam-coverage matrix (~14 "deferred to manifest assembly" promises) 0.2 Converts informal promises into one enforced invariant; valuable, not user-facing, not gating.
S4 parse-garbage fuzz (managed/undo/repocache) 0.2 On-disk-state hardening; pairs naturally with S1.
S5 artifact fuzz alphabet → R/T/U/W/Z 0.2 Those card semantics are themselves deferred; fuzz them when they land.
Extended smoke scenarios (bidirectional sync, delta-heavy) 0.2 Marginal conformance dollar goes furthest here — after the tag.
N1 main.zig split Post-release, explicitly deferred Shallow god-file, real debt — but a reorg buys users nothing pre-tag and invites merge churn. Seam and target shape already documented in the assessment.
N2 kcov cov in CI + floor 0.2 The kcov plan already exists (queued post-CLI); wire it once CI is stable.
N3 separate test-aggregation root · N4 repostate_crosslink_garbage Backlog No release relevance.

7. Quality bar (per step — the workflow enforces this; do not thin it)

Every P1–P6 step runs the full gauntlet built over the storage lever:

  1. Ousterhout design review (design tier, bounded ≤3 rounds): shallow module / leaky interface / pass-through check; complexity pushed down, not up to callers; lighter-mechanism check; premise check.
  2. Implement, TDD: failing test first (shown failing), then to green.
  3. STANDALONE TigerStyle audit — its own gate with its own agent, never folded into code review (bounded ≤3 audit→fix rounds).
  4. DST/property + seeded fuzz where the step touches simulated or fuzzed surfaces (F6/F7 are themselves this; F3/F4/F5 must keep zig build dst green and extend e2e).
  5. Full local verify battery: zig fmt --check . · zig build · zig build test · zig build e2e · zig build dst · zig build smoke where interop-relevant (F3, P6 always; others when they touch sync/serve).
  6. Commit discipline (the pack.zig lesson): check fossil extras, fossil add every new file, commit (SQLITE_BUSY retry), then verify a clean checkout of the new commit builds (fossil open into a temp dir → zig build + zig build test). Guard hook: never bypass.
  7. From P3 onward: mirror export + CI green on the branch is an additional gate per commit.
  8. Attribution: borrowed techniques cite author + codebase/paper in the code comment AND the commit message. No AI attribution anywhere.
  9. Rule 7: no performance claim without a benchmark, in code comments, commit messages, or release notes.

8. Definition-of-done checklist (P6 GO/NO-GO input)

9. Risks & contingencies

Risk Mitigation
F3 exposes deeper checkout/update breakage than 1–2 days absorbs It's the first track dispatched (longest pole). If it balloons: keep open minimal (materialize tip + records; defer flags), and let the e2e chain define "enough". If still blocked, the workflow halts that track and surfaces it — 0.1 does not ship without F3.
CI environment fights back (smoke in Actions) Budgeted into P2's day; smoke's server-spawn is the likely friction. If a runner-specific smoke issue can't be fixed same-day, gate on test/e2e/dst and file the smoke-in-CI gap as a named caveat + 0.2 item — an explicitly surfaced downgrade, not a silent one.
Concurrent sessions race trunk Re-merge before finalizing; SQLITE_BUSY retries; inspect merged-in content; guard-hook flakes get retried, never bypassed.
F6/F7 fuzz finds real bugs That's success. Fix-with-pinned-seed inside the step; if a finding is structural (e.g. a protocol flaw), the workflow halts the track and surfaces it rather than papering over.
F5 full detection overruns Recorded fallback: notice line + 0.2 deferral (see P3-F5).