docs(architecture): resolve OQ-04 — receive-pack state machine (ADR-013)

- ADR-013: V0-framed push machine grounded in real git 2.43.0 captures
  (file://, git://, smart-http mock, raw stdio into real receive-pack):
  V0-shaped ref advertisement (caps on first ref line, capabilities^{}
  sentinel only for empty repos), served capability set, shallow requests
  rejected for v1, thin packs accepted with server-odb bases (no
  capability involved; push.thin default), ingestion bound to
  Bundle::write_to_directory_eagerly + gix-fsck + one gix-ref transaction
  per push (.keep-guarded), unpack-first CAS timing with observed
  upstream order, band-1 pkt-line-framed status report, http framing
  (probe/Content-Length/chunked), v1 update policy (CAS only; deletes
  and force-push allowed)
- docs/research/push-captures.md: the normative push wire record
- transport.md/backend.md/doors.md: receive-pack sections rewritten to
  the decided shapes; backend.md ingestion composition bound; stale
  OQ-04 references resolved
- ADR-003 amended: V2-only governs fetch; push is V0-framed by upstream
  design (fixes the V2-only contradiction found in review)
- ADR-009 amended: haves default reconciled with the client's stateless
  ceiling (16384); blocking-pipeline budget covers generation+ingestion
- OQ-04 resolved; tracker task closed; CAS-fail-fast optimization
  tracked (tasks/architecture/oq-13-cas-failfast.md)
- research index: poc findings + capture docs listed

Verification: cargo test / clippy -D warnings / fmt --check / doc pass
This commit is contained in:
glm-5.3-flash committed 2026-09-25 04:05:07 +00:00
1 parent dbb056f451
commit e76f91f6d7
14 files changed
+621 -138

No files matched your search

+30 -16
View File
@@ -1,6 +1,6 @@
---
status: draft
last_updated: 2026-09-21
last_updated: 2026-09-25
---
# Backend traits: the storage seam
@@ -36,20 +36,28 @@ gix types:
for receive-pack, name validation per git ref rules + reserved-
namespace deny-list).
4. **`GitPackGen`** — (repo, wants, haves, limits) → streaming pack
(`io::Write` consumer). Negotiation-agnostic. Missing objects abort
with an error, never a broken pack (ADR-004).
(`io::Write` consumer), plus `common_haves(repo, haves) -> recognized
subset` for the negotiation ack loop (a have is recognized iff it
exists in the object store and is a commit — the honest boundary:
never ack what we cannot subtract; ADR-014). Negotiation-agnostic
generation. Missing objects abort with an error, never a broken pack
(ADR-004).
5. **`GitPackIngest`** — client pack stream → indexed pack + fsck/
connectivity report. It *prepares* the validated ref updates; the
transaction itself is applied by `GitRefs` (single CAS home — ingest
validates, refs commits). Budgeted (ADR-009 max pack size);
blocking-thread friendly.
connectivity report. Thin-pack bases resolve from the server's own odb
(push sends thin packs by default — ADR-013 §5); the pack lands with a
`.keep` guard; missing objects → `unpack ng`. It *prepares* the
validated ref updates; the transaction itself is applied by `GitRefs`
(single CAS home — ingest validates, refs commits; one transaction per
push is what makes `atomic` correct — ADR-013 §7). Budgeted (ADR-009
max pack size); blocking-thread friendly.
Minimal-vs-full was the open sub-question; resolved as **full family** —
the four traits are each one or two methods plus types, and collapsing
them (e.g. refs into the registry) would force one impl block per
downstream where independent seams are cheaper to satisfy. The `gix`
feature implements all four; a downstream with its own object store
implements 3–4 and reuses 1–2, or none of it.
each trait is one or two methods plus types, and collapsing them (e.g.
refs into the registry) would force one impl block per downstream where
independent seams are cheaper to satisfy. The `gix` feature implements
the three object-storage traits (3–5: `GitRefs`, `GitPackGen`,
`GitPackIngest`); a downstream with its own object store implements 3–5
and reuses 1–2 (`GitRegistry`/`GitRegistryStore`), or none of it.
## Feature model (ADR-012 §4, amends ADR-010's single-`gix` story)
@@ -77,9 +85,10 @@ Two independent seams, two default-on features:
(`Arc<Store>` shared, per-session handles, `prevent_pack_unload()` +
`ignore_replacements = true`), generation on blocking threads,
O(counts) memory, missing-objects abort.
- Received-pack ingestion via `gix-pack::data::input` (`streaming-input`)
+ `gix-fsck` + `gix-ref` transactions (ADR-004). Validation against
real `git push` is OQ-04.
- Received-pack ingestion via `gix-pack::Bundle::write_to_directory_eagerly`
(`streaming-input`, thin-base lookup) + `gix-fsck` + `gix-ref`
transactions with `PreviousValue::MustExistAndMatch` CAS (ADR-004, ADR-013
— the full push state machine is decided there).
## Concurrency model
@@ -132,12 +141,17 @@ planned: call ops only):
| [010](decisions/010-pure-protocol-crate.md) | Pure protocol crate | traits in-crate, impls behind features |
| [011](decisions/011-per-repo-authorization.md) | Per-repo authorization | grants in records, policy in core |
| [012](decisions/012-registry-backing-and-ops.md) | Registry + ops | write trait, file default, CRUD ops, feature split |
| [013](decisions/013-receive-pack-state-machine.md) | receive-pack | thin-pack ingestion, transaction CAS, report framing |
| [014](decisions/014-v2-negotiation-ack-loop.md) | Negotiation | ack loop, `common_haves` seam, no `ready` |
## Open Questions
- **OQ-04**: pack ingestion validation (deferred(unclear)).
- **OQ-05**: sha256 policy (deferred(scope)).
- **OQ-03**: publish-time API freeze inventory (the op set enters it).
- OQ-04 resolved (ADR-013 — ingestion composition bound: thin-base
lookup, `.keep` landing, transaction CAS).
- OQ-02 resolved (ADR-014 — `common_haves` ack seam added to
`GitPackGen`).
## References