docs(architecture): ADR-013 — family composition, reactive boundary, sweep lease; OQ-11 resolved
- No sibling (alkstore) dependency in-core; reactivity is deployment-layer composition above the facade — cadence stays embedder-owned (ADR-005), with alkstore the named substrate for it; in-crate notify/scheduler/queues/invalidation hooks are non-goals. - Fleet sweeper lock pinned as 'named exclusive sweep lease' semantics; shipped provider unchanged (pg advisory clause); provider swaps are ADR-007-shaped substitution doors. - Composition contract for downstream ADRs: co-tenancy of CAS/GC state + reactive machinery + consumer/liveness tables on one fleet host; no-cross-store-atomicity ruled; compensation rests on idempotent re-puts, windowed deletes, sweeps-as-report-data. - OQ-11 resolved (the register's one live circular hedge): engine features pull their own driver clusters — sqlite becomes feature 'sqlite' (default-on, carries rusqlite); 'kv'/'large' tier features retired; 'local' ungated; pg-pure builds are supported, non-default. Deciding fact already in-tree (REQ-2/ADR-009). - requirements.md: UC-4/REQ-5 records the composition facts (operator record 2026-10-11, corroborated by alkstore's consumer-inventory). - Publish guard stated pre-code: facade ships before publish; semver-is-contract from first publish (alkstore ADR-017 discipline). - Amended accepted ADRs' feature wording (ADR-010 §1, ADR-011 §3/§4, ADR-003) with supersession notes; README/overview/open-questions/ backends/store-api/gc aligned; parked index now empty. Verification: docs-only round (no Rust surface touched); full-tree single-value check on the feature graph and sweep-lease wording.
This commit is contained in:
1 parent
7b9d904b8a
commit
bee86b0dbd
11 files changed
+433
-64
No files matched your search
@@ -1,6 +1,6 @@
|
|||||||
---
|
---
|
||||||
status: draft
|
status: draft
|
||||||
last_updated: 2026-10-03 (ADR-012 — pre-decomposition consistency rulings: sweep-abort shape, token wire shape, fleet activation, GC-state host)
|
last_updated: 2026-10-11 (ADR-013 — family composition: no sibling dependency in-core, sweep-lease semantics, co-tenancy/no-cross-store-atomicity rulings, OQ-11 resolved, feature graph re-pinned to engine features)
|
||||||
---
|
---
|
||||||
|
|
||||||
# alkblobs — Architecture
|
# alkblobs — Architecture
|
||||||
@@ -106,6 +106,25 @@ there — not inherited as hedges:
|
|||||||
repaired (pin/sweep-lock bodies dropped; window staging for
|
repaired (pin/sweep-lock bodies dropped; window staging for
|
||||||
non-SQL engines is in-process). The tree is now single-valued on
|
non-SQL engines is in-process). The tree is now single-valued on
|
||||||
every implementation-facing signature.
|
every implementation-facing signature.
|
||||||
|
- **ADR-013** (2026-10-11, the composition round): with the sibling
|
||||||
|
substrate **alkstore** shipped (v0.1.0 — the reactive coordination
|
||||||
|
crate the first consumers co-deploy), this ADR rules the
|
||||||
|
composition so nothing waits on anything downstream or upstream:
|
||||||
|
the core keeps **no alkstore dependency and no reactive machinery**
|
||||||
|
(notify/scheduler/queues/invalidation hooks are non-goals — cadence
|
||||||
|
stays embedder-owned per ADR-005, with alkstore the named
|
||||||
|
deployment-layer substrate for it); the fleet sweeper's
|
||||||
|
single-flighter is pinned as **named exclusive sweep lease**
|
||||||
|
semantics (shipped provider: the pg advisory-lock clause — provider
|
||||||
|
swaps are new-ADR substitution doors); **co-tenancy and
|
||||||
|
no-cross-store-atomicity** are ruled as the downstream-facing
|
||||||
|
composition contract (consumer liveness tables co-tenant the fleet
|
||||||
|
host; compensation rests on idempotent re-puts, windowed deletes,
|
||||||
|
sweeps-as-report-data); and **OQ-11 is resolved** — engine features
|
||||||
|
pull their own driver clusters (`sqlite` default-on carrying
|
||||||
|
rusqlite; `kv`/`large` tier features retired; `local` ungated), so
|
||||||
|
pg-pure builds are supported non-default configurations. REQ-5
|
||||||
|
records the composition facts; the parked index is now empty.
|
||||||
|
|
||||||
## Architecture Documents
|
## Architecture Documents
|
||||||
|
|
||||||
@@ -136,17 +155,21 @@ there — not inherited as hedges:
|
|||||||
| [010](decisions/010-backend-io-seams-and-fleet-state-home.md) | Backend I/O seams (read cursor, staged put); GC state store-core-owned via the engine-state seam; kv fleet-validity; pin-token renewal | Accepted |
|
| [010](decisions/010-backend-io-seams-and-fleet-state-home.md) | Backend I/O seams (read cursor, staged put); GC state store-core-owned via the engine-state seam; kv fleet-validity; pin-token renewal | Accepted |
|
||||||
| [012](decisions/012-pre-decomposition-consistency-rulings.md) | Pre-decomposition rulings: sweep aborts are report data (`GcAborted` retired); pin-token wire shape; fleet-mode constructor declaration; GC state hosts on the kv engine; sqlite seam reduction | Accepted |
|
| [012](decisions/012-pre-decomposition-consistency-rulings.md) | Pre-decomposition rulings: sweep aborts are report data (`GcAborted` retired); pin-token wire shape; fleet-mode constructor declaration; GC state hosts on the kv engine; sqlite seam reduction | Accepted |
|
||||||
| [011](decisions/011-vocabulary-tiers-engines-and-fleet-state.md) | Vocabulary: tier/engine/instance/node/fleet; mem joins kv; large tier renamed `large`; constructor modes | Accepted |
|
| [011](decisions/011-vocabulary-tiers-engines-and-fleet-state.md) | Vocabulary: tier/engine/instance/node/fleet; mem joins kv; large tier renamed `large`; constructor modes | Accepted |
|
||||||
|
| [013](decisions/013-family-composition-reactive-boundary-and-feature-graph.md) | Family composition: no sibling dependency in-core; sweep lease; co-tenancy & no-cross-store-atomicity; OQ-11 resolved (engine features pull own drivers) | Accepted |
|
||||||
|
|
||||||
## Open Questions
|
## Open Questions
|
||||||
|
|
||||||
Tracked in [open-questions.md](open-questions.md). The Phase 0 register
|
Tracked in [open-questions.md](open-questions.md). The Phase 0 register
|
||||||
(OQ-BL-01..06) is promoted there with its resolutions; three questions
|
(OQ-BL-01..06) is promoted there with its resolutions; two questions
|
||||||
are parked — OQ-07/OQ-08 externally-owned (alkgit seam mapping,
|
remain externally-owned — OQ-07/OQ-08 (alkgit seam mapping,
|
||||||
alkfs intake; carried for visibility, gating nothing here) and
|
alkfs intake; carried for visibility, gating nothing here) — and the
|
||||||
OQ-11 `deferred(scope)` (the pg-only-kv feature graph; a named
|
parked index is **empty**: OQ-09
|
||||||
requirement from outside this crate decides it). OQ-09
|
|
||||||
(namespace-visibility default) resolved closed-by-default; OQ-10
|
(namespace-visibility default) resolved closed-by-default; OQ-10
|
||||||
(second kv engine) resolved by ADR-007. The pg-lo large-engine question
|
(second kv engine) resolved by ADR-007; OQ-11 (the pg-only-kv feature
|
||||||
|
graph, once `deferred(scope)` on a "named deployment requirement from
|
||||||
|
outside") resolved by ADR-013 §4 — its deciding fact already existed
|
||||||
|
in-tree (REQ-2's consolidation posture; ADR-009), the caught example
|
||||||
|
of the circular form. The pg-lo large-engine question
|
||||||
resolved to *sequenced work*, not a parked question: REQ-2/ADR-008
|
resolved to *sequenced work*, not a parked question: REQ-2/ADR-008
|
||||||
named it the candidate large-tier engine and POC #7
|
named it the candidate large-tier engine and POC #7
|
||||||
(`docs/research/poc-pglo-spec.md`) was its admission evidence — a
|
(`docs/research/poc-pglo-spec.md`) was its admission evidence — a
|
||||||
@@ -167,7 +190,8 @@ corollary worth restating, because it recurs: a fact this crate must
|
|||||||
create (its own first deployment, its own first consumer) is never a
|
create (its own first deployment, its own first consumer) is never a
|
||||||
deciding input — decisions stand on evidence in hand, and future needs
|
deciding input — decisions stand on evidence in hand, and future needs
|
||||||
reopen via named requirements, not via waiting. (Corollary
|
reopen via named requirements, not via waiting. (Corollary
|
||||||
applications so far: OQ-09 and OQ-10 both resolved on it.)
|
applications so far: OQ-09, OQ-10, and OQ-11 — ADR-013's audit round —
|
||||||
|
all resolved on it.)
|
||||||
|
|
||||||
## Lifecycle
|
## Lifecycle
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
---
|
---
|
||||||
status: draft
|
status: draft
|
||||||
last_updated: 2026-10-03 (ADR-012 — composite predicate gains the pg-lo same-instance clause; kv-only rejection is put-time; GC state hosts on the kv engine; window wording)
|
last_updated: 2026-10-11 (ADR-013 — feature graph re-pinned: engine features pull their own drivers; `kv`/`large` tier features retired, `sqlite` default-on, `local` ungated)
|
||||||
---
|
---
|
||||||
|
|
||||||
# Backends, tiers, and dispatch
|
# Backends, tiers, and dispatch
|
||||||
@@ -111,13 +111,20 @@ the store's seams (see "What the Backend trait is").
|
|||||||
|
|
||||||
| Tier | Engine | Feature | Default | Fleet-valid |
|
| Tier | Engine | Feature | Default | Fleet-valid |
|
||||||
|---|---|---|---|---|
|
|---|---|---|---|---|
|
||||||
| kv | sqlite | — (tier default) | yes (tier default-on) | no |
|
| kv | sqlite | `sqlite` | **yes (default-on)** | no |
|
||||||
| kv | postgres | `postgres` | no | **yes** |
|
| kv | postgres | `postgres` | no | **yes** |
|
||||||
| kv | mem | `mem` | no | no (test/reference engine) |
|
| kv | mem | `mem` | no | no (test/reference engine) |
|
||||||
| large | local | — (tier default) | yes (tier default-on) | shared media only, explicit assertion |
|
| large | local | *(ungated — dependency-free)* | always present | shared media only, explicit assertion |
|
||||||
| large | pg-lo | `pg-lo` | no | **yes** |
|
| large | pg-lo | `pg-lo` | no | **yes** |
|
||||||
|
|
||||||
### kv tier (tier default-on, feature `kv`); engines sqlite (default) + postgres (feature `postgres`, default-off) + mem (feature `mem`, default-off)
|
Features gate engines, each pulling only its own driver cluster
|
||||||
|
(ADR-010 §1 extended to every engine — ADR-013 §4); tiers are
|
||||||
|
constructor composition, not features. The tier table's "Feature"
|
||||||
|
column is therefore the engine features: `sqlite` default-on carries
|
||||||
|
rusqlite (the batteries-included default); a pg-pure build (`sqlite`
|
||||||
|
off) is supported, non-default — REQ-2's consolidation posture.
|
||||||
|
|
||||||
|
### kv tier (ADR-003/007/011); engines sqlite (feature `sqlite`, default-on) + postgres (feature `postgres`, default-off) + mem (feature `mem`, default-off)
|
||||||
|
|
||||||
Small blobs — **`kv` is the tier name; the engine is a per-node
|
Small blobs — **`kv` is the tier name; the engine is a per-node
|
||||||
constructor choice** between the shipped engines (ADR-007; ADR-003
|
constructor choice** between the shipped engines (ADR-007; ADR-003
|
||||||
@@ -177,7 +184,7 @@ fleet-valid, and the **contract-reference engine**: every other
|
|||||||
engine's conformance tests mirror its suite (the role it already
|
engine's conformance tests mirror its suite (the role it already
|
||||||
played in POC #1's miniature).
|
played in POC #1's miniature).
|
||||||
|
|
||||||
### large tier (tier default-on, feature `large`; the tier formerly named `fs` — renamed in ADR-011); engines `local` (default) + `pg-lo` (feature `pg-lo`, default-off)
|
### large tier (ADR-003/008/011; the tier formerly named `fs` — renamed in ADR-011); engines `local` (ungated, always present) + `pg-lo` (feature `pg-lo`, default-off)
|
||||||
|
|
||||||
Large blobs. Like the kv tier (ADR-007), the large tier is **one
|
Large blobs. Like the kv tier (ADR-007), the large tier is **one
|
||||||
contract, multiple engines** — the medium is a per-node constructor
|
contract, multiple engines** — the medium is a per-node constructor
|
||||||
@@ -301,6 +308,7 @@ alkfs intake may name needs externally; OQ-08).
|
|||||||
| [010](decisions/010-backend-io-seams-and-fleet-state-home.md) | I/O seams + GC-state home | read cursor / staged put; store-core-owned GC state via the engine-state seam; the kv fleet-validity rule |
|
| [010](decisions/010-backend-io-seams-and-fleet-state-home.md) | I/O seams + GC-state home | read cursor / staged put; store-core-owned GC state via the engine-state seam; the kv fleet-validity rule |
|
||||||
| [011](decisions/011-vocabulary-tiers-engines-and-fleet-state.md) | Vocabulary | tier/engine/instance/node/fleet; mem is a kv engine; fs → large; constructor modes |
|
| [011](decisions/011-vocabulary-tiers-engines-and-fleet-state.md) | Vocabulary | tier/engine/instance/node/fleet; mem is a kv engine; fs → large; constructor modes |
|
||||||
| [012](decisions/012-pre-decomposition-consistency-rulings.md) | Fleet activation & GC-state host | `fleet:` constructor declaration; all fleet GC state on the kv engine; pg-lo same-instance clause; kv-only put-time rejection |
|
| [012](decisions/012-pre-decomposition-consistency-rulings.md) | Fleet activation & GC-state host | `fleet:` constructor declaration; all fleet GC state on the kv engine; pg-lo same-instance clause; kv-only put-time rejection |
|
||||||
|
| [013](decisions/013-family-composition-reactive-boundary-and-feature-graph.md) | Feature graph + fleet lease | engine features pull their own drivers (`kv`/`large` tier features retired); sweep lease semantics; no sibling dependency in-core |
|
||||||
|
|
||||||
## Open Questions
|
## Open Questions
|
||||||
|
|
||||||
@@ -325,6 +333,8 @@ alkfs intake may name needs externally; OQ-08).
|
|||||||
not adopted (threshold dispatch chose the simpler policy;
|
not adopted (threshold dispatch chose the simpler policy;
|
||||||
ADR-003 §Context)
|
ADR-003 §Context)
|
||||||
- ADR-003, ADR-004, ADR-005; ADR-012 (fleet activation, GC-state
|
- ADR-003, ADR-004, ADR-005; ADR-012 (fleet activation, GC-state
|
||||||
host, the composite predicate's same-instance clause); [store-api.md](store-api.md) (the invariants
|
host, the composite predicate's same-instance clause); ADR-013 (the
|
||||||
|
feature graph's engine-feature ruling, the sweep lease, the family
|
||||||
|
composition contract); [store-api.md](store-api.md) (the invariants
|
||||||
engines must satisfy); [requirements.md](requirements.md) (the pinned
|
engines must satisfy); [requirements.md](requirements.md) (the pinned
|
||||||
vocabulary)
|
vocabulary)
|
||||||
@@ -80,7 +80,10 @@ Evidence in hand (no further POC or consumer-waiting needed):
|
|||||||
engine.
|
engine.
|
||||||
|
|
||||||
Both production tiers are default-on (batteries-included, the
|
Both production tiers are default-on (batteries-included, the
|
||||||
alkgit feature-model precedent); `mem` is not.
|
alkgit feature-model precedent); `mem` is not. *(Feature shape
|
||||||
|
superseded by ADR-013 §4: "default-on" now reads as constructor-presence
|
||||||
|
default — the sqlite and `local` engines — not tier features; engine
|
||||||
|
features pull their own drivers.)*
|
||||||
|
|
||||||
### Dispatch policy
|
### Dispatch policy
|
||||||
|
|
||||||
|
|||||||
@@ -128,7 +128,10 @@ store core programs the abstraction, never the engine shape.
|
|||||||
feature pulling the shared driver cluster (`tokio-postgres` +
|
feature pulling the shared driver cluster (`tokio-postgres` +
|
||||||
`deadpool-postgres`) independently — a `large`=pg-lo + kv=sqlite
|
`deadpool-postgres`) independently — a `large`=pg-lo + kv=sqlite
|
||||||
node is coherent and supported. overview.md's dependency table pins
|
node is coherent and supported. overview.md's dependency table pins
|
||||||
the edge.
|
the edge. *(The same independent-driver discipline is extended to
|
||||||
|
every engine by ADR-013 §4 — `sqlite` becomes its own default-on
|
||||||
|
feature carrying rusqlite, the `kv`/`large` tier features retire, and
|
||||||
|
pg-pure builds become supported, non-default.)*
|
||||||
|
|
||||||
### 2. GC state is store-core-owned; per-engine state seams are crate-internal and contract-tested
|
### 2. GC state is store-core-owned; per-engine state seams are crate-internal and contract-tested
|
||||||
|
|
||||||
@@ -369,4 +372,8 @@ token-mapping note; nothing else changes.
|
|||||||
- [ops-surface.md](../ops-surface.md) (the pin-token contract §4
|
- [ops-surface.md](../ops-surface.md) (the pin-token contract §4
|
||||||
extends); [store-api.md](../store-api.md) (stage hygiene, sweep
|
extends); [store-api.md](../store-api.md) (stage hygiene, sweep
|
||||||
semantics, cursor-adjacent facade shapes); [requirements.md](../requirements.md)
|
semantics, cursor-adjacent facade shapes); [requirements.md](../requirements.md)
|
||||||
(REQ-2, the fleet requirement §2/§3 serve)
|
(REQ-2, the fleet requirement §2/§3 serve)
|
||||||
|
- [ADR-013](013-family-composition-reactive-boundary-and-feature-graph.md)
|
||||||
|
(family composition — §4's feature-graph extension of this ADR's §1
|
||||||
|
consequence; §2's sweep-lease semantics for this ADR's sweep-lock
|
||||||
|
clause)
|
||||||
@@ -138,6 +138,9 @@ kept (its meaning — this node's own filesystem — stays the
|
|||||||
constructor's meaning even under the shared-media fleet posture,
|
constructor's meaning even under the shared-media fleet posture,
|
||||||
where it is explicitly *not* only-node-local; the constructor's
|
where it is explicitly *not* only-node-local; the constructor's
|
||||||
explicit shared-media assertion, ADR-008 §3, is what disambiguates).
|
explicit shared-media assertion, ADR-008 §3, is what disambiguates).
|
||||||
|
*(The rename's feature clause is superseded by ADR-013 §4 — engine
|
||||||
|
features pull their own driver clusters, tier features retire — the
|
||||||
|
tier `large` and constructor wording stands.)*
|
||||||
|
|
||||||
### 4. The constructor table (the load-bearing copy of the fleet-validity summary; ADR-010 §3 points here)
|
### 4. The constructor table (the load-bearing copy of the fleet-validity summary; ADR-010 §3 points here)
|
||||||
|
|
||||||
@@ -149,6 +152,12 @@ explicit shared-media assertion, ADR-008 §3, is what disambiguates).
|
|||||||
| large | local | — (tier default) | yes (tier default-on) | shared media only, explicit assertion |
|
| large | local | — (tier default) | yes (tier default-on) | shared media only, explicit assertion |
|
||||||
| large | pg-lo | `pg-lo` | no | **yes** |
|
| large | pg-lo | `pg-lo` | no | **yes** |
|
||||||
|
|
||||||
|
*(The table's engine/constructor columns stand; the Feature column's
|
||||||
|
tier-default shape is superseded by ADR-013 §4 — engine features:
|
||||||
|
`sqlite` default-on, `postgres`, `mem`, `pg-lo` default-off, `local`
|
||||||
|
ungated. Fleet-validity, defaults as constructor presence, and modes
|
||||||
|
are unchanged.)*
|
||||||
|
|
||||||
Constructor modes (dispatch shapes) — pinned here (implied by
|
Constructor modes (dispatch shapes) — pinned here (implied by
|
||||||
ADR-008 §3's re-routing bullet and the mem tests, but never named):
|
ADR-008 §3's re-routing bullet and the mem tests, but never named):
|
||||||
- **dual-tier** (the default): kv + large, size-threshold dispatch
|
- **dual-tier** (the default): kv + large, size-threshold dispatch
|
||||||
|
|||||||
+242
@@ -0,0 +1,242 @@
|
|||||||
|
# ADR-013: Family composition — the reactive boundary, the sweep lease, and the feature-graph resolution
|
||||||
|
|
||||||
|
## Status
|
||||||
|
|
||||||
|
Accepted (2026-10-11)
|
||||||
|
|
||||||
|
## Context
|
||||||
|
|
||||||
|
While this crate paused mid-pre-decomposition (ADR-012 was its last
|
||||||
|
round), the sibling **alkstore** shipped v0.1.0: one reactive
|
||||||
|
coordination contract (`Store` — notify/listen, named TTL locks,
|
||||||
|
durable queues, streams with per-consumer offsets, outbox,
|
||||||
|
leader-elected scheduler) behind engine crates (`-mem`, `-sqlite`,
|
||||||
|
`-postgres`), contract-pinned by a factory-driven suite. Its
|
||||||
|
consumer-inventory — built from the paused consumers' written
|
||||||
|
documents under this crate's own deferral discipline — grades three
|
||||||
|
needs of *this crate's specced machinery*:
|
||||||
|
|
||||||
|
- the fleet sweeper lock → named locks, **pinned**;
|
||||||
|
- maintenance cadence (sweep scheduling, pin-renewal workers) →
|
||||||
|
scheduler/queues, **documented** — against ADR-005's "embedder owns
|
||||||
|
cadence";
|
||||||
|
- fleet invalidation ("sweep/manifest changed" among fleet instances)
|
||||||
|
→ notify, **documented**.
|
||||||
|
|
||||||
|
The first consumers (alkgit, alkfs) will deploy both crates together;
|
||||||
|
composition facts are therefore inter-crate decisions that must be
|
||||||
|
decided on evidence in hand, not parked on deployments that cannot
|
||||||
|
pre-exist this crate (the Schrödinger's-code rule; the OQ register's
|
||||||
|
OQ-09/OQ-10 precedents). The audit round that produced this ADR also
|
||||||
|
found one live circular hedge in the register: OQ-11 parked the
|
||||||
|
pg-only kv feature graph on "a named deployment requirement arriving
|
||||||
|
from outside this crate" — a deciding fact *this tree already
|
||||||
|
contains* (REQ-2's consolidation posture, which ADR-009 shipped as
|
||||||
|
"a fleet node can consolidate on postgres-only"), the same
|
||||||
|
shape-shifter OQ-09/OQ-10 were rejected for, and the last one in the
|
||||||
|
register.
|
||||||
|
|
||||||
|
Additionally, the alkcall failure mode ("published doc surface
|
||||||
|
promising unimplemented behavior, surviving versions") motivates
|
||||||
|
stating this crate's publish discipline before anything ships.
|
||||||
|
|
||||||
|
## Decision
|
||||||
|
|
||||||
|
### 1. The reactive boundary: no sibling dependency in-core; reactivity is deployment-layer composition
|
||||||
|
|
||||||
|
**The core crate gains no alkstore dependency and no reactive
|
||||||
|
machinery.** No in-crate notify, no in-crate scheduler, no queues,
|
||||||
|
no invalidation hooks on any facade method. ADR-001's substrate
|
||||||
|
posture extends by the same reasoning to the reactive substrate:
|
||||||
|
the store layer ships bytes-and-addresses, and everything that makes
|
||||||
|
bytes *reactive* is composition above the facade.
|
||||||
|
|
||||||
|
- **ADR-005's "embedder owns cadence" stands, with the substrate
|
||||||
|
named.** An embedder wanting interval sweeps, pin-renewal workers
|
||||||
|
(the TTL/3 cadence, ADR-008), or maintenance dispatch composes them
|
||||||
|
above the facade — typically with the sibling substrate's
|
||||||
|
scheduler/queues. The punt was always a placement decision, not a
|
||||||
|
missing mechanism; the sibling's existence changes nothing here.
|
||||||
|
- **Sweep outcomes are data (ADR-012 §1) — with a pinned consumer.**
|
||||||
|
The report is what the embedder's reactive wiring consumes:
|
||||||
|
`SweepReport` → sibling-notify/stream events (downstream cache
|
||||||
|
invalidation), report cadence → the embedder's scheduler. No
|
||||||
|
in-crate hook exists for any of it; this crate never references
|
||||||
|
the sibling's types, channels, or names.
|
||||||
|
- The engine-driver dependency posture is likewise untouched: no
|
||||||
|
alkstore engine crate enters this crate's feature graph (see §4's
|
||||||
|
graph, which names only storage engines).
|
||||||
|
|
||||||
|
### 2. The sweep lease: semantics pinned, provider shipped
|
||||||
|
|
||||||
|
The fleet sweep single-flighter (ADR-008 §2's advisory lock, whose
|
||||||
|
state home is the engine-state seam's sweep-lock clause — ADR-010 §2,
|
||||||
|
ADR-012 §5) is hereby pinned as a **named exclusive sweep lease**:
|
||||||
|
|
||||||
|
- at most one sweep in flight per pool;
|
||||||
|
- acquisition is an atomic test-and-set at the fleet host;
|
||||||
|
- holdership ends at explicit release, owner-process death, or the
|
||||||
|
underlying engine session's loss;
|
||||||
|
- contention is the `GcAbortCause::SweeperLock` report abort with
|
||||||
|
nothing deleted (ADR-012 §1 — report data, never an error).
|
||||||
|
|
||||||
|
These are exactly pg advisory-lock semantics — the shipped provider
|
||||||
|
(ADR-008's mechanism, unchanged; a lease whose life is the sweep's
|
||||||
|
own) — and exactly what a named TTL lock provides when its lease
|
||||||
|
spans the sweep run. Pinning the *semantics* is what makes the
|
||||||
|
provider substitutable without a contract change: if the family ever
|
||||||
|
wants the in-crate provider swapped (e.g. onto the sibling's lock),
|
||||||
|
that is the ADR-007-shaped substitution door — a new ADR with its own
|
||||||
|
evidence, not an open question.
|
||||||
|
|
||||||
|
A deployment may *additionally* serialize its maintenance above this
|
||||||
|
crate: an embedder holding a named lease on the sibling substrate
|
||||||
|
before invoking `sweep()` composes one discipline over its own
|
||||||
|
scheduled tasks (sweeps, expiry reaping, renewal workers). This is
|
||||||
|
composition above the facade — it changes no contract and is not
|
||||||
|
required for fleet correctness, because the in-crate advisory clause
|
||||||
|
is this crate's own guarantee regardless.
|
||||||
|
|
||||||
|
### 3. Co-tenancy and the no-cross-store-atomicity ruling (the downstream-facing composition contract)
|
||||||
|
|
||||||
|
A fleet pg instance hosts, as co-tenants: this crate's CAS rows and
|
||||||
|
GC state (its pool; `gc_pins` / `gc_window` — ADR-012 §4); the
|
||||||
|
sibling substrate's reactive machinery; consumer business tables
|
||||||
|
(refs, manifests, path trees — outside this crate per ADR-004); and
|
||||||
|
the consumer liveness tables this crate's fleet arbitration
|
||||||
|
consults as named table references (ADR-012 §6.6). **No crate owns
|
||||||
|
another's tables; co-tenancy is a named deployment statement** — the
|
||||||
|
posture decisions/007 and ADR-008 §2 already assumed, now citable so
|
||||||
|
downstream ADRs (alkgit's refs, alkfs's tree) reference it rather
|
||||||
|
than re-derive it.
|
||||||
|
|
||||||
|
- **Cross-store atomicity is unachievable by construction** — each
|
||||||
|
crate owns its own transaction scope, and no seam bridges them.
|
||||||
|
The family posture downstream is the outbox/compensation pattern
|
||||||
|
(alkstore's `outbox`/`notify_tx` are its substrate), and this
|
||||||
|
crate's load-bearing properties for that pattern are already
|
||||||
|
guaranteed and stay so: **puts are idempotent under CAS**
|
||||||
|
(delete-then-recover is a byte-identical re-put, ADR-005),
|
||||||
|
**deletes are windowed and re-arbitrated before execution**
|
||||||
|
(ADR-005; ADR-012 §6.7), and **sweep outcomes are data** (ADR-012
|
||||||
|
§1). Nothing downstream may rely on cross-store-atomic anything;
|
||||||
|
nothing here relies on it either.
|
||||||
|
- **Fleet liveness registration's two forms are the
|
||||||
|
downstream-facing seam:** the fleet form — a named liveness table
|
||||||
|
co-tenant on the fleet host (ADR-012 §6.6) — and the single-node
|
||||||
|
form — the in-process callback (ADR-005). Every downstream crate
|
||||||
|
plans against both. The requirement to keep a consumer's liveness
|
||||||
|
protection expressible on the fleet host is a *composition
|
||||||
|
constraint* on downstream schema placement: a consumer whose
|
||||||
|
liveness lives on a non-co-tenant engine participates in fleet GC
|
||||||
|
via pins (put-path) only, and its sweep-time liveness is expressed
|
||||||
|
by materializing rows per ADR-008 §2's materialization rule.
|
||||||
|
|
||||||
|
### 4. OQ-11 resolved: engine features pull driver clusters independently — including sqlite
|
||||||
|
|
||||||
|
**Features gate engines; tiers are constructor composition, not
|
||||||
|
features.** The ADR-010 §1 discipline ("engine features pull the
|
||||||
|
driver cluster independently" — pg-lo does not imply postgres)
|
||||||
|
extends to every engine:
|
||||||
|
|
||||||
|
| Engine | Feature | Default | Pulls |
|
||||||
|
|---|---|---|---|
|
||||||
|
| kv `sqlite` | `sqlite` | **on** | `rusqlite` |
|
||||||
|
| kv `postgres` | `postgres` | off | `tokio-postgres` + `deadpool-postgres` |
|
||||||
|
| kv `mem` | `mem` | off | nothing |
|
||||||
|
| large `local` | *(ungated)* | always present | nothing |
|
||||||
|
| large `pg-lo` | `pg-lo` | off | the shared pg driver cluster |
|
||||||
|
|
||||||
|
- The base `kv` feature — which existed because sqlite was the kv
|
||||||
|
tier's only shippable driver carrier and so pulled `rusqlite` under
|
||||||
|
the tier's name — is retired, as is the `large` tier feature
|
||||||
|
(nothing was ever behind it; `local` is ungated because it pulls
|
||||||
|
nothing). ADR-011 §3's `feature fs` → `feature large` rename line
|
||||||
|
is superseded by this table; ADR-003's "tiers default-on" wording
|
||||||
|
now reads as constructor-presence, not feature claims.
|
||||||
|
- **A pg-pure build (`sqlite` off — kv=postgres, and `large`=pg-lo if
|
||||||
|
wanted) is a supported configuration this crate does not default
|
||||||
|
to.** This resolves OQ-11 on evidence in hand: REQ-2 (operator
|
||||||
|
authority, 2026-10-03) is the deployment requirement the parked row
|
||||||
|
awaited — its consolidation posture (ADR-009's postgres-only fleet
|
||||||
|
node) is the pg-pure node, and parking the feature graph on it was
|
||||||
|
circular (a deployment requirement whose name the tree already
|
||||||
|
carries is not an absent fact). Mechanism unchanged from the
|
||||||
|
precedent: engine features pull their own drivers, so sqlite's
|
||||||
|
absence is one feature toggle, never a graph surgery.
|
||||||
|
- **Default builds are unchanged**: `sqlite` default-on keeps the
|
||||||
|
batteries-included default (the lean-base principle constrains
|
||||||
|
default builds, which still pull only tokio/thiserror/parking_lot
|
||||||
|
plus the default engine's driver).
|
||||||
|
|
||||||
|
### 5. Publish guard (stated pre-code, on the alkcall lesson)
|
||||||
|
|
||||||
|
The crate publishes only after its facade exists and passes the
|
||||||
|
invariants' test gate. From first publish, its semver is the contract
|
||||||
|
version — every published surface is implemented, every contract
|
||||||
|
change cites its ADR — the discipline the sibling substrate pinned in
|
||||||
|
its contract-versioning ADR. No published doc surface ever carries a
|
||||||
|
"planned/not-implemented" promise.
|
||||||
|
|
||||||
|
## Consequences
|
||||||
|
|
||||||
|
**Positive**
|
||||||
|
|
||||||
|
- The composition contract exists as a named ADR: alkgit/alkfs cite
|
||||||
|
§3 for schema co-tenancy, the two-form liveness seam, and the
|
||||||
|
no-cross-store-atomicity posture, instead of re-deriving or
|
||||||
|
guessing.
|
||||||
|
- The register's one live circular hedge (OQ-11) is resolved on
|
||||||
|
in-tree evidence, pre-consumer, where the fix is cheapest — the
|
||||||
|
alkcall-shaped outcome this round exists to avoid.
|
||||||
|
- A new fleet node's dependency tree is honest: pg-pure nodes do not
|
||||||
|
compile rusqlite; nothing reactive is dead code in the core.
|
||||||
|
- The sweep lease's semantics and provider are single-valued; a
|
||||||
|
provider swap later is a mechanism ADR, not a contract break.
|
||||||
|
|
||||||
|
**Negative**
|
||||||
|
|
||||||
|
- One more ADR's worth of surface for every reader of the fleet GC
|
||||||
|
story (§2 changes no signatures; it pins names for semantics the
|
||||||
|
mechanisms already had).
|
||||||
|
- The feature graph must be implemented as specced in §4's table — an
|
||||||
|
implementation-facing edit arriving with the engine tasks rather
|
||||||
|
than inside one of them.
|
||||||
|
|
||||||
|
**Neutral**
|
||||||
|
|
||||||
|
- No tier, engine, constructor mode, facade signature, error variant,
|
||||||
|
or ops payload changes. `fleet: true` validation (ADR-012 §3) and
|
||||||
|
the composite fleet-validity predicate are untouched.
|
||||||
|
- The sibling substrate relationship is documentary here —
|
||||||
|
requirements-level facts and boundary rules — with the eventual
|
||||||
|
dependency edge (if any, e.g. an ops-layer convenience) requiring
|
||||||
|
its own ADR through the substitution door.
|
||||||
|
|
||||||
|
## References
|
||||||
|
|
||||||
|
- ADR-001 (the substrate posture §1 extends to the reactive
|
||||||
|
substrate), ADR-003/007 (the substitution door §2 applies), ADR-005
|
||||||
|
(cadence ownership; delete-then-recover as the idempotency
|
||||||
|
backstop), ADR-006 (the deferred-cost pattern this ADR's OQ-11
|
||||||
|
resolution follows), ADR-008 (the fleet sweep lock §2 pins and the
|
||||||
|
liveness materialization rule §3 reuses), ADR-010/012 (the
|
||||||
|
engine-state seam, GC-state host, outcomes-as-data, the composite
|
||||||
|
predicate), ADR-011 (vocabulary; the constructor tables §4
|
||||||
|
supersedes in feature shape)
|
||||||
|
- [requirements.md](../requirements.md) — REQ-2 (the fact OQ-11 parked
|
||||||
|
on), REQ-5 (the composition requirement §3 serves)
|
||||||
|
- [overview.md](../overview.md) (dependency posture, amended §4;
|
||||||
|
non-goals, extended §1); [backends-and-dispatch.md](../backends-and-dispatch.md)
|
||||||
|
(engine/features table, amended §4); [store-api.md](../store-api.md)
|
||||||
|
(sweep semantics, §2's lease wording); [gc-and-namespaces.md](../gc-and-namespaces.md)
|
||||||
|
(sweeper locking); [open-questions.md](../open-questions.md) (OQ-11
|
||||||
|
resolved)
|
||||||
|
- alkstore — `/workspace/@alkdev/alkstore`: its consumer-inventory
|
||||||
|
(`docs/research/consumer-inventory.md`, the graded fleet rows),
|
||||||
|
ADR-016 (deployment honesty), ADR-017 (contract versioning — §5's
|
||||||
|
cited discipline); the sibling's payload model (JSON-only) is its
|
||||||
|
own non-goal boundary, matching this crate's object-storage
|
||||||
|
boundary here
|
||||||
|
- The alkcall hedge precedent (published-doc-surface drift) — the
|
||||||
|
failure mode §5 guards, on the operator's record
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
---
|
---
|
||||||
status: draft
|
status: draft
|
||||||
last_updated: 2026-10-03 (ADR-012 — aborts are report data (GcAbortCause), never sweep errors; window executor = the next sweep; non-SQL engines stage in-process)
|
last_updated: 2026-10-11 (ADR-013 — sweeper-lock semantics pinned as the named exclusive sweep lease; provider unchanged; composition boundary referenced)
|
||||||
---
|
---
|
||||||
|
|
||||||
# Pooling, namespaces, and GC
|
# Pooling, namespaces, and GC
|
||||||
@@ -25,11 +25,15 @@ price, and this document specifies the mechanism the POCs validated
|
|||||||
per-repo stores. Git's `alternates`/object-pool mechanism (GitLab
|
per-repo stores. Git's `alternates`/object-pool mechanism (GitLab
|
||||||
object pools) is the same idea done awkwardly; the pool generalizes it
|
object pools) is the same idea done awkwardly; the pool generalizes it
|
||||||
to unrelated repos and to p2p replication. **A fleet (REQ-2) — two
|
to unrelated repos and to p2p replication. **A fleet (REQ-2) — two
|
||||||
or more store instances sharing one pool — is the one topology where
|
or more store instances sharing one pool — is the one topology where
|
||||||
the GC mechanism below extends beyond one instance: ADR-008 pins,
|
the GC mechanism below extends beyond one instance: ADR-008 pins,
|
||||||
sweeper locking, and delete arbitration move into the shared engine;
|
sweeper locking, and delete arbitration move into the shared engine;
|
||||||
the invariants are unchanged. Where this crate's GC state (fleet
|
the invariants are unchanged — and the sweeper lock's semantics are
|
||||||
rows and locks) physically lives, and who holds the joint
|
the **named exclusive sweep lease** (one sweeper per pool; atomic
|
||||||
|
test-and-set; holdership ends at release/owner death/engine-session
|
||||||
|
loss; contention is the `SweeperLock` report abort — ADR-013 §2,
|
||||||
|
shipped provider: the pg advisory lock). Where this crate's GC state
|
||||||
|
(fleet rows and locks) physically lives, and who holds the joint
|
||||||
entry+pin transaction, is ruled by ADR-010 §2: the store core owns
|
entry+pin transaction, is ruled by ADR-010 §2: the store core owns
|
||||||
all GC state; SQL-backed engines host it through a crate-internal,
|
all GC state; SQL-backed engines host it through a crate-internal,
|
||||||
contract-tested seam; backends never learn liveness.**
|
contract-tested seam; backends never learn liveness.**
|
||||||
@@ -140,6 +144,7 @@ existence flips atomically per key).
|
|||||||
| [008](decisions/008-trait-size-probe-fleet-gc-and-large-engines.md) | Fleet GC extension | DB-backed pins, advisory-locked single sweeper, SQL delete arbitration — for shared-pool (fleet) topologies only; the in-process protocol is unchanged elsewhere |
|
| [008](decisions/008-trait-size-probe-fleet-gc-and-large-engines.md) | Fleet GC extension | DB-backed pins, advisory-locked single sweeper, SQL delete arbitration — for shared-pool (fleet) topologies only; the in-process protocol is unchanged elsewhere |
|
||||||
| [010](decisions/010-backend-io-seams-and-fleet-state-home.md) | GC-state home | the store core owns all GC state; SQL engines host it via the contract-tested engine-state seam; pin-tx ownership ruled |
|
| [010](decisions/010-backend-io-seams-and-fleet-state-home.md) | GC-state home | the store core owns all GC state; SQL engines host it via the contract-tested engine-state seam; pin-tx ownership ruled |
|
||||||
| [012](decisions/012-pre-decomposition-consistency-rulings.md) | Abort-as-data + window executor | sweep aborts are `GcAbortCause` report data, never errors; the next sweep executes overdue window rows; non-SQL engines stage in-process; GC state hosts on the fleet's kv engine |
|
| [012](decisions/012-pre-decomposition-consistency-rulings.md) | Abort-as-data + window executor | sweep aborts are `GcAbortCause` report data, never errors; the next sweep executes overdue window rows; non-SQL engines stage in-process; GC state hosts on the fleet's kv engine |
|
||||||
|
| [013](decisions/013-family-composition-reactive-boundary-and-feature-graph.md) | Sweep lease + composition boundary | the fleet sweeper lock's semantics = named exclusive sweep lease (provider unchanged: pg advisory lock); reactivity/cadence composition lives above the facade (alkstore the sibling substrate), never in-core |
|
||||||
|
|
||||||
## Open Questions
|
## Open Questions
|
||||||
|
|
||||||
@@ -162,5 +167,6 @@ a future requirement does not create one retroactively.
|
|||||||
- rudolfs — the physical-namespace anti-pattern (inverted)
|
- rudolfs — the physical-namespace anti-pattern (inverted)
|
||||||
- gix-odb — alternates/pool prior art for the p2p case
|
- gix-odb — alternates/pool prior art for the p2p case
|
||||||
- ADR-005; ADR-012 (the abort-as-data ruling, the window executor,
|
- ADR-005; ADR-012 (the abort-as-data ruling, the window executor,
|
||||||
the GC-state host); [store-api.md](store-api.md) (Pin semantics, sweep API);
|
the GC-state host); ADR-013 (§2 the sweep lease; §1 the composition
|
||||||
|
boundary); [store-api.md](store-api.md) (Pin semantics, sweep API);
|
||||||
[ops-surface.md](ops-surface.md) (namespace-as-resource ACL)
|
[ops-surface.md](ops-surface.md) (namespace-as-resource ACL)
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
---
|
---
|
||||||
status: draft
|
status: draft
|
||||||
last_updated: 2026-10-03 (ADR-012 round — no new OQs; overview's pg-only-kv feature-graph deferral recorded in the parked index)
|
last_updated: 2026-10-11 (ADR-013 round — OQ-11 resolved on in-tree evidence; the parked index is now empty)
|
||||||
---
|
---
|
||||||
|
|
||||||
# Open Questions
|
# Open Questions
|
||||||
@@ -46,9 +46,9 @@ and not a hedge. A decision is not "unmade" because its verification
|
|||||||
is pending; deferral policy applies to decisions, not to work.
|
is pending; deferral policy applies to decisions, not to work.
|
||||||
|
|
||||||
**Index of active OQs:** OQ-07, OQ-08 (externally-owned, carried
|
**Index of active OQs:** OQ-07, OQ-08 (externally-owned, carried
|
||||||
for visibility). OQ-09 and OQ-10 are resolved (recorded below).
|
for visibility). OQ-09, OQ-10, and OQ-11 are resolved (recorded
|
||||||
OQ-11 (deferred(scope), parked index) records a feature-graph
|
below). The parked index has no active rows —
|
||||||
deferral. Promoted Phase 0 questions OQ-BL-01..06 are recorded here
|
OQ-07/OQ-08 are externally-owned, not parked rows. Promoted Phase 0 questions OQ-BL-01..06 are recorded here
|
||||||
with their resolutions for traceability.
|
with their resolutions for traceability.
|
||||||
|
|
||||||
---
|
---
|
||||||
@@ -233,21 +233,33 @@ with their resolutions for traceability.
|
|||||||
|
|
||||||
## Parked index (deciding fact / owner — externally-owned rows are not blockers)
|
## Parked index (deciding fact / owner — externally-owned rows are not blockers)
|
||||||
|
|
||||||
|
No active parked rows remain. OQ-07/OQ-08 are externally-owned
|
||||||
|
(above), not parked; OQ-09/OQ-10/OQ-11 are resolved with their
|
||||||
|
records kept here for auditability.
|
||||||
|
|
||||||
| OQ | Status | Deciding fact / owner |
|
| OQ | Status | Deciding fact / owner |
|
||||||
|---|---|---|
|
|---|---|---|
|
||||||
| OQ-07 | externally-owned | alkgit's architecture process (answerable on paper anytime; gates nothing here) |
|
| OQ-07 | externally-owned | alkgit's architecture process (answerable on paper anytime; gates nothing here) |
|
||||||
| OQ-08 | externally-owned | alkfs Phase 0 intake |
|
| OQ-08 | externally-owned | alkfs Phase 0 intake |
|
||||||
| OQ-11 | deferred(scope) | a named deployment requirement for a pg-only kv node (postgres kv engine without rusqlite in the build) — overview.md's feature-graph note |
|
|
||||||
|
|
||||||
OQ-11 records [overview.md](overview.md)'s dependency-posture deferral
|
OQ-11 was `deferred(scope)` on "a named deployment requirement for a
|
||||||
(a pg-only kv node — the `kv` feature's rusqlite pull decoupled from
|
pg-only kv node arriving from outside this crate" (overview.md's
|
||||||
default-on feature graphing) for auditability: the deciding fact is a
|
feature-graph note; the audit trail of the parked wording is kept:
|
||||||
*deployment requirement that arrives from outside this crate*
|
the `kv` feature's rusqlite pull decoupled from default-on feature
|
||||||
(Schrödinger's-code rule compliant — it is not a fact this crate
|
graphing). The 2026-10-11 audit round resolved it as a live circular
|
||||||
creates), the accepted v1 shape (base `kv` feature pulls rusqlite,
|
hedge: the deciding fact already existed in this tree — REQ-2
|
||||||
default-on) stands until then, and the deferral is a feature-graph
|
(operator authority, 2026-10-03, pre-POC) names the deployment
|
||||||
work item, not an open architecture decision. No spec or ADR gates on
|
requirement, and ADR-009 shipped its consolidation posture ("a fleet
|
||||||
it.
|
node can consolidate on postgres-only"), which is the pg-pure node
|
||||||
|
whose feature graph was parked — the same invalid shape OQ-09/OQ-10
|
||||||
|
were rejected for, with the deciding fact created by the tree's own
|
||||||
|
progress and nobody re-auditing the row. Resolved by
|
||||||
|
[ADR-013](decisions/013-family-composition-reactive-boundary-and-feature-graph.md)
|
||||||
|
§4: engine features pull driver clusters independently (the ADR-010
|
||||||
|
§1 discipline extended to sqlite — feature `sqlite`, default-on,
|
||||||
|
carrying rusqlite; `kv`/`large` tier features retired; `local`
|
||||||
|
ungated), and a pg-pure build is a supported configuration this
|
||||||
|
crate does not default to. No deferral remains.
|
||||||
|
|
||||||
OQ-09 was `deferred(scope)` on the first embedded ops deployment — a
|
OQ-09 was `deferred(scope)` on the first embedded ops deployment — a
|
||||||
deciding fact that could only exist once the ops module ships, i.e. a
|
deciding fact that could only exist once the ops module ships, i.e. a
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
---
|
---
|
||||||
status: draft
|
status: draft
|
||||||
last_updated: 2026-10-03 (ADR-012 — OQ-11 parking reference; ADR-012 row added)
|
last_updated: 2026-10-11 (ADR-013 — family composition & reactive boundary; OQ-11 resolved, feature graph re-pinned to engine features)
|
||||||
---
|
---
|
||||||
|
|
||||||
# Overview
|
# Overview
|
||||||
@@ -72,8 +72,9 @@ or three times.
|
|||||||
┌──────────────────────┐ ┌─────────────────────────────────┐
|
┌──────────────────────┐ ┌─────────────────────────────────┐
|
||||||
│ kv tier (ADR-003/ │ │ large tier (ADR-003/008/011; │
|
│ kv tier (ADR-003/ │ │ large tier (ADR-003/008/011; │
|
||||||
│ 007/011) │ │ formerly "fs") │
|
│ 007/011) │ │ formerly "fs") │
|
||||||
│ sqlite (default) │ │ local (default) │
|
│ sqlite ("sqlite"; │ │ local (ungated; ADR-013 §4) │
|
||||||
│ postgres ("postgres") │ pg-lo ("pg-lo"; ADR-009) │
|
│ default engine) │ │ pg-lo ("pg-lo"; ADR-009) │
|
||||||
|
│ postgres ("postgres") │ │
|
||||||
│ mem ("mem"; tests + │ │
|
│ mem ("mem"; tests + │ │
|
||||||
│ contract reference) │ │
|
│ contract reference) │ │
|
||||||
└──────────────────────┘ └─────────────────────────────────┘
|
└──────────────────────┘ └─────────────────────────────────┘
|
||||||
@@ -90,24 +91,31 @@ or three times.
|
|||||||
|
|
||||||
| Layer | Dependencies | Notes |
|
| Layer | Dependencies | Notes |
|
||||||
|---|---|---|
|
|---|---|---|
|
||||||
| base crate (store core + tier engines) | `tokio`, `thiserror`, `parking_lot`, plus `rusqlite` behind the `kv` feature and nothing behind `large`/`mem` | no alkcall, no serialization frameworks |
|
| base crate (store core + tier dispatch + GC) | `tokio`, `thiserror`, `parking_lot` | no alkcall, no reactive substrate, no serialization frameworks |
|
||||||
| `postgres` engine feature (ADR-007) | adds `tokio-postgres` + `deadpool-postgres` behind the kv tier's second engine, default-off | depends on `kv`'s contract surface (the `Backend` trait definition is base-crate code); the engine's *impl* is additive. Same `Backend` contract, own sweep-safety CI gate under `--all-features` |
|
| engine features (ADR-010 §1, extended to every engine by ADR-013 §4) | each engine pulls only its own driver cluster | `sqlite` (default-on, `rusqlite`), `postgres` (default-off, `tokio-postgres`+`deadpool-postgres`), `mem` (default-off, nothing), `pg-lo` (default-off, the shared pg cluster); `local` is ungated (dependency-free). Every engine-feature combination is a coherent node shape (e.g. `large`=pg-lo + kv=sqlite, ADR-010 §1); the feature-gated engines' conformance/sweep-safety gates run under `--all-features` (ADR-007/009) |
|
||||||
| `pg-lo` engine feature (ADR-009) | Large Objects as the large tier's second engine, default-off | pulls the same `tokio-postgres`/`deadpool-postgres` cluster as the `postgres` kv engine (SQL `lo_*` functions, no additional dependency); **does not imply the `postgres` feature** — engine features pull the driver cluster independently (ADR-010 §1), so a `large`=pg-lo + kv=sqlite node is coherent and supported; same admission/test-gate shape |
|
|
||||||
| `mem` engine feature (ADR-011) | the kv tier's ephemeral engine, default-off | `BTreeMap`-shaped; the contract-reference engine; no dependencies |
|
|
||||||
| `ops` feature | adds `alkcall` (feature-gated, default-off) | JSON op specs + binary channels + `AccessControl`; ADR-001 |
|
| `ops` feature | adds `alkcall` (feature-gated, default-off) | JSON op specs + binary channels + `AccessControl`; ADR-001 |
|
||||||
| consumer crates | depend on alkblobs (and on alkcall directly when they speak ops) | alkgit, alkfs |
|
| consumer crates | depend on alkblobs (and on alkcall directly when they speak ops) | alkgit, alkfs — which also deploy the sibling reactive substrate beside it (below) |
|
||||||
|
|
||||||
Tiers are feature-gated; both default-on (batteries-included, the
|
**The sibling reactive substrate (ADR-013 §1/§3):** **alkstore**
|
||||||
alkgit feature-model pattern — default-on features, opt-out rather
|
(v0.1.0 — notify/locks/queues/streams/outbox/scheduler behind its own
|
||||||
than opt-in); the engine features `postgres` and `pg-lo`, and `mem`,
|
engine crates) ships beside this crate, not under it. The core keeps
|
||||||
are default-off, as are `ops`. A pg-only kv node (postgres engine
|
no alkstore dependency and no reactive machinery: cadence and
|
||||||
without rusqlite) is **not** a supported configuration this crate
|
invalidation wiring are deployment-layer composition above the facade
|
||||||
optimizes for — feature-graph work for it is deferred until a named
|
(ADR-005's "embedder owns cadence" stands, with the sibling the
|
||||||
requirement exists (the base `kv` feature pulling rusqlite is the
|
named substrate; sweep outcomes are report data — ADR-012 §1 — whose
|
||||||
accepted v1 shape; convention 8's lean-base principle applies to
|
pinned consumer is that wiring). Cross-store atomicity is unachievable
|
||||||
*default* builds, which stay lean since `kv` is what the feature model
|
by construction; the composition properties are idempotent re-puts,
|
||||||
controls, not what default-on means downstream; tracked as OQ-11 in
|
windowed deletes, and reports-as-data (ADR-013 §3, the citable
|
||||||
open-questions.md's parked index). Wasm: the tier
|
downstream contract).
|
||||||
|
|
||||||
|
Tiers are **constructor composition**, not features (ADR-013 §4);
|
||||||
|
engine features as in the table above. The default build is
|
||||||
|
batteries-included (sqlite default-on — opt-out rather than opt-in,
|
||||||
|
the alkgit feature-model pattern), and a pg-pure build (sqlite off;
|
||||||
|
kv=postgres, `large`=pg-lo if wanted) is a supported configuration
|
||||||
|
this crate does not default to — REQ-2's consolidation posture
|
||||||
|
(ADR-009), now carried by the feature graph instead of parked on it.
|
||||||
|
Wasm: the tier
|
||||||
engines are not a wasm story; a wasm client is a consumer of the `ops`
|
engines are not a wasm story; a wasm client is a consumer of the `ops`
|
||||||
surface (which rides alkcall, itself wasm-clean), never an in-process
|
surface (which rides alkcall, itself wasm-clean), never an in-process
|
||||||
embedder (ADR-001 §Consequences).
|
embedder (ADR-001 §Consequences).
|
||||||
@@ -124,6 +132,10 @@ embedder (ADR-001 §Consequences).
|
|||||||
Backend to model paths (ADR-004).
|
Backend to model paths (ADR-004).
|
||||||
- No chunk-tree/CDC storage encoding; whole-file CAS is the store's only
|
- No chunk-tree/CDC storage encoding; whole-file CAS is the store's only
|
||||||
granularity (ADR-006).
|
granularity (ADR-006).
|
||||||
|
- No reactive coordination machinery in-core — no notify, no scheduler,
|
||||||
|
no queues, no invalidation hooks: reactivity is the sibling
|
||||||
|
substrate's (alkstore), composed at the deployment layer above the
|
||||||
|
facade (ADR-013 §1).
|
||||||
- No per-repo or per-namespace walled stores — one pooled CAS
|
- No per-repo or per-namespace walled stores — one pooled CAS
|
||||||
(ADR-005).
|
(ADR-005).
|
||||||
|
|
||||||
@@ -143,6 +155,7 @@ embedder (ADR-001 §Consequences).
|
|||||||
| [010](decisions/010-backend-io-seams-and-fleet-state-home.md) | I/O seams + GC-state home | read cursor / staged put; store-core-owned GC state via the engine-state seam; kv fleet-validity rule; pin-token renewal |
|
| [010](decisions/010-backend-io-seams-and-fleet-state-home.md) | I/O seams + GC-state home | read cursor / staged put; store-core-owned GC state via the engine-state seam; kv fleet-validity rule; pin-token renewal |
|
||||||
| [012](decisions/012-pre-decomposition-consistency-rulings.md) | Sweep-abort shape + composition rulings | sweeps never error (`GcAborted` retired, `GcRefuse` for direct-delete); token wire shape; fleet mode = constructor declaration; GC state hosts on the kv engine; sqlite seam reduction |
|
| [012](decisions/012-pre-decomposition-consistency-rulings.md) | Sweep-abort shape + composition rulings | sweeps never error (`GcAborted` retired, `GcRefuse` for direct-delete); token wire shape; fleet mode = constructor declaration; GC state hosts on the kv engine; sqlite seam reduction |
|
||||||
| [011](decisions/011-vocabulary-tiers-engines-and-fleet-state.md) | Vocabulary + rename | tier/engine/instance/node/fleet pinned; mem joins the kv tier; large tier renamed `large`; constructor modes |
|
| [011](decisions/011-vocabulary-tiers-engines-and-fleet-state.md) | Vocabulary + rename | tier/engine/instance/node/fleet pinned; mem joins the kv tier; large tier renamed `large`; constructor modes |
|
||||||
|
| [013](decisions/013-family-composition-reactive-boundary-and-feature-graph.md) | Family composition & reactive boundary | no sibling dependency in-core; sweep lease semantics (provider: raw advisory clause); co-tenancy + no-cross-store-atomicity; OQ-11 resolved (engine features pull own drivers) |
|
||||||
|
|
||||||
## Open Questions
|
## Open Questions
|
||||||
|
|
||||||
@@ -157,8 +170,9 @@ crate level:
|
|||||||
open-read requirement)
|
open-read requirement)
|
||||||
- **OQ-10**: second kv engine — resolved by ADR-007 (sqlite + postgres
|
- **OQ-10**: second kv engine — resolved by ADR-007 (sqlite + postgres
|
||||||
shipped behind one trait; per-node constructor choice)
|
shipped behind one trait; per-node constructor choice)
|
||||||
- **OQ-11**: pg-only kv node feature graph — deferred(scope), parked
|
- **OQ-11**: pg-only kv node feature graph — resolved by ADR-013 §4
|
||||||
(see [open-questions.md](open-questions.md))
|
(engine features pull their own driver clusters; `sqlite` default-on;
|
||||||
|
pg-pure builds supported, non-default)
|
||||||
- **POC #7** (`docs/research/poc-pglo-findings.md`): pg Large Objects
|
- **POC #7** (`docs/research/poc-pglo-findings.md`): pg Large Objects
|
||||||
as the large tier's `pg-lo` engine — **passed** 2026-10-03; admitted as
|
as the large tier's `pg-lo` engine — **passed** 2026-10-03; admitted as
|
||||||
a shipped engine by ADR-009 (REQ-2's consolidation option)
|
a shipped engine by ADR-009 (REQ-2's consolidation option)
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
---
|
---
|
||||||
status: draft
|
status: draft
|
||||||
last_updated: 2026-10-03 (vocabulary pinned by ADR-011 — tiers/engines/instances/fleet; large tier renamed `large`)
|
last_updated: 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
|
# Requirements
|
||||||
@@ -102,12 +102,47 @@ path→hash manifests above.
|
|||||||
performance is what matters; the large tier is hit rarely (large
|
performance is what matters; the large tier is hit rarely (large
|
||||||
workspace artifacts).
|
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
|
## What this document is not
|
||||||
|
|
||||||
- **Not consumer architecture**: how alkgit maps refs/manifests onto
|
- **Not consumer architecture**: how alkgit maps refs/manifests onto
|
||||||
the store remains OQ-07 (externally-owned); how alkfs shapes
|
the store remains OQ-07 (externally-owned); how alkfs shapes
|
||||||
workspaces remains OQ-08. This document records what each use case
|
workspaces remains OQ-08. This document records what each use case
|
||||||
*forces on this crate*, not what each consumer *is*.
|
*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
|
- **Not a requirements-change process substitute**: a new or changed
|
||||||
REQ arrives here with a dated note and owner; ADRs that depended on
|
REQ arrives here with a dated note and owner; ADRs that depended on
|
||||||
the old fact state their reopen conditions and reopen when this
|
the old fact state their reopen conditions and reopen when this
|
||||||
@@ -120,4 +155,5 @@ path→hash manifests above.
|
|||||||
| REQ-1 | 2026-10-03 (fact: phase-0 §deployment posture) | POC #3 A4 | ADR-007 |
|
| 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-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-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-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 |
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
---
|
---
|
||||||
status: draft
|
status: draft
|
||||||
last_updated: 2026-10-03 (ADR-012 — sweep-abort = Ok(report) with GcAbortCause; GcAborted retired; fall-through for all key-addressed ops; put-time kv-only rejection; delete/missing/error-model ruling)
|
last_updated: 2026-10-11 (ADR-013 — the fleet sweeper lock's semantics pinned as the named exclusive sweep lease; provider unchanged; composition boundary referenced)
|
||||||
---
|
---
|
||||||
|
|
||||||
# Store API
|
# Store API
|
||||||
@@ -138,10 +138,15 @@ mechanism lives in the engine-state seam, ADR-010 §2).
|
|||||||
mechanism-extended by ADR-008: pins are DB-backed rows committed
|
mechanism-extended by ADR-008: pins are DB-backed rows committed
|
||||||
atomically with entries (the store core holds the joint tx,
|
atomically with entries (the store core holds the joint tx,
|
||||||
ADR-010 §2; all GC state hosts on the fleet's kv engine —
|
ADR-010 §2; all GC state hosts on the fleet's kv engine —
|
||||||
ADR-012 §4), one sweeper holds the advisory lock (contention is a
|
ADR-012 §4), one sweeper holds the advisory lock (named exclusive
|
||||||
report abort, not an error — ADR-012 §1), and delete
|
sweep lease semantics: one sweeper per pool, atomic test-and-set,
|
||||||
|
holdership ends at release/owner death/engine-session loss;
|
||||||
|
contention is a report abort, not an error — ADR-012 §1, semantics
|
||||||
|
pinned ADR-013 §2), and delete
|
||||||
arbitration is SQL-level — the facade's invariant text is
|
arbitration is SQL-level — the facade's invariant text is
|
||||||
unchanged.
|
unchanged. The facade owns no reactive machinery: cadence,
|
||||||
|
maintenance dispatch, and invalidation wiring are embedder
|
||||||
|
composition above it (ADR-005; ADR-013 §1).
|
||||||
|
|
||||||
## Error model
|
## Error model
|
||||||
|
|
||||||
@@ -231,6 +236,7 @@ lesson generalizes to any kv engine).
|
|||||||
| [008](decisions/008-trait-size-probe-fleet-gc-and-large-engines.md) | Trait `size` + fleet GC | stat/accounting ride the trait's `size` probe; fleet pins/sweeps are engine-backed |
|
| [008](decisions/008-trait-size-probe-fleet-gc-and-large-engines.md) | Trait `size` + fleet GC | stat/accounting ride the trait's `size` probe; fleet pins/sweeps are engine-backed |
|
||||||
| [010](decisions/010-backend-io-seams-and-fleet-state-home.md) | I/O seams + GC-state home | read cursor / staged put behind the facade; the store core holds the joint entry+pin tx |
|
| [010](decisions/010-backend-io-seams-and-fleet-state-home.md) | I/O seams + GC-state home | read cursor / staged put behind the facade; the store core holds the joint entry+pin tx |
|
||||||
| [012](decisions/012-pre-decomposition-consistency-rulings.md) | Sweep-abort shape + wire/activation rulings | swept = Ok(report) always, `GcRefuse` for direct-delete refusal; fleet mode is a constructor declaration; GC state hosts on the kv engine |
|
| [012](decisions/012-pre-decomposition-consistency-rulings.md) | Sweep-abort shape + wire/activation rulings | swept = Ok(report) always, `GcRefuse` for direct-delete refusal; fleet mode is a constructor declaration; GC state hosts on the kv engine |
|
||||||
|
| [013](decisions/013-family-composition-reactive-boundary-and-feature-graph.md) | Sweep lease + composition boundary | the advisory lock's semantics = named exclusive sweep lease (provider unchanged); no reactive machinery/facade hooks in-core |
|
||||||
|
|
||||||
## Open Questions
|
## Open Questions
|
||||||
|
|
||||||
|
|||||||
Reference in new issue
Block a user