Files
alkgit/docs/plans/implementation.md
T
glm-5.3-flash 2de7ca08ff docs(tasks): decompose wave 5 — core validation & release
Eight task files under tasks/implementation/core/ (review 005's
dispositions absorbed; no remediation wave):

- core/w5-session-split — review 005 R-1 first (flat siblings +
  helper dedups + R-3's constants; zero public-API change)
- core/w5-size-bomb-bound — review 004 R-2 pre-publish (the cap
  shape decision + the doubles' incremental inflate + the
  hostile-declared-size regression)
- core/w5-oq19-positive-acks / core/w5-oq19-real-client-tokens —
  OQ-19 riders 2 and 3; rider 1 executes as the existing
  architecture/n5-unborn-head-rider (the tracker's depends_on now
  spans all three)
- core/w5-fuzz-harness — the fuzz/ sidecar + targets over R-5's
  inventory + the corpus gate joining the verification list
- core/w5-release-readiness — crate docs, feature matrix,
  CHANGELOG/README creation, publish dry-run
- core/review-wave-5 — the release gate (OQ-19 dispositions, corpus
  gate, size-bomb disposition, the counting-reader decision, N-1's
  read, the OQ-03 freeze inventory)
- core/w5-publish — the publish checklist over the gate (manifest
  sanity + 0.1.0 bump, tests, CHANGELOG check, dry-run, the
  crate-qualified alkgit-v0.1.0 tag, cargo publish)

Plan updated: wave-5 decomposition record + flagged interpretations,
the review-gates bullet, wave-5 status decomposed. taskgraph validate
(54 tasks) green.
2026-10-23 06:11:57 +00:00

39 KiB
Raw Permalink Blame History

status, last_updated
status last_updated
draft 2026-10-23 (wave 5 decomposed — eight task files under tasks/implementation/core/, decomposition record below; review 005's dispositions absorbed. Track B (waves 6-10) remains sketched; wave 6 decomposes after the core's publish.)

alkgit — Implementation plan

Wave-based decomposition of the architecture (docs/architecture/) into units of implementable work. This is a deliberate deviation from the SDD process's decompose-everything-upfront step, adopting the sibling pattern proven on alkstore and alkblobs (the ADR-025 consequence naming the precedent): the architecture is large, early waves change the shape of later ones (wave 1's seam types and trait signatures are what every later task implements against; wave 2's fetch machines are what wave 3's receive-pack composes beside), and decomposing only the next wave at a time keeps each session's task set reviewable and lets later decompositions absorb earlier waves' course corrections — reviews sometimes change things, and tasks downstream then have to be edited, so decomposing too far ahead is paid rework.

Rhythm: decompose a wave → implement it → review gate → decompose the next wave. Task files live in tasks/implementation/ (frontmatter carries the categorical estimates; the DAG ordering keeps parallel branches from stepping on each other). Wave boundaries are also review boundaries. Each wave's section here carries a decomposition record added when that wave actually decomposes (not written upfront).

