Files
glm-5.3-flash 6f6ab19b8e docs(architecture): fix YAML-invalid frontmatter; changelog text moves to notes
- last_updated annotations containing ': ' ('family composition: ...',
  'feature graph re-pinned: ...', 'REQ-5 added: ...') broke Gitea's
  frontmatter parser in docs/architecture/README.md,
  backends-and-dispatch.md, and requirements.md
- last_updated is now a bare date; the annotation lives in a quoted
  notes field across all annotated architecture docs
- convention documented in .opencode/agents/architect.md: bare
  last_updated date, quoted notes, quotes required for ': ' scalars

Verification: all docs/**/*.md frontmatter parsed with yaml.safe_load
(16/16 OK); cargo test, clippy --all-targets -D warnings, fmt --check pass
2026-10-11 17:20:50 +00:00

8.4 KiB

status, last_updated, notes
status last_updated notes
draft 2026-10-11 ADR-013 composition round — REQ-5 added: the family's first consumers deploy this crate with the sibling reactive substrate; co-tenancy + liveness-form facts pinned

Requirements

The deployment-relevant facts each use case forces on this crate. Specs and ADRs reference these by ID (REQ-n); a future session must be able to read this document and never re-derive a requirement from conversation or vision prose. This document is the anchor: when a consumer fact changes, it changes here first, with a dated note, and the ADRs re-derive from it.

Vocabulary (defined once; used everywhere — ADR-011 is the decision behind this section)

  • Tier — one contract (a Backend trait implementation boundary) serving one routing class of the pool. Exactly two tiers: kv (small content) and large (over the size-threshold content; the tier formerly named fs — renamed in ADR-011 because the tier's routing criterion is length, and half its engines are not filesystems). "Backend" names the same thing at a different altitude (the tier as shipped including its engine set); "two backends" (ADR-004) reads forever as "two tiers".
  • Engine — a concrete storage implementation of one tier's contract. Five ship: the kv tier's sqlite (default), postgres (feature postgres), and mem (ephemeral; feature mem; the contract-reference engine; ADR-003); the large tier's local (default; ADR-003) and pg-lo (ADR-009).
  • Pool — the flat content-addressed namespace of one deployment (hash → bytes everywhere; ADR-005).
  • Store instance — one running instantiation of the store facade (one engine set — one or two engines per the constructor mode, ADR-011 §4 — plus dispatch and GC state). The GC arbitration domain for single-instance topologies (ADR-005's pins, liveness locks, and delete windows live inside one); the pin-row owner id under fleet engines (ADR-008/010 — ADR-008 wrote "node id" before this split existed; a store instance is what owns pins).
  • Node — one running process (an embedder's binary). A node hosts one or more store instances — this fixes the prior definition's collapse (which REQ-2's phrasing contradicted). Node is the process/ops unit: alkcall connections, ACL grants, sweep cadence, and deployment identity attach to nodes.
  • Fleet — two or more store instances over one shared pool (via a shared SQL engine). Defined by pool-sharing, not node count: two instances in one process over one postgres pool are a fleet; two processes on two sqlite files are two pools, never a fleet. A fleet is the only topology in which cross-instance GC and large-tier locality questions exist (ADR-008).
  • Deployment — one operator's whole system: typically one fleet or one node; consumer tables, op registrations, and sweep cadence are deployment choices.

The use cases and their requirements

UC-1: Self-hosted git serving (gitea-replacement scale)

Git repos for a user/team on one machine. Small-object regime dominated by loose objects in the kv tier; packs and large artifacts (git-lfs shape) on the large tier.

  • REQ-1 (traces: ADR-007, POC #3 A4) — single-machine economics: sqlite is the default kv engine; single-writer WAL ceiling (~1.2k objects/s under contention) is comfortably above single-node push rates. Zero-ops is a requirement: no daemon beyond the node process itself.

UC-2: Distributed git with replicator nodes hosting repos for many users

The p2p/OSS sketch (phase-0 §p2p shape): replicators are push/clone endpoints serving many concurrent users; gossip-style sync between replicators; ACL/ownership above (alkcall layer).

  • REQ-2 (traces: ADR-007, ADR-008) — a replicator node may run multiple store instances (a fleet) sharing one pool via a shared postgres kv engine, including serving large (>threshold) pool content from that fleet. This is stated as a requirement, dated 2026-10-03 (recording a planning fact from the use case's origin discussion; it predates all POCs and architecture docs, recorded here on the operator's authority as the use case's stated shape). Consequences are derived in ADR-008: fleet GC (DB-backed pins, advisory-locked sweeper, SQL delete arbitration), the large-tier engine concept with its locality contract, and the pg-lo engine (POC #7 — passed 2026-10-03, admitted as a shipped engine by ADR-009: a fleet node can consolidate on postgres-only).
  • REQ-3 (traces: ADR-007, POC #5 B3/B4) — concurrency scale-out: the fleet's hub-class nodes need put throughput that increases under concurrency (pg: near-linear to ~37k puts/s at 24 conns); sqlite's WAL ceiling (~1.2k/s, latency-climbs-unbounded) bounds single-node sustained write pressure regardless of client count. This is the measured basis for the per-node engine split.

UC-3: Agent workspaces (many agents, near-identical content)

Agents working simultaneously in mostly-identical workspaces, each making small edits; a shared pool dedups by construction; per-workspace path→hash manifests above.

  • REQ-4 (traces: ADR-005, ADR-002) — pooling and one address space across workspaces; dedup must be structural (not configured). Small edits mean the small-blob regime dominates: sqlite-class kv performance is what matters; the large tier is hit rarely (large workspace artifacts).

UC-4: The family's first consumers run composition (alkgit + alkfs + the reactive substrate)

The first consumers of this crate — alkgit (its object backend, OQ-07) and alkfs (path trees/workspaces, OQ-08) — deploy alkstore, the sibling reactive coordination substrate (v0.1.0: notify/locks/queues/streams/outbox/scheduler behind its own engine crates) together with this crate, per an operator-authority planning fact (2026-10-11, recorded in alkstore's docs/research/consumer-inventory.md — which grades this crate's fleet needs against alkstore's surface). This forces composition facts on this crate regardless of whether its core takes a dependency:

  • REQ-5 (traces: ADR-013) — composition facts (decided, not deferred; ADR-013 is the ruling):
    • The first consumers' nodes co-deploy both crates; this crate's core keeps no alkstore dependency (ADR-013 §1) — the reactive substrate is deployment-layer composition above the facade, the named substrate for the cadence ADR-005 assigns to embedders.
    • No cross-store atomicity: each crate owns its transaction scope; the compensation properties downstreams rely on are this crate's idempotent re-puts (CAS; delete-then-recover backstop), windowed deletes, and reports-as-data sweep outcomes (ADR-013 §3).
    • Consumer liveness tables co-tenant the fleet host (the named-table-reference form, ADR-012 §6.6) or ride the in-process callback form single-node — the two-form liveness seam every downstream plans against (ADR-013 §3).
    • The fleet sweeper's single-flighter is a named exclusive sweep lease whose shipped provider is the pg advisory lock; other providers (the substrate's lock) are composition above the facade or a new-ADR substitution door (ADR-013 §2).

What this document is not

  • Not consumer architecture: how alkgit maps refs/manifests onto the store remains OQ-07 (externally-owned); how alkfs shapes workspaces remains OQ-08. This document records what each use case forces on this crate, not what each consumer is — UC-4's rows are the one composition shape the use cases themselves force (REQ-5/ADR-013), stated as constraints, not as a consumer design.
  • Not a requirements-change process substitute: a new or changed REQ arrives here with a dated note and owner; ADRs that depended on the old fact state their reopen conditions and reopen when this file changes.

Decision trace

REQ Introduced Source ADRs
REQ-1 2026-10-03 (fact: phase-0 §deployment posture) POC #3 A4 ADR-007
REQ-2 2026-10-03 (fact: use case origin discussion, pre-POC) user/planning record ADR-007, ADR-008
REQ-3 2026-10-03 (fact: phase-0 §deployment posture) POC #5 B3/B4 ADR-007
REQ-4 2026-10-03 (fact: phase-0 §vision) POC #1, ADR-002 ADR-002, ADR-005
REQ-5 2026-10-11 (fact: composition planning; corroborated by alkstore's consumer-inventory) operator record ADR-013