docs(architecture): Phase 1 spec tree — 7 specs, ADRs 001-006, OQ tracker

- overview/hashing/store-api/backends/gc/ops specs + README index,
  all draft status with YAML frontmatter and cross-referenced ADR/OQ
- ADR-001: substrate posture (store layer alkcall-free = family
  conformance, not deviation) + ops placement decided (feature-gated
  module, no consumer-waited deferral)
- ADR-002: canonical git-blob-sha-256 + key encoding — the declared
  wire-format ADR (one-way door)
- ADR-003: backend contract, sqlite/fs/mem tiers, pure-function
  dispatch, no migration, unknown-length buffering semantics pinned
- ADR-004: exactly two production backends; manifest/path-tree layers
  are consumer-side (the corrected two-vs-three-backends framing)
- ADR-005: pooled CAS, three liveness sources, delete windows with
  pin-before-publish + delete-time arbitration (race closed), no
  ambient scheduling
- ADR-006: whole-blob verification; chunk-tree encodings excluded by
  scoping, not deferred on a paused consumer
- open-questions.md: OQ-BL-01..06 resolved with ADR cross-refs; OQ-07
  /08 externally-owned, OQ-09 deferred(scope) with SDD tracker task
- AGENTS.md: status updated to Phase 1, gix-odb path corrected

Verification: cargo test / clippy -D warnings / fmt --check clean
This commit is contained in:
glm-5.3-flash committed 2026-10-01 14:45:32 +00:00
1 parent 7d9ac8a6ec
commit 4b5009d85a
16 files changed
+1953 -8

No files matched your search

@@ -0,0 +1,101 @@
# ADR-004: Exactly two backends; manifest layers stay above the crate
## Status
Accepted
## Context
An early Phase 0 framing — "using two backends is ugly" — shaped several
rounds before being edited away, but the correction it was reaching for
arrived only in the Phase 1 review, and needs recording so no future
round reabsorbs the concern incorrectly:
- There are **always** two physical backends in this problem: storage
for small content (fast, kv/sqlite) and the filesystem for large
content. This is not ugly, it is the measured shape of the problem
(POC #3 A4: crossover ~128-256 KiB; SQLite ~9-10× faster below it).
- The "two backends is ugly" sentiment was actually about a **third
layer** in the old alknet-filesystem research — the *virtual
filesystem / appfile* stack: path trees, branches, tombstones,
filename↔hash maps living *alongside* the blob store (in that research,
literally a co-equal SQLite store beside iroh-blobs).
If left implicit, this misframing has two failure modes: (a) this crate
absorbs a path-tree/manifest layer ("three backends"), growing tables,
schemas, and branching semantics that belong to consumers; (b) this
crate shapes itself such that a future vfs builder must hack around it.
alkfs is a pending consumer precisely of that manifest layer — this ADR
is the boundary it will build against.
## Decision
**This crate ships exactly two production backends and nothing more;
every manifest/namespace/path-tree layer is a consumer, by construction
and by contract.** (A non-production ephemeral `mem` tier exists for
tests — ADR-003; it is not a storage story and does not extend this
boundary.)
1. **Two production backends is the complete set.** kv/sqlite (small)
+ fs (large) cover the scale economics with measured evidence
(ADR-003); no other physical tier is needed by any current or
planned consumer's *blob* needs.
2. **The "third backend" is not this crate's layer.** The vfs/appfile
shape — path→hash mapping, branches/tombstones/deltas per workspace,
the alknet-filesystem lineage — is storage *above* the pool:
reference sets over hashes (ADR-005's namespace machinery), not a
Backend implementation. Consumers build it on their own storage
(alkfs's path tree, alkgit's refs).
3. **The store provides the seams the manifest layer needs, and nothing
that forces it to hack:**
- hash-addressed put/get with a verified, git-interop-compatible
addressing scheme (ADR-002) — manifests reference entries by the
same canonical addresses git uses;
- liveness registration so manifest-holds maps to GC roots without
the store learning manifest formats (ADR-005);
- `stat`/`read_range` — enough for serving path-resolved content and
large-file ranges;
- pinned batch puts — manifest-atomic multi-file writes are
protected in flight (ADR-005).
4. **Namespacing at the store level is logical only** (ADR-005):
reference tables above, flat bytes below. If a consumer wants
physical isolation per tenant/repo, that is two pool deployments (a
topology choice), never a namespace flag inside one.
## Consequences
**Positive**
- The crate's scope stays testable and small: two backends, one pool,
one hash domain. No schema/versioning surface for manifest formats —
consumers iterate on theirs freely.
- alkgit and alkfs build their (very different) mapping layers on one
shared pool without either bending the store; their manifests interop
at the address level by construction.
- The alknet-filesystem lineage is honored as *shape guidance* for
consumers (branches, write sessions, chain walks), with zero of its
storage-layer mechanics (SQLite path tables, honker wiring, CRDT
sync) leaking in here.
**Negative**
- Consumers each re-build the small amount of manifest plumbing they
need (a reference table and a root registration). This is deliberate:
the manifest needs of alkgit (git refs) and alkfs (path trees) are
different enough that a shared one would fit neither.
**Neutral**
- If a future consumer *did* want a first-party manifest layer, it
would be a sibling crate over this one (e.g. `alkfs` itself), not an
extension of this crate.
## References
- `docs/research/phase-0.md` (settled approach: pooled CAS; OQ-BL-05)
- `/workspace/@alkdev/alknet/docs/research/alknet-filesystem/poc-summary.md`
— the historical lineage this ADR explicitly bounds (not a design
input)
- ADR-003 (the two backends), ADR-005 (namespaces as reference sets)
- [overview.md](../overview.md) (consumer map: alkgit, alkfs);
[backends-and-dispatch.md](../backends-and-dispatch.md)