Scope authority: v0.1.0 publishes both crates — the protocol core alkgit (complete: wire layer, state machines, the five-trait seam, GitSession, ops) and the composition alkgit-substrate (ADR-019's entirety, LFS included — LFS is v1 scope per ADR-022). Doors stay out of these waves: the alkhttp git/lfs features land in the alkhttp repo after the core's first publish (doors.md sequencing; POC-3/POC-L's reference implementations cover the interim), and alkssh is entirely future. An assembly of both published crates plus the native path is the complete v1 deliverable.

The waves

Two tracks, staggered on ADR-025's anchored-basis posture: the protocol core decomposes, completes, and publishes first — the substrate track builds against a published anchor rather than a sibling in flux, and its waves do not decompose until the core's release gate lifts.

Dependency logic in one line: seam → (fetch machines ∥ wire transport) → receive-pack → consumer half + ops → [wire validations ∥ fuzzing] → core release → substrate foundation → objects → ingestion → LFS → substrate release.

Track A — the protocol core (alkgit)

Wave Contents Depends on Status
1 Workspace conversion; error model; seam types + record schemas; the five trait signatures; Limits; session substrate types; test convention — decomposed
2 Native transport + preamble; stateless substrate; V2 advertisement + ls-refs; negotiation ack loop; GitPackGen over doubles; ACL + authorize wave 1 decomposed
3 V0 push advertisement; push state machine; bounded packless peek; GitPackIngest over doubles; status report framing wave 2 decomposed
4 GitSession (construction wrapper, ls_refs/fetch/push); the git/repo/* op family + gate wave 3 decomposed
5 OQ-19 wire-validation sessions; fuzzing; crate docs; feature matrix; the core's publish wave 4 decomposed

Track B — the shipped composition (alkgit-substrate)

Decomposes only after wave 5's gate lifts. The sketches below are pre-decomposition commitments in the plan, not task material.

Wave Contents Depends on Status
6 Substrate scaffold + alkgit version pin; migrations (ADR-024, day-one LFS refcount); refs row tables + row CAS; the registry adapter family + reactive halves core published (wave 5) sketched
7 alkblobs git-object-family wiring; the substrate Find front; GitPackGen/common_haves real impls (+ OQ-19 rider (b)'s substrate facet) wave 6 sketched
8 GitPackIngest: unpack-to-loose, pin bridge + pending-pin registry, connectivity walk; end-to-end push/fetch against real git wave 7 sketched
9 Reference rows + day-one refcount; liveness wiring; ops composition; batch adapter wave 8 sketched
10 OQ-21 composition-validation POC; docs; feature matrix; the substrate's publish wave 9 sketched

Wave 1 — The seam

The contract surface in code, engine-blind: everything later waves implement against, and nothing that needs a backend to test. The alkgit analog of alkblobs' foundations wave.

  • Workspace conversion — ADR-025's manifest surgery from the first commit: the root manifest becomes the workspace (two members: alkgit at the root's src/, alkgit-substrate under substrate/), the crate manifests reworked per the placement table (backend.md §crate and feature model: alkgit's sha256 off-by- default; alkgit-substrate's sqlite on, postgres/lfs per ADR-025's defaults), the pinned-version rule over path deps (alkblobs 0.2, the alkstore family 0.1, alkcall 0.10 — never sibling-checkout paths).
  • Error model — RegistryError (the registry family's five variants, ADR-012 §1 as re-scoped by ADR-018 §7) and StorageError (the object-storage family, ADR-018 §7); the wire-mapping table is transport-layer business; no catch-alls anywhere.
  • Seam types (ADR-018 §5) — RefLine/RefUpdate/RefOutcome/ PreparedPush/PushOptions and the RepoRecord serde schemas (backend.md §registry types — the N-3 draft: no path fields, the ADR-015 three-action grant shape, the opaque grant-key rule, additionalProperties: false on op inputs); the repo-id grammar (review 002 R-5, pinned once — backend.md §repo-id grammar).
  • The five trait signatures (ADR-018 §4) — the #[async_trait] family exactly as pinned; the repo: &RepoRecord parameter rule (§2 as amended on the substrate baseline — record carries no path).
  • Limits (ADR-009 + ADR-020 §1's new bounded-peek field; name pinned at implementation) — the budget model declared from the first commit, every session carries it.
  • Session substrate types (ADR-005) — the duplex + stateless machinery shapes, pkt-line framings under them.
  • Test convention — the in-memory backend doubles (the contract-reference analog of alkblobs' mem engine): the doubles implementing the five traits in-crate for the protocol waves' conformance work; every protocol wave's gate runs its machines against them. The convention (row/stamp discipline adapted from the sibling crates) is established here even though the harness compiles with wave 2's first machines.

The wave-1 review gate is the cheap-place gate: the seam types and trait signatures are one-way doors (they enter OQ-03's freeze inventory at the core's first publish — convention 9). A shape error found at this gate costs a task revision; found after the machine waves implement against them it costs signature churn across five workstreams. The gate reads the seam against ADR-018 §2–7 line by line, amended-status text included.

Decomposition record (2026-10-20): wave 1 decomposed into ten task files under tasks/implementation/core/ (track-A tasks live under tasks/implementation/core/; track B gets tasks/implementation/substrate/ when it decomposes). Shape: core/w1-workspace-conversion first (the ADR-025 manifest surgery everything lands inside); then four independent seam tasks — core/w1-error-model, core/w1-seam-types, core/w1-repo-record (record schema + repo-id grammar), core/w1-limits (the peek field's name pins here); core/w1-trait-signatures over those four; the substrate machinery as core/w1-substrate-duplex (framing primitives + error taxonomy) with core/w1-substrate-stateless building on it; core/w1-test-doubles over the trait signatures; and core/review-wave-1 (the one-way-door gate) depending on all nine. Interpretation recorded: wave 1 lands the substrate machinery shapes and pkt-line framings (ADR-005 §1–2); the serving entry points, service-selection wiring, and state machines are wave 2's transport work.

Wave 2 — The wire serves fetch (dependency-free)

The first machines, chosen so the whole fetch story is provable with zero substrate dependencies (in-memory doubles only):

  • Native transport + preamble (ADR-002/016) — GitAdapter, the git-daemon request-line grammar, the {repo, service} open-op params, the channels open op (register_openable), the authorized-repo marker flowing past the ADR-007 check.
  • Stateless substrate (ADR-005) — the stateless request/response entry point the http door will mount.
  • V2 advertisement + capabilities (ADR-003's honest list) + ls-refs (peel/symrefs/ref-prefix arguments; the unborn symref line shape).
  • Negotiation ack loop (ADR-014) — no-done rounds (acknowledgments section, NAK + flush, never ready), the stateless done round with done-closure generation, common_haves driving the ack check behind the seam.
  • GitPackGen over doubles — ADR-004's pipeline composed against the doubles' Find; missing-object aborts; the generation budget.
  • ACL + authorize (ADR-007 before any advertisement/ref line; ADR-011/015's policy function — public+read anonymous-first-class, write always authenticated+granted; unknown-repo ≡ unauthorized).
  • Ref-name validation + deny-list (ADR-013 §9's transport-side home, wired where the machines consume PreparedPush.updates).

Wave 2 decomposes after the wave-1 gate, absorbing whatever it corrects.

Decomposition record (2026-10-21): wave 2 decomposed into nine task files under tasks/implementation/core/ (the wave-1 gate is green — core/review-wave-1's verdict; nothing needed absorbing). Shape: three independent foundations first — core/w2-authorize (the ADR-011/015 policy function + the authorized-repo marker type, the gate surface every entry consumes), core/w2-refname-validation (the ADR-013 §9 transport-side home), and core/w2-packgen-doubles (the doubles' generate wired to ADR-004's real pipeline over the mem Find — the done round's provability); then the two substrate-agnostic machines in parallel — core/w2-v2-advertisement (advertisement + ls-refs) and core/w2-negotiation (the ack loop + done round, over the wired doubles); then the two serving entries in parallel — core/w2-duplex-serving (advertisement-once + command loop + wall clock) and core/w2-stateless-serving (the per-POST entry the http door mounts); then core/w2-native-transport (GitAdapter + the request-line grammar + the channels open op) over the duplex entry; and core/review-wave-2 (the capture-level gate) depending on all eight.

Interpretations recorded:

  • The machines are substrate-agnostic (both serving entries feed them, ADR-005 §3); the serving entries own ordering (ACL-first via the marker), budgets (wall clock, rounds, the concurrency semaphore's construction), and error mapping — the machines stay pure protocol.
  • The ref-name-validation home lands in wave 2 as the plan places it, with its consumption wiring in wave 3's push machine (the PreparedPush.updates point); the function is crate-public so the interim is not dead code — additive pre-publish, the gate confirms the placement.
  • The common_haves contract reads as exists + is-commit per ADR-014 §5 and backend.md §trait-family bullet 4; backend.md's pinned-signatures sentence ("existence is the operative predicate here — the is-commit refinement is the wire layer's ACK-line rule") reads as the done-round-subtraction note — flagged for the wave-2 gate to confirm or fix as docs drift.
  • The pipeline-concurrency permit (ADR-009's enforcement point) lands with the negotiation machine (the first generate call site); the shared semaphore is constructed by the serving entries from Limits::max_concurrent_blocking_tasks.
  • The stateless serving entry includes the advertisement emission for the door's future info/refs route (the capability dump as a stateless response) — the door itself stays out of these waves.
  • The channels establisher maps resolution and authorization failures to one channel:open_failed reason with a generic message (ADR-008's indistinguishability rule outranks the alktty vocabulary's unknown-resource/ACL-denial split at this gate) — flagged for the wave-2 gate to confirm.

Wave 3 — receive-pack

The push machine (ADR-013 as amended by ADR-020, all against doubles):

  • V0 push advertisement — caps NUL-attached on the first ref line, the capabilities^{} sentinel for empty repos, the served set per ADR-013 §2, private repos advertise nothing (ACL first).
  • Request parsing — command lines, shallow rejection up front v1, push-options section (gated off v1), deletes/creates zero-id shapes.
  • The bounded packless peek (ADR-020 §1) — duplex scoping, the four ordering rules verbatim, the named Limits field, the partial-header-is-malformed rule.
  • GitPackIngest over doubles — prepare/apply split (the single CAS home in GitRefs::apply_updates), CAS timing per ADR-013 §7's observed order, atomic semantics, thin-pack default acceptance.
  • Status report framing (ADR-013 §8) — band-1-wrapped with its inner flush when sideband was selected; bare otherwise; the substrate-owns-the-framing rule (handlers cannot get it wrong).

The wave-3 gate reads the landed machine against push-captures.md and the ADR-020 amendments line by line (the packless-report capture and the ~40 ms datum's shape are the capture-level acceptance).

Decomposition record (2026-10-22): wave 3 decomposed into ten task files under tasks/implementation/core/ (the wave-2 gate is green — core/review-wave-2's verdict; its findings were records, not absorptions: the alkcall early-arrival race is patched upstream, the include-tag rider is booked on OQ-19 for wave 5). Shape: the seam amendment first — core/w3-prepare-updates-seam (the ADR-018 §4 signature gains the command-derived updates; see the interpretation below — everything in the wave composes against it); then core/w3-pack-ingest-doubles (the doubles' real prepare: index with thin bases from the mem Find, unpack-to-loose into the mem store, the tips-based connectivity walk); three independent tasks in parallel — core/w3-push-grammar (the request parse: commands, caps selection, shallow rejection, the nothing-to-do shape), core/w3-v0-advertisement (the V0 ref advertisement + the stateless GET emission), and core/w3-pack-source (the raw-tail escape on both substrates + the async→blocking pack bridge + the wire-side size budget); then core/w3-push-report (the ADR-013 §8 framing — over the advertisement task, the shared-module ordering on src/v0.rs); then core/w3-push-machine (the orchestration: the bounded peek's four rules, prepare under the permit, name validation, the single CAS home, the report) over the grammar, the report, the pack source, and the doubles; then the two serving entries in parallel — core/ w3-duplex-serving (the V0 advertisement + one-request service + the ingest-handle threading through the native transport) and core/ w3-stateless-serving (the push POST path + the advertisement GET); and core/review-wave-3 (the capture-level gate) depending on all nine.

Interpretations recorded:

  • The prepare-seam amendment (the wave's one spec-level call, flagged for the wave-3 gate to confirm): ADR-018 §4's pinned prepare signature cannot express the pinned composition — the impl needs the command-derived updates (the connectivity walk runs "from the pushed tips", ADR-020 §2; the pin-custody correlation keys on "the prepared RefUpdate list it produced", substrate.md §object storage; ADR-013 §6's "prepares validated updates" must be executable) and the pinned signature hands it only the pack stream. The amendment adds updates: Vec<RefUpdate> after repo (the POC-O-validated parameter order — POC-O ran exactly this shape end-to-end against real git); the impl validates and returns the list verbatim as PreparedPush.updates (ADR-018 §5's "the transport consumes PreparedPush.updates" unchanged; the wire layer keeps command parsing — shallow rejection stays up-front, ADR-013 §4). Pre-publish draft shape (the OQ-03 freeze happens at the core's publish); the amendment note lands on ADR-018 with the task.
  • Unknown request capabilities fail closed (the fetch parse's posture); the accepted set is the advertised set plus quiet and agent (honored by omission — we never send progress; upstream clients select quiet habitually).
  • Duplicate ref commands in one push fail closed (upstream rejects multiple updates for one ref; not capture-pinned — flagged for the gate).
  • No-oid ref lines (the unborn-HEAD symref row) are filtered from the V0 advertisement — a V0 line has no symref shape; the OQ-19 rider (a) covers the ls-refs side, this covers the push side (flagged for the gate).
  • A client selecting no report capability gets no report (the captures always select report-status-v2; the honest grammar sends only what was negotiated — flagged for the gate).
  • The pack tail crosses the pkt-line→raw boundary through a substrate escape (the demand-driven pkt-line iterator is positioned exactly after the terminating flush; the no-byte-loss proof is a required test on both substrates).
  • The pack-size budget is enforced wire-side by the relay (fail-closed, the client-fault class — 413-mapped at the door on stateless), with Limits flowing into prepare as the impl-side defense (the POC's counting-reader posture).

General review (2026-10-22, post-wave-3 whole-surface pass — docs/reviews/004): the findings volume sat inside the inline bar — S-1/S-2/C-1/D-1 (the duplex request Readers' missing per-request budget, the O(n²) fetch dedup, the advertisement GET's unbounded listing, the unused tracing dep) fixed and stamped in the review session, tests added, all gates green. Recorded for later waves: R-1 (the wave-4 manifest read for the dead-until-then gix-protocol/gix-transport pins), R-2 (originally a wave-8 booking; re-booked pre-publish 2026-10-23 — the bound decision + the doubles' guard are wave-5 work, before the OQ-03 freeze; wave 8's impl inherits the decision — the size-bomb class), N-1 (the permit-through- stalled-relay observation — read at the wave-5 gate), N-2 (the substrate manifest's wave-6 read). No remediation wave.

Wave 4 — Consumer half + ops

  • GitSession (ADR-017, construction simplified per ADR-023 §2) — the wrapper over gix-transport's git-daemon Connection on the alkcall BiStream compat halves; connect_direct rides ADR-016's request-line preamble, open_via_channels rides the open-op params; ls_refs/fetch (full ack-loop negotiation client-side) and the hand-rolled push (mirror of ADR-013's server shapes); the client riders: packless push holds the write side open until the report (ADR-020 §3) and the RST read-your-request discipline (ADR-023 §5). Below-Negotiate/RefMap is the POC-X-validated structure (ADR-023 §6's recorded scope).
  • The op family (ADR-012 §3/§5, gate per ADR-015) — git/repo/{create,delete,update,get} as thin OperationSpec + handler pairs over Arc<dyn GitRegistryStore>; the PATCH semantics (review 002 R-3), the error-code table with the N-2 collapse rule (unauthorized = unknown ≡ unauthorized; already_exists create-side only — review 002 R-6's posture), create seeding the caller's {read, write, manage} grants.

Wave 4 may decompose as one wave or split (git-session ∥ op family are near-independent bodies of work sharing only the seam) — the decomposition decides with the task sizing in hand.

Decomposition record (2026-10-23): wave 4 decomposed into six task files under tasks/implementation/core/ (the wave-3 gate is green — core/review-wave-3's verdict; nothing needed absorbing). The near-independence read confirmed: the session branch and the op branch share only the seam, so the wave decomposes as two parallel branches under one wave (not a split — one gate). Shape: the construction first on the session branch — core/w4-session-construction (the ADR-017 §2-as-amended wrapper over the gix-transport git-daemon Connection on the BiStream compat halves; both constructors; the Limits wiring; the no-credentials posture) — then the two client machines in parallel over it, core/w4-session-fetch (ls_refs + the client-side ack loop, below-Negotiate/RefMap per ADR-023 §6) and core/w4-session-push (the hand-rolled send-pack mirror of ADR-013's shapes + the two client riders); the op branch runs core/w4-ops-gate (the ADR-015 §3 two-tier function, pure, no dependencies) with core/w4-repo-ops (the four spec+handler pairs + registration + the wire schemas/error table) over it; and core/review-wave-4 (the capture-level gate) depending on all five.

Interpretations recorded (flagged for the wave-4 gate):

  • The open_via_channels dialing responsibility (the session branch's one shape call): ADR-016 §4's "sends the same {repo, service} params schema" is ambiguous between the constructor dialing channels/git/sub itself and the constructor accepting the post-open BiStream (the POC-X shape — the transport writes nothing, the preamble rode the open-op). The task pins the decision against alkcall's client API and records it; the contract either way is that the params are sent by whoever dials.
  • The client-side response-pack budget maps to Limits::max_pack_size (the same budget kind, client-side direction) — no new Limits fields (ADR-009's no-new-kinds rule, the wave-3 gate's confirmed posture). Flagged for the gate to confirm the mapping reads honestly (a fetch response pack and a push report are both "response" directions of the same budget).
  • The ownership mint is required in v1's op family (the registration takes Arc<dyn OwnershipStore>): ADR-015 §Neutral records the mint could become optional for embedders that don't want spawn-tracking, but v1 keeps it ("kept in v1 because git/repo/* ops are family-shared and the record is cheap"); the optional-downgrade is a future additive change, not this wave's.
  • Review 004's dispositions are followed, not re-litigated: R-1 (the gix-protocol/gix-transport pin confirmation) folds into core/w4-session-construction as an acceptance criterion — the review's own recommendation ("a checklist item, not a task"), executed at that task's first commit where the deps become live code. R-2 is re-booked pre-publish (2026-10-23, operator review of the decomposition): the bound decision + the doubles' guard are wave-5 work — the original wave-8 booking would have placed the shape decision after the OQ-03 freeze of Limits (a post-freeze Limits field is semver surface on a published crate) and left the doubles — the reference ingest composition the wave-8 task is written against — unguarded through the core's publish; wave 8's impl inherits the decision. N-1 reads at the wave-5 gate, N-2 at the wave-6 manifest read; S-1/S-2/C-1/D-1 are landed — the wave-4 machines compose against the request-budget semantics as fixed.

General review (2026-10-23, post-wave-4 whole-surface pass — docs/reviews/005): the review-004 precedent's five code-quality dimensions (not a spec-compliance pass — the wave-4 gate's ADR verdict stands). The findings volume sat inside the inline bar — D-1/D-2 (the request-line wire reader's malformed-frame coverage gap, fixed with two byte-level tests — preamble 85→96%; the lib.rs crate-root changelog doc, rewritten module-oriented) fixed and stamped in the review session, full verification set green, coverage at 93.52% lines. Booked for wave 5: R-1 (the session.rs split — three clean-seamed workstreams, flat siblings per convention 14, zero public-API change, two in-file helper dedups folded in — the recommended first task of the wave, ahead of the fuzz harnesses: refactor → fuzz → docs → publish), and R-5 (the per-ingress coverage table — wave 5's fuzz-target inventory's input; the coverage disposition is review-time evidence, per-gate). R-2/R-3/R-4 are duplication keeps with rationale recorded (the client-mirror grammar shares nothing by design). No remediation wave.

Wave 5 — Core validation & release

The wave that lifts track B's gate. Three workstreams:

  • OQ-19's wire validations (tasks/architecture/ oq-19-wire-validations.md) — rider (a): the unborn-HEAD symref line against a real client on both substrates (the seeded-HEAD fixture from substrate.md §refs makes a dangling-HEAD repo trivial once the doubles provide one). Rider (b): the ack loop emitting positive ACKs — validated against the doubles' Find here; its substrate-odb facet re-runs in track B wave 7 against the real Find before the substrate crate's freeze (the OQ entry's wording covers both runs). Either ride's honest-retraction outcome stays inside this wave (ADR-003: serve or retract before publish).
  • Fuzzing — the family standing rule (anything worth fuzzing gets fuzzed), on the core's ingress surfaces: the pkt-line framing parsers, the git-daemon request-line grammar, the negotiation command/state ingress, the repo-id grammar, the preamble open-op params (the alkgit-owned post-schema ingress; the JSON schema gate itself is alkcall's surface, family-fuzzed there — the alkblobs finding). The fuzz/ sidecar convention (detached runner, corpus gate joining the verification list).
  • The size-bomb bound (review 004 R-2, re-booked here pre-publish 2026-10-23 — the original wave-8 booking would have decided the shape after the OQ-03 freeze of Limits and left the doubles unguarded through the publish): the decompressed-size/expansion cap for pack ingestion decided and landed before the freeze — the shape call (a named Limits field is the recommended posture, per the ADR-009 budget model where embedders tune budgets; a fixed fsck-style multiple is the alternative), the doubles' inflate_entry converted to incremental reading under the cap (no pre-allocation of the pack-declared size), and a hostile-declared-size regression test (the breach maps to the unpack-ng leg — the outcome class, never Err, matching the impl-side defense posture the wave-3 gate confirmed). Wave 8's real GitPackIngest inherits the decision and lands the incremental inflate in the substrate impl.
  • Release readiness for the core — crate docs (public-API rustdoc warning-free; docs honesty), the feature matrix (default, sha256 over it, --all-features), CHANGELOG/README, publish dry-run, then the publish of alkgit 0.1 — the gate review that unblocks track B's decomposition. OQ-03's core-side freeze inventory is settled here.

Decomposition record (2026-10-23): wave 5 decomposed into eight task files under tasks/implementation/core/ (the wave-4 gate is green — core/review-wave-4's verdict — and review 005's dispositions are absorbed; no remediation wave). Shape: the review-005 refactor first — core/w5-session-split (R-1: the flat-sibling split per convention 14 + the two in-file helper dedups + R-3's two cheap constants; zero public-API change) — with three independent tasks parallel to it: core/w5-size-bomb-bound (review-004 R-2: the decompressed-size cap decided and landed before the OQ-03 freeze — a named Limits field is the recommended posture; the doubles' incremental inflate; the hostile-declared-size regression), and the OQ-19 riders — core/w5-oq19-positive-acks (rider 2: positive ACKs over the doubles' Find; the substrate facet re-runs in wave 7) and core/w5-oq19-real-client-tokens (rider 3: the wave-2-gate-booked include-tag/session-id round — the survivable-decline validation + the per-token honor-by-omission/retraction decision); rider 1 (the unborn-HEAD symref) executes in-wave as the existing architecture/n5-unborn-head-rider task (the OQ-19 tracker's depends_on now spans all three riders). Then core/w5-fuzz-harness (the fuzz/ sidecar + targets over the plan's ingress inventory with R-5's table as the starting data + the corpus gate joining the verification list) over the split (targets pin the final module paths); core/w5-release-readiness (crate docs, the feature matrix, CHANGELOG/README creation, the publish dry-run) over all five; core/review-wave-5 (the release gate: the OQ-19 dispositions, the fuzz corpus gate, the size-bomb disposition, the counting-reader decision, N-1's read, the freeze inventory; flips the docs to stable) over everything; and core/w5-publish (the publish checklist: manifest sanity + the 0.1.0 bump, tests, CHANGELOG check, dry-run, the alkgit-v0.1.0 tag, cargo publish) over the gate — the gate stays a pure review; the publish is its own task.

Interpretations recorded (flagged for the wave-5 gate):

  • The size-bomb cap shape (core/w5-size-bomb-bound's one spec-level call): a named Limits field is the recommended posture (ADR-009's embedder-tuned budget model); the fixed fsck-style multiple is the alternative. The field lands pre-publish (additive — the freeze happens at the publish); wave 8's impl inherits the decision.
  • The rider-3 token dispositions (core/w5-oq19-real-client-tokens): per token (include-tag, session-id=, the rest of the modern-client inventory), honor-by-omission vs honest retraction — decided in the task, confirmed or corrected at the gate.
  • The counting-reader decision (the wave-4 gate's recorded residual — gix's V2 handshake buffers the peer's capability advertisement internally): read at the gate — accept the wall-clock-only bound (peer servers are authenticated alk nodes) or land the read-half wrapper; recorded for the OQ-03 inventory either way.
  • Review 004 N-1 (the permit-through-stalled-relay observation) reads at the gate, per its booking.
  • The tag convention (core/w5-publish): crate-qualified tags (alkgit-v0.1.0) — two crates publish from this repo over the plan's life; a bare v0.1.0 would be ambiguous at the substrate's publish. Pinned at the publish task; flagged for the gate to confirm.

Wave 6 — Substrate foundation

Track B's seam-side foundation (against the published alkgit):

  • Substrate scaffold + alkgit version pin — the crate manifest (features sqlite on / postgres off / lfs on per ADR-025; alkgit pinned by version, never a path).
  • References + the git-object tables: refs row tables + row-CAS apply_updates (ADR-019 §2 — one transaction per call; the atomic realization POC-O validated); the put_repo-seeds-HEAD rule (substrate.md §refs — HEAD → refs/heads/main in the record transaction, this wave's implementation home).
  • Migrations (ADR-024) — construction-run, version-stamped, additive-first; the LFS refcount column ships day-one in the initial schema; the in-memory adapter stamp-free.
  • The registry adapter family + reactive halves (ADR-019 §3–5) — the family adapter pattern (read-side cached provider, write-side seq-bumping transactions + post-commit announce), publish/notify/ enqueue, the reconcile sweep, the per-push mutation marker; the in-memory default adapter; the sqlite twin (default) and postgres twin (feature).

The wave-6 gate reads the landed tables/adapters against ADR-019/024 and the POC-R evidence (the coherence contract's staleness bounds, the reconcile-closure recipe).

Wave 7 — Objects & generation

  • alkblobs git-object-family wiring — loose objects under the derivation family (alkblobs ADR-018), the odb layer owning the header it writes, stat = preimage length.
  • The substrate Find/odb front (SubstrateOdb-class) — gix_object::Find/Exists + gix_pack::Find shapes, location legs empty, constructed once with per-session views.
  • GitPackGen + common_haves real impls — ADR-004's pipeline over the real Find; both serving shapes (direct + through-store); OQ-19 rider (b)'s substrate facet re-validation runs here (positive acks over the real substrate Find) and closes the rider alongside wave 5's machinery-side run.

Wave 8 — receive-pack ingestion

The ADR-020 composition, for real:

  • GitPackIngest: budgeted index into the host working area (thin bases via the substrate Find), unpack-to-loose under verification-on-put, the pin bridge + pending-pin registry (substrate.md §object storage's pinned custody rules: content- addressed correlation, post-CAS guard commits, stale-entry pruning, GC-decreed recovery), the full-graph connectivity walk (parents included), working-area discard. The inflate path reads incrementally under the wave-5-pinned decompressed-size bound (review 004 R-2's implementation half — the decision and the doubles' guard landed pre-publish in wave 5; this wave's decomposition record names the inherited cap).
  • End-to-end against real git — git push (ordinary, thin, packless-delete, atomic) and git clone/fetch against the substrate-backed server, plus the replication loop through GitSession (ADR-023's POC-X gates re-landed on the real code).

Wave 9 — LFS

ADR-022's surface on the landed stack:

  • Reference rows + day-one refcount (the ADR-024 column) — repo-lifetime scoping v1 (OQ-20 stands deferred), the media-re-protected-while-any-row-remains sweep contract.
  • Liveness wiring — the LFS media rows as the second alkgit-owned liveness source; register/remove with push and ops.
  • The batch adapter + ops composition — lfs batch/checking over the ops control plane, the pre-keyed put / ranged fetch byte paths, wired ownership providers (the POC-L door's seam evidence as the regression posture).
  • Real git-lfs client round trips against the composition (the POC-L gates re-landed).

Wave 10 — Substrate validation & release

  • OQ-21's composition-validation POC (tasks/architecture/ oq-21-lfs-lock-validation.md) — the real lfs.locks API flows over the substrate stack (alkstore's named-TTL locks, lock rows co-tenant on the shared engine, sqlite contention measured); both outcomes are wins (posture evidenced / gaps surfaced upstream-cheap). The build stays deferred on the first client need.
  • Release readiness for the substrate — crate docs, the feature matrix (sqlite on, postgres, lfs, combos: pg-pure, lfs without postgres, --all-features), the three assembly shapes (substrate.md §assembly shapes) exercised in tests, CHANGELOG/ README/docs honesty, publish dry-run, then the publish of alkgit-substrate 0.1 — the release gate that closes the plan.

Decided points

  • Wave-based decomposition (ADR-025 consequence; operator-endorsed 2026-10-20): the SDD decompose-everything-upfront step is replaced by the alkstore/alkblobs rhythm — decompose the next wave after the previous wave's review gate; later decompositions absorb earlier outcomes. Renumbering is a guide, not a contract: remediation waves discovered by whole-surface reviews get inserted and the later waves renumbered (the alkblobs 8–10 precedent).
  • Two-track stagger on the publish gate (ADR-025's anchored-basis posture): the core publishes before the substrate track decomposes or implements. The substrate builds against a published anchor; the inter-crate version pin is enforced from wave 6's first commit.
  • Fuzzing embedded in wave 5 (operator, 2026-10-20): "5 is great for now and we'll move it later if needed" — the core's ingress fuzzing runs inside the core-validation wave rather than as its own wave; if the ingress inventory found at decomposition says it wants more room, it splits and the numbering follows.
  • Scope authority (operator, 2026-10-20): v0.1 publishes both crates with LFS in scope (ADR-022); doors stay out of this plan — the alkhttp git/lfs features land in that repo after the core's publish (doors.md sequencing), alkssh stays future.
  • OQ-19 reclassified deferred(scope) (review-003, 2026-10-20): blocked on the booked wave item; rider (a) runs in wave 5, rider (b)'s machinery-side run in wave 5 and its substrate facet in wave 7 — both must pass before the respective crate's freeze (OQ-03).
  • W-2/W-3 decisions recorded in-line (review-003, 2026-10-20): the pending-pin registry (substrate.md §object storage) and the put_repo-seeds-HEAD rule (substrate.md §refs; ADR-023 §4's stated limitation) are composition-internal pins, not ADRs.
  • Taskgraph DAG discipline: task files live under tasks/implementation/ with the frontmatter enums (AGENTS.md task rules), decomposed one wave at a time; taskgraph validate after every write; parallel branches are ordered by the graph so independent workstreams don't step on each other.

Review gates

Each wave ends in a review task (review-wave-N) before the next wave decomposes. The wave's gate reads the landed surface against the pinned ADR text line by line (the alkblobs gate discipline):

  • Wave 1 review — the one-way-door gate: seam types, trait signatures, and Limits read against ADR-018 (as amended) and the N-3 schemas; a shape error here is a task revision.
  • Waves 2–4 reviews — capture-level: the fetch machine against negotiation-captures.md/ADR-014, the push machine against push-captures.md/ADR-020, GitSession/ops against the ADR-016/ 017/023 shapes and the POC-X evidence.
  • Wave 5 review — the core release gate: OQ-19's disposition (all three riders served or retracted), the fuzz corpus gate, the size-bomb bound's disposition (the cap landed, the doubles guarded — R-2 closed pre-publish), the counting-reader decision (the wave-4 gate's residual) and review 004 N-1's read, the feature matrix, the publish dry-run, the OQ-03 core-side freeze inventory settled; flips transport/overview/backend to stable on green. The publish itself is core/w5-publish over the gate (the gate stays a pure review).
  • Waves 6–9 reviews — evidence-level: landed composition vs ADR-019/020/022/024 and the four POC findings' measured datums.
  • Wave 10 review — the substrate release gate: OQ-21's POC outcome recorded, the substrate-side freeze inventory settled; flips substrate/lfs/substrate-era backend sections to stable.

Remediation waves, when a review's findings exceed the inline bar, insert before fuzzing/release of the affected track and renumber (the alkblobs rule: the original numbering was a guide, not a contract; older pre-renumbering records read as their current numbers).