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.
39 KiB
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:
alkgitat the root'ssrc/,alkgit-substrateundersubstrate/), the crate manifests reworked per the placement table (backend.md §crate and feature model:alkgit'ssha256off-by- default;alkgit-substrate'ssqliteon,postgres/lfsper 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) andStorageError(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/PushOptionsand theRepoRecordserde 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: falseon 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; therepo: &RepoRecordparameter 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-
donerounds (acknowledgmentssection, NAK + flush, neverready), the statelessdoneround with done-closure generation,common_havesdriving the ack check behind the seam. GitPackGenover 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.updatespoint); the function is crate-public so the interim is not dead code — additive pre-publish, the gate confirms the placement. - The
common_havescontract 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
generatecall site); the shared semaphore is constructed by the serving entries fromLimits::max_concurrent_blocking_tasks. - The stateless serving entry includes the advertisement emission for
the door's future
info/refsroute (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_failedreason 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
Limitsfield, the partial-header-is-malformed rule. GitPackIngestover doubles — prepare/apply split (the single CAS home inGitRefs::apply_updates), CAS timing per ADR-013 §7's observed order,atomicsemantics, 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
preparesignature 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 preparedRefUpdatelist 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 addsupdates: Vec<RefUpdate>afterrepo(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 asPreparedPush.updates(ADR-018 §5's "the transport consumesPreparedPush.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
quietandagent(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
Limitsflowing intoprepareas 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-daemonConnectionon the alkcallBiStreamcompat halves;connect_directrides ADR-016's request-line preamble,open_via_channelsrides the open-op params;ls_refs/fetch(full ack-loop negotiation client-side) and the hand-rolledpush(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 thinOperationSpec+ handler pairs overArc<dyn GitRegistryStore>; the PATCH semantics (review 002 R-3), the error-code table with the N-2 collapse rule (unauthorized= unknown ≡ unauthorized;already_existscreate-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_channelsdialing responsibility (the session branch's one shape call): ADR-016 §4's "sends the same{repo, service}params schema" is ambiguous between the constructor dialingchannels/git/subitself and the constructor accepting the post-openBiStream(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 newLimitsfields (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 becausegit/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-transportpin confirmation) folds intocore/w4-session-constructionas 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 ofLimits(a post-freezeLimitsfield 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 positiveACKs — 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
Limitsand 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 namedLimitsfield 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_entryconverted 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, neverErr, matching the impl-side defense posture the wave-3 gate confirmed). Wave 8's realGitPackIngestinherits 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,sha256over it,--all-features), CHANGELOG/README, publish dry-run, then the publish ofalkgit 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 namedLimitsfield 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 barev0.1.0would 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
sqliteon /postgresoff /lfson per ADR-025;alkgitpinned by version, never a path). - References + the git-object tables: refs row tables + row-CAS
apply_updates(ADR-019 §2 — one transaction per call; theatomicrealization POC-O validated); theput_repo-seeds-HEAD rule (substrate.md §refs —HEAD → refs/heads/mainin 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::Findshapes, location legs empty, constructed once with per-session views. GitPackGen+common_havesreal 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) andgit clone/fetchagainst the substrate-backed server, plus the replication loop throughGitSession(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 —
lfsbatch/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-lfsclient 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 reallfs.locksAPI 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 (
sqliteon,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 ofalkgit-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/lfsfeatures 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 validateafter 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
Limitsread 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 againstpush-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
stableon green. The publish itself iscore/w5-publishover 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).