3a51bb43fcc6ed711225862fe93fc1c76bc0e24e
68
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
3a51bb43fc |
feat(store): the fleet GC protocol — the sweep lease, SQL arbitration, pin reaping, and the fleet liveness verb (wave 6)
Grow the sweep and delete paths into their fleet forms under fleet: true (ADR-008 §2, ADR-010 §2, ADR-012 §1/§5/§6.6/§6.7, ADR-013 §2); the non-fleet forms are byte-unchanged. - the sweep lease: sweep() composes the in-process single-flight flag with the SQL lease (sweep_lock_acquire on the fleet host), both held for the whole sweep and released on every exit path; contention at either domain aborts as SweeperLock report data, nothing deleted; the pool-sizing composition enforced (max_size >= 2 at the fleet constructors' entry — the lease holds one checkout; the body's clauses ride further checkouts) - SQL-level delete arbitration: arbitrated_delete composes the two domains (in-process first — cheap, local; the engine's arbitrate second — fleet-authoritative) on the direct path (GcRefuse) and the window execution's re-arbitration; a foreign instance's un-expired pin row or liveness-table row refuses cross-instance - the maintenance step: EngineFleetState::pin_reap (the seam as the named clause home) reaps exactly the expired pin rows, one statement on the postgres host, contract-tested, driven under the lease before the overdue execution - the fleet liveness verb: register_liveness_source takes the fleet-shaped LivenessRegistration argument (ADR-012 §6.6's same-verb wording) — the named-table form registers the embedder's own table (schema-ownership rule holds; idempotent; deregistration composes via a new engine-side unregister), the in-process form stays legal (instance-local protection, documented); Registration carries its kind; the verb is fallible (the fleet form's error surface) - the protect callback's fleet posture recorded: single-node-only, instance-local consult remains, materialization is the embedder's duty — no new machinery - the fleet sweep body: protect consult → reap → execute overdue (two-domain re-arbitration, fall-through delete across both tiers) → mark (pool + in-process sources; SQL liveness consulted at arbitration time, never enumerated) → stage → release; counts exact relative to the schedule - tests: lease contention/reaping between two instances, the foreign-row GcRefuse, the exact-set reap, the embedder-table protection + correction flow + malformed and fleet:false rejections, the two-instance exact-count shape, and the delay race in its fleet form; the wave-6 inert-machinery construction test rewritten to the now-live fleet semantics Verification: cargo test (default) green; cargo test --features postgres green server-less (skipping) and against pglo-poc :15432; cargo test --all-features green; cargo clippy --all-targets -D warnings green (default, postgres, all-features); cargo fmt --check green; cargo doc --no-deps clean (postgres-feature warnings unchanged at the pre-existing 21). |
||
|
|
950af9ab0d |
feat(store): the fleet put path — the joint entry+pin tx driver and the pin guard's fleet form (wave 6)
- Under fleet: true, puts route through the joint entry+pin tx (store-fleet-put): kv tier via begin_joint_tx -> joint_put (CAS; a dedup re-put commits the pin row alone, upsert-renewing (key, owner)) -> pin_commit — entry + pin row in one step, verified against an independent connection; large = pg-lo rides the one-tx consolidation form (known- and unknown-length; the 512 KiB knee, the empty-contribution re-put form, read-your-own-writes derivation); large = local on declared shared media is pin-first, publish-second (the row-only joint tx, then the staged commit-rename; the orphan-pin crash window is the expiry machinery's). - Amendment A1 landed: catch-propagate-terminate — every error path between begin_joint_tx and pin_commit terminates the held tx before propagating (no mid-flight PgJointTx returns dirty to the pool, PG-2); tested with a forced mid-driver failure (zero residue, the pool's idle-in-transaction count zero, a follow-up put clean). - The pin guard's fleet form: the returned guard carries the row's TTL/3 renewal duty — a per-guard tokio task (spawned at the row's durability), silent on Err, ended by the never-resurrect Ok(false), aborted at every guard-life boundary (drop, batch contribution, conversion); PinRenewal gated feature postgres. - KvEngine::Postgres holds Arc<PostgresBackend> so detached renewal tasks share the fleet host's engine; tokio gains the time feature for the cadence. - Non-fleet put paths byte-unchanged (all existing suites green); a non-fleet test pins that no pin rows are ever committed. - Tests: new postgres_fleet_put_tests (fresh schema + SchemaJanitor; kv joint atomicity both-or-neither, forced pin_commit failure, renewal cadence/drop/conversion stop, never-resurrect, batch release ends renewals, dedup upsert-renewal + by-row composition, local pin-first success + forced-failure forms, non-fleet non-event) and postgres_pglo_fleet_put_tests (fresh db + DatabaseJanitor; the A1 pg-lo failure form, the consolidation round-trip through the public put). Env-driven, skipping cleanly server-less. Verified: cargo test (default) green; --features postgres and --features "postgres,pg-lo" green server-less (skipping) and against the harness server (pglo-poc, :15432); --all-features green live; pg-pure and mem,pg-lo pure builds green; clippy -D warnings green (all combos); cargo fmt --check green; cargo doc --no-deps clean (default; the gated builds' pre-existing warnings unchanged). Schema/db residue 0. |
||
|
|
76be7b0273 |
feat(pg-lo): the joint contribution — staged-put mechanics into the store-held tx (wave 6)
The wave-5 deferred design point, landed: under the consolidation posture (kv = postgres + large = pg-lo, same pg instance), a large entry's content reaches the store-held joint transaction the pin row commits in (ADR-012 §4's one-tx ruling). - The contribution's API home (the named design point) — decided: the joint staging-cursor variant. `PgLoBackend::put_joint_staged(&mut PgJointTx)` hands `PgLoJointStaging<'_>`, the staged put with inverted ownership: it borrows the held checkout, owns no checkout, opens no tx, never COMMITs, never ROLLBACKs (the driver owns termination, amendment A1) — no Drop, by design. The deciding facts: the large tier's put contract is the streaming cursor, the fleet put driver routes to the joint form as the non-fleet path routes to the staged form, and the unknown-length derivation pass reads back through the cursor's `ReadCursor` seam — none servable by a whole-value method. - The mechanics are the landed bodies re-homed: the 512 KiB `lowrite` knee (LOWRITE_CHUNK aggregation of the store core's 64 KiB windows), read-your-own-writes `lo_get` windows, the publish-time CAS (the key resolves at publish — the staged put's named design point), the companion-row insert, transactional LO creation (C6: the LO lives and dies with the store-held tx). The re-put form publishes an empty contribution — the staged LO unlinked inside the store-held tx, zero residue; a concurrent CAS miss surfaces the companion key's unique violation (the landed puts' posture). - Enabling affordance: `PgJointTx::client()` — the crate-internal statement surface every joint contribution (and the store-fleet-put driver's A1 termination design) rides through; deliberately not an abort verb. - Crate-internal seam: the public trait surface untouched, nothing of `EngineFleetState` on pg-lo (ADR-010 §2's correction), the engine-level held-tx staging cursor and the non-fleet put path byte-unchanged. Gated `feature = "postgres"`; both pg-lo pure builds compile. - Contract tests (`src/pg_lo.rs` mod joint, both engines on one dedicated database): one-tx publish (kv row + LO + companion row + pin row, byte-exact round-trips after `pin_commit`); forced failure mid-contribution → driver ROLLBACK → zero residue (no LO pages, no companion row, no pin row, no kv row); the CAS re-put form (empty contribution, upsert renewal); the 512 KiB knee (multi-chunk). Verification: `cargo test` (default) green; `--features "pg-lo,postgres"` green server-less (skipping) and against the harness server (pglo-poc, :15432 — the four joint tests + full pg_lo + conformance suites live); `--all-features` green; clippy `-D warnings` green (default / postgres / pg-lo+postgres / all-features / the two pg-lo pure builds); `cargo fmt --check` green; `cargo doc --no-deps` clean. The store-core driver that calls the contribution on the fleet put path is store-fleet-put. |
||
|
|
b6b353242b |
feat(store): the fleet declaration — the composite fleet-validity predicate at the constructor (wave 6)
- Three `_fleet` twin constructors slot the explicit `fleet: true` declaration in without reshaping the fleet-capable constructors; every pre-existing constructor stays implicitly `fleet: false` with byte-unchanged behavior (the recorded non-event landed as recorded): `kv_only_postgres_fleet` (the re-routing client posture, large = none), `dual_tier_postgres_local_fleet` (the shared-media posture), `dual_tier_postgres_pglo_fleet` (the consolidation posture). - The composite fleet-validity predicate (`fleet_validity_error`) is one function, one sentence, both tiers, run at the constructor keyed off the engine representation: kv = postgres AND (pg-lo on the same pg instance OR local on declared shared media OR none). The structural clauses (mem never, sqlite never, local needs the declaration) are enforced by the constructor set's shape and tested per clause at the predicate level; the pg-lo same-instance clause is the runtime arm: `pg_control_system().system_identifier` compared across one pooled connection per engine — mismatch or failed probe is a constructor error, never a silent pass. - The named design points decided and recorded: the shared-media assertion is a sealed dedicated type (`FleetSharedMedia::asserted()`, never a bare flag); the pin TTL is a fleet constructor parameter (default `DEFAULT_PIN_TTL` 60 s, `Duration::ZERO` rejected at entry) wired through `PostgresBackend::new_with_pin_ttl` to the renewal body (proven against the live server); the owner id mints 32 raw bytes at construction (SHA-256 fold over pid/nanos/instance counter/ASLR anchor), stable per instance, crate-internal. - The store carries its fleet state (`FleetState`: the declaration flag, the TTL, the owner id) — crate-internal (fleet parameters, not API); the one-window-host posture verified intact under fleet (kv host's both-tables GC schema, pg-lo window clauses dormant, the form branch keyed off the engine representation), and the declaration turns nothing else on yet (put/sweep run the non-fleet machinery until the driver tasks). - Constructor docs updated to the landed state (module doc's fleet-declaration paragraph, the three non-fleet recordings, the scope notes, gc.rs/postgres.rs/pg_lo.rs wave-6 notes); `cargo doc --no-deps` clean in the default build. Verification: cargo test (default) green; --features postgres and --features "postgres,pg-lo" green server-less (skipping cleanly) and against the harness server (pglo-poc, :15432 — verified live 2026- 10-12); --all-features green; clippy -D warnings green (default + postgres + postgres,pg-lo + all-features); cargo fmt --check green; cargo doc --no-deps clean. |
||
|
|
009de420e1 |
docs(tasks): decompose wave 6 — the fleet GC mechanism, the architecture's most intricate state machine under concurrency
- store-fleet-construction: the fleet: true declaration slotting into the fleet-capable constructors (the recorded non-event), the composite fleet-validity predicate enforced at the constructor (kv=postgres AND pg-lo-same-instance OR local-on-declared-shared- media OR kv-only re-routing), the TTL/owner-id fleet state, the same-instance probe design point - engine-pg-lo-joint: the pg-lo joint contribution — staged-put mechanics (512 KiB knee, CAS, transactional LO creation) into the store-held PgJointTx, the wave-5 deferred API-shape design point - store-fleet-put: the joint entry+pin tx driver (amendment A1's drop discipline lands here — its recorded owner), the pin guard's TTL/3 renewal form, the dedup upsert-renewal, pin-first/ publish-second for large=local, the consolidation one-tx put - store-fleet-sweep: the SQL sweep lease (ADR-013 §2 semantics), SQL-level delete arbitration on the direct + window-execution paths, the expired-pin reaping clause (the seam as the named home for fleet-clause growth), the fleet liveness registration verb, the two-domain arbitration composition - suite-column-fleet: the fleet column — two-instance factories over one schema, the exact-count fleet sweep rows (the wave's gate), lease contention, cross-instance arbitration, TTL/renewal rows, the fleet-validity predicate rows - review-wave-6: the fleet gate per the plan, plus a standing code-smell/hygiene block — cargo doc --no-deps asserted clean (the twice-bitten PGLO-1 rustdoc private-link class), the allow(dead_code) scope notes audited against their remainder, stale wave-6 forward references, and task-Summary-vs-diff correctness Harness server cited in every server-touching task: dockerized pglo-poc (postgres:16-alpine) on :15432, db blobs, postgres/poc (verified live 2026-10-12); env-driven ALKBLOBS_PG_* settings, clean server-less skips, acceptance requires green against the server. Dependency graph validated: two parallel roots (construction, pg-lo joint), put merging them, sweep parallel to put, the fleet column merging, the review gate last; no cycles. |
||
|
|
93f9a9332c |
docs(review): the wave-5 review gate — every gate check green
The engine-wave gate read the pg-lo engine, its window-family seam, the constructor wiring, and the suite column against the pinned ADR text and found them conformant. One finding fixed inline (PGLO-1, the wave-5 rustdoc private-link warnings — see the previous commit); no wave-6 amendments arise from this gate. All gates re-run green after the fix. Wave 6 (fleet GC) decomposition is unblocked. |
||
|
|
cad799b28e |
fix(store): rustdoc hygiene — the wave-5 module doc's private-item links de-linked
The wave-5 landing introduced four rustdoc warnings (cargo doc was no longer clean): the public store module doc's new intra-doc links resolved to crate-internal items — AnyCursor, KvCursor, LargeCursor, and the store-core ensure_gc_schema_at_construction. The names are prose now (no links): private items never resolve from public docs. Verified: cargo doc --no-deps zero warnings; cargo clippy --all-targets -- -D warnings green; cargo fmt --check green. Found by the wave-5 review gate (tasks/review-wave-5.md PGLO-1). |
||
|
|
aa96f8b981 |
feat(tests): the conformance suite's pg-lo column — the LO-medium SKIP-posture column green (wave 5)
- tests/conformance/pg_lo_column.rs (new): the two pair-gated factories (DualTierMemPgLoFactory on all(mem, pg-lo) — the one-window-host rule's named test topology; DualTierPostgresPgLoFactory on all(postgres, pg-lo) — the consolidation posture, two schemas per open, carrying the dormancy row), a fresh dedicated database per open with the engine-owned schemas minted inside, teardown reaping tracked LOIDs (lo_unlink) then dropping the minted schemas then the database (WITH (FORCE)) with per-open drop-guard janitors for the abandoned paths, and the rows: the verbatim form-independent mirror (INV-1, INV-6, the two no-op abort shapes), the schedule-relative INV-2/3, the two-sweep exact counts over the full protection set across each factory's observed window host, the window-delay race, the holder-stages-only lease, INV-5's durable row, the three dispatch rows over the LO-medium placement oracle, the INV-1 extension (window-edge ranged reads + the planted-overclaim beyond-length clean failure), the virgin-store rows, and the dormancy row; structural isolation + panicking-row tests probing zero orphan residue (no kv-only rejection row — recorded; pg-lo is a large engine) - the LO-isolation corollary carried in the engine task's recorded per-test-database shape (the C7 lesson): zero LO residue on the shared harness database is structural, so the engine suite's shared_harness_db_holds_zero_lo_residue gate stays green alongside the column under --all-features - main.rs: the pg_lo_column gate (feature pg-lo, default-off) + the columns' doc paragraph - README: the pg-lo rows table in §Columns beyond the reference set, the SKIP-posture note naming the second landed column and the per-factory feature-pair gating, current state = five columns - store.rs: one mechanical gate fix — the PgOpts import was unused in the sqlite+pg-lo config (tripping clippy --features pg-lo -D warnings); the gate now names the configs that consume it Verification (harness server live 2026-10-12): --all-features against the server fully green (188 lib + 106 conformance + 4 git-interop; zero LO/schema/database residue after the run); default test green (69/19/3); --features "pg-lo,mem" and "postgres,pg-lo" green server-less (skipping cleanly) and --features pg-lo alone; clippy -D warnings green on default, pg-lo, pg-lo+mem, postgres+pg-lo, all-features; fmt --check green; cargo doc --no-deps clean. |
||
|
|
cddcea3cfa |
feat(store): the pg-lo constructor wiring — the consolidation shape and the window-host completion (wave 5)
- LargeEngine::PgLo slots in behind `pg-lo` + all six large-op dispatch arms; the large cursor/staging bodies land as the LargeCursor/LargeStaging sub-enums behind AnyCursor/AnyStaging's one large arm (the recorded KvCursor pattern's large-tier twin) - dual_tier_postgres_pglo (the consolidation posture; two DSN+opts pairs, implicitly non-fleet, completing the fleet-valid engine set) and dual_tier_mem_pglo (the one-window-host rule's named test topology), both carrying the constructor-tunable delete-window delay - ensure_gc_schema_at_construction: the one-window-host rule's store-core dispatch (kv host on dual-SQL — pg-lo's window clauses dormant; pg-lo host on kv=mem); the sweep's mem arm gains the large-host delegation, the branch keyed off the engine representation never a flag - stale scope notes updated to the landed state (store.rs module doc, LargeEngine, the sweep docs, PUT_CHUNK, pg_lo.rs, gc.rs) - 7 new env-driven constructor tests (dedicated-database janitor discipline; consolidation + mem-pglo both shapes Verified live against the pglo-poc harness server: cargo test default 69/19/3, --features pg-lo 92/19/3, --features "pg-lo,mem" 151/54/4 (server-less 124/37/4), --no-default-features "postgres,pg-lo" 102/19/3 (both live and server-less), --all-features 188/71/4; clippy -D warnings and fmt --check green on all four configurations; cargo doc clean. The literal kv-less pg-lo config re-confirmed structurally uncompilable (d69d9a3's finding; not a gate). |
||
|
|
c8fc519978 |
feat(gc): the pg-lo engine-state window family — the conditional window host (wave 5)
- impl EngineWindowState on PgLoBackend: ensure_gc_schema creates gc_window only (never gc_pins, never the embedder's liveness table), window_stage upserts idempotent per (key, deadline), window_take is the one atomic DELETE ... RETURNING step over the <= boundary with the 42P01 -> empty virgin-take split - nothing of EngineFleetState exists on this engine (ADR-012 §5's pg-lo row: fleet pins live on the kv host, sweep lock no); the one-window-host dormancy posture is recorded in the impl doc - five contract tests mirror the postgres/sqlite suites (schema-only + row shape, stage idempotence/restage, take-exact-set/never-twice, the <= boundary with nanosecond slack, both virgin shapes); env-driven, skipping cleanly server-less, green live against the harness server Verification: cargo test (default + --features pg-lo, server-less and live), clippy -D warnings (default, pg-lo, + the pure builds "pg-lo,mem" and "postgres,pg-lo"), cargo fmt --check, cargo doc --no-deps |
||
|
|
fb24f30e0a |
docs(tasks): correct the wave-5 pure-build gates — the literal kv-less pg-lo config is not a gate
The wave-5 engine task (
|
||
|
|
d69d9a345b |
feat(pg-lo): the large-tier engine — LOs + companion table behind the Backend trait (wave 5)
Implement the pg-lo engine (ADR-009, POC #7 C1-C7) in src/pg_lo.rs behind feature pg-lo (default-off), plus the shared-helper home the task's design points resolve into src/pg_common.rs. - Engine: seven Backend methods + put_staged; the lo_entries companion table is the contract authority (list/size/has/CAS table-level; pg_largeobject read only by the orphan sweep's candidate enumeration); one-tx puts with 512 KiB lowrite statements (LOWRITE_CHUNK, the C2 knee), the descriptorless lo_get window cursor (READ_CHUNK; EOF clamping + the unexpected-EOF clean failure), the held-tx staging cursor aggregating 64 KiB windows, one-tx deletes, the complete single-snapshot list, the 42P01 read-only virgin-store split, and the crate-internal reap_orphans (scope-noted, planted-orphan contract test). Posture deltas in the module doc (catalog space never returned / VACUUM FULL runbook line / autovacuum inherited / the cached-get capacity facts). Nothing of EngineFleetState; the window family remains engine-pg-lo-window. - Shared helpers (pg_err, 42P01, quote_identifier, size clamp, i64-nanos encoding, build_pool, PgOpts, test janitors) move to src/pg_common.rs gated cfg(any(postgres, pg-lo)); PgOpts's public re-export widens (definition moved; crate::postgres::PgOpts path held by re-export); src/postgres.rs's changes are mechanical swaps. - LO isolation (C7 lesson): per-test dedicated databases + DatabaseJanitor (WITH (FORCE)); zero LO/schema/database residue on the shared harness db (pre-existing public.lo_entries exploration residue found and removed). - 18 in-module tests, env-driven (ALKBLOBS_PG_*), skipping cleanly server-less; all green against the pglo-poc harness server. Verified: cargo test (69/19/3), --features pg-lo live (87/19/3, 18/18 pg-lo), --features postgres live (103/36/3), --all-features (176/71/4); clippy -D warnings green for default, pg-lo, postgres, all-features, --no-default-features --features "pg-lo,mem" and "postgres,pg-lo"; cargo fmt --check; cargo doc --no-deps. |
||
|
|
5a217f7bc7 |
docs(tasks): decompose wave 5 — the pg-lo large engine, the window host's large-tier arm, the LO-medium SKIP-posture column
- engine-pg-lo: the Backend impl behind feature pg-lo (default-off) — companion-table authority, one-tx staged put at the 512 KiB knee, descriptorless lo_get windows, the orphan-recovery sweep, the posture deltas recorded; the shared-helper/PgOpts/LO-isolation design points named (LOs are database-level — the C7 lesson cited so it is not rediscovered) - engine-pg-lo-window: the EngineWindowState impl per ADR-012 §5's pg-lo row — gc_window only, no fleet family anywhere - store-pg-lo-construction: LargeEngine::PgLo slotting (the large sub-enum design point), the consolidation + mem-pglo constructors (the fleet-valid set completed, implicitly non-fleet), and the one-window-host rule's store-core completion (dormancy + the large-host sweep arm) - suite-column-pg-lo: the second SKIP-posture column, two factories spanning feature pairs, the dormancy row, zero LO residue in teardown - review-wave-5: the engine-wave gate; amendment A2 (janitor adoption) verified here Harness server cited in every server-touching task: dockerized pglo-poc (postgres:16-alpine) on :15432, db blobs, postgres/poc (verified live 2026-10-12); env-driven ALKBLOBS_PG_* settings, clean server-less skips, acceptance requires green against the server. Verification: cargo test 69/19/3 ok, clippy --all-targets -D warnings green, fmt --check green (docs-only change). |
||
|
|
f3e8a88e2e | docs(review): the wave-4 review gate — every gate check green | ||
|
|
f195759989 |
fix(postgres): the wave-4 review gate's inline findings
- PG-1: sweep_lock_acquire reaps a lease whose held session died (in-instance session-loss reaping, ADR-013 §2) — previously the mutex answered Ok(false) until an explicit release; new test terminates the lease's own backend via pg_terminate_backend and re-acquires without a release - PG-3: the engine-level pg tests now drop their per-test schemas (test_support::SchemaJanitor, the Harness deref-guard wrapper in src/postgres.rs's tests, fresh_opts carrying a janitor in the construction tests) — 380 leaked abt_* schemas on the shared harness db are 0 after every run now - PG-2: PgJointTx's doc records the mid-flight-drop hazard (deadpool Fast recycle never resets transaction state; the clause bodies' failure paths all ROLLBACK — the store core's joint-tx driver owes pin_commit on every path from begin_joint_tx; wave 6 amendment) Verification: cargo test (69/19/3), --features mem (124/54/4), --features postgres vs server (103/36/3, 51 clean skips server-less), --all-features vs server (158/71/4), pg-pure (76/19/3); clippy -D warnings green (default/postgres/all-features/pg-pure); fmt --check green; shared-db schema residue 0; cargo doc clean |
||
|
|
0fb05fce3a |
feat(tests): the conformance suite's postgres column — the first SKIP-posture column green (wave 4)
- The column (tests/conformance/postgres_column.rs, feature `postgres`, default-off): the factories are the alkstore pg-factory pattern reused — `dual-tier postgres+local` (kv = postgres + large = local at the column-declared threshold 1024, window delay `Duration::ZERO`) and `kv-only(postgres)` at 1024 (the rejection row's host); a fresh engine-owned schema per `open()` (unique per call, seeded per tag/pid/time — racing opens included), registry-tracked per instance, teardown `DROP SCHEMA IF EXISTS … CASCADE` over exactly the minted schemas + the vended temp roots (idempotent, owned, panicking-row-safe) - The SKIP posture: connection settings ride the environment (ALKBLOBS_PG_HOST/PORT/USER/PASSWORD/DB), every runner gates behind a reachability probe, server-less runs skip cleanly (a skip is not a failure); the acceptance run is green against the harness server (dockerized `pglo-poc`, :15432 — the alkstore harness's same server) - Rows: the form-independent reference rows mirror verbatim (INV-1, INV-6, the NoLivenessSources/ProtectFailed abort shapes, the kv-only rejection row); the sqlite column's durable-schedule sweep family mirrors (schedule-relative INV-2/INV-3 readings, the two-sweep exact counts over the full protection set, the race across the window delay, the holder-stages-only lease shape, INV-5 list correctness); the dispatch rows adapt the placement oracle by medium (independent- connection counts of the schema's rows vs the root's committed files) plus the virgin-store row; every row ADR-stamped, traced to the backlog - Structural factory tests: fresh isolated opens (two opens mint distinct schemas and share nothing) and teardown against a panicking row (schemas really dropped, pg_namespace-verified) - README amended: the postgres rows table in §Columns beyond the reference set; the column plan's SKIP-posture note names the landed column; current state (four columns, waves 2–4) Verification: cargo test (default) 69/19/3 ok — the column compiles out; cargo test --features postgres 102/36/3 ok against the harness server (and green skipping server-less); cargo clippy --all-targets -- -D warnings green (default, postgres, all-features); cargo fmt --check green; pg_namespace verified schema-clean after live runs. |
||
|
|
67b927b13c |
feat(store): the postgres constructor wiring — the fleet-capable store shapes (wave 4)
- `KvEngine::Postgres` slotted in: all six kv-op dispatch arms, the `KvCursor::Postgres` materializing body behind `AnyCursor`'s kv arm (the recorded sub-enum design point, landed as predicted), and the engine-neutral `ensure_gc_schema` call creating `gc_pins` + `gc_window` in the fresh schema at construction (the fleet host's both-tables row, ADR-012 §5) - The two named constructors behind feature `postgres` (default-off): `dual_tier_postgres_local(config, opts, root, threshold, window_delay)` — the fleet-capable-kv production shape — and `kv_only_postgres(config, opts, threshold, window_delay)`; the DSN connection settings + engine opts ride the constructor parameters, never invented defaults. `PgOpts` is pub and re-exported from `lib.rs` (it is on the public constructor signature now) - The sweep's durable form runs on the postgres host: the form body extracted into `sweep_window_durable<W: EngineWindowState>` (sqlite's arm delegates, behavior unchanged), both SQL-backed arms keyed off the kv-engine enum, never a flag — wave 5's pg-lo dormancy slots in without reshaping - Non-fleet posture recorded on both constructors (ADR-012 §3): implicitly `fleet: false`, the ADR-005 in-process protocol verbatim, the `fleet: true` declaration is wave 6 and slots in without reshaping - Stale scope notes updated to the landed state (engine-representation doc, `KvCursor` design point, fleet-declaration paragraph, one-window-host paragraph, postgres module doc, gc.rs companion-seam note); the batch-delete helper's scope note stays (wave 6 consumer) - 5 new env-driven construction tests (skipping server-less): the modes construct and introspect, both GC tables exist in the fresh schema, the window-delay default is the pinned 5-min constant and tunable (`0` legal), the kv-only rejection wiring is unchanged, the dual-tier shape round-trips through both tiers, and the durable form is observed staging/executing across sweeps on the postgres host Verification: `cargo test` 69/19/3 ok; `cargo test --features postgres` 102/19/3 ok live (pglo-poc harness) and green server-less; `cargo test --no-default-features --features postgres` (the pg-pure build, ADR-013 §4) 75/2/3 ok server-less; `cargo test --all-features` 157/54/4 ok; clippy `-D warnings` green (default, postgres, pg-pure, all-features); `cargo fmt --check` green; `cargo doc --no-deps` clean. |
||
|
|
29209fe53f |
feat(gc): the postgres engine-state clause families — the fleet host's full seam (wave 4)
- EngineWindowState: the both-tables ensure_gc_schema (gc_pins with the (key, owner) identity + gc_window), idempotent window_stage (the upsert discipline per (key, deadline)), and window_take as one atomic DELETE ... RETURNING with the <= boundary - EngineFleetState: PgJointTx (the pooled checkout carrying a session-level BEGIN), joint_put (the CAS clause inside the held tx), pin_commit (pin-row upsert-renewals + commit, rollback-on-failure => zero residue), pin_renew (expiry = now + DEFAULT_PIN_TTL, never resurrects), pin_check (the two-variant classification), arbitrate (the FOR UPDATE finger-lock folding pins + registered liveness-table references), and the engine-side register_liveness_source home - The sweep lease: the session-scoped pg advisory lock with the namespaced FNV lock key and the same-connection acquire/release holding discipline (the lease's checkout stored until release) - 13 contract tests (28 postgres tests total), env-driven, skipping cleanly server-less; verified green against the live pglo-poc harness server; clippy/fmt green default + --features postgres Verification: cargo test (69/19/3 ok), cargo test --features postgres server-less (97 ok, 28 skips) and live (97 ok), cargo test --all-features (152/54/4 ok), cargo clippy -D warnings (both configs), cargo fmt --check, cargo doc --no-deps. |
||
|
|
1efc46f953 |
feat(engines): the postgres kv engine — the fleet host's first engine (wave 4)
The 'postgres' kv-tier engine (ADR-007, feature 'postgres', default-off) against the wave-1 trait: the sqlite engine's structural twin on a Pooled-Postgres driver cluster. - One engine-owned schema (default 'alkblobs'; quoted-identifier boundary via the alkstore quote_identifier pattern) holding the B5 row shape — key bytea PK / value bytea NOT NULL / size bigint NOT NULL, WITH (fillfactor = 90) — idempotently bootstrapped at construction; autovacuum and the ~40x small-tier storage overhead recorded as posture, not silence - deadpool pool with DEFAULT_MAX_SIZE = 8 (load-bearing, POC #5 B2); per-connection prepare_cached discipline (B5: statements die with sessions); the synchronous_commit parity knob as connect options on every pooled connection (default 'on', configurable, alkstore src/opts.rs mechanics) - CAS first-writer-wins (INSERT ... ON CONFLICT DO NOTHING) on both put forms; metadata-only size probe riding the PK index (EXPLAIN gate-tested); EXISTS has; materializing get cursor; degenerate in-memory stage with both hygiene forms (fail_commit injection) - list: the single-statement MVCC-snapshot cursor (portal-batched row stream, keys only); crate-internal batch-delete helper (DELETE ... WHERE key = ANY() over bytea[]), scope-noted until wave 6; virgin-store read-only split via SQLSTATE 42P01 mapping - 15 in-module unit tests, env-driven (ALKBLOBS_PG_*), skipping cleanly server-less; green against the dockerized pglo-poc harness server (:15432) Verified: cargo test; cargo test --features postgres (84 lib tests); cargo test --all-features (139); clippy all-targets, default + features + all-features, -D warnings; fmt --check; doc --no-deps. |
||
|
|
7ea6dd6bcb |
docs(tasks): decompose wave 4 — the postgres kv engine, the fleet host's full seam, the first SKIP-posture column
Five tasks against the wave-3 contract surface as reviewed (no amendments from the gate): - engine-postgres: the fleet-capable kv engine — the B5 row shape (fillfactor 90) in one engine-owned schema, the deadpool pool with per-connection prepared-statement discipline, the synchronous_commit parity knob wired per-connection, ON CONFLICT DO NOTHING CAS, the stable MVCC-snapshot list cursor, the batch-delete helper (key = ANY($1), consumer wave 6), the read-only virgin-store split - engine-postgres-fleet: the engine-state seam's full clause families (ADR-012 §5's postgres row — every family yes): gc_pins + gc_window, the joint tx ownership rule in the JointTx shape, pin_commit atomicity, pin_renew/pin_check/arbitrate, the pg advisory lock as the sweep lease's shipped provider; fleet activation stays wave 6 - store-postgres-construction: KvEngine::Postgres slots in, the KvCursor arm, the dual-tier/kv-only constructors (DSN + opts + threshold + window delay), the non-fleet posture recorded, the pg-pure build verified - suite-column-postgres: the first SKIP-posture column — the alkstore pg-factory pattern reused (fresh schema per open, CASCADE teardown, env-driven ALKBLOBS_PG_* settings), durable-schedule sweep rows, acceptance green against the harness server (pglo-poc :15432, verified live) - review-wave-4: the engine-wave gate (engine-vs-contract conformance) Verification: dependency graph validated (all ids resolve, no cycles, topological order clean); docs-only change. Refs: docs/plans/implementation.md §Wave 4 |
||
|
|
611d30a3c4 |
fix(store): rustdoc hygiene + u64-safe cursor clamping
- resolve or unlink 25 preexisting rustdoc warnings: broken intra-doc links (error variants), public-docs-linking-private-items (crate-internal cursor/registry names), and one stale reference to a `Store::window_host` that never existed (the form branch is `sweep_window`'s engine-enum match) - `clamped` runs its arithmetic in u64 and narrows to usize after clamping, so a wide u64 offset can never wrap onto a small slice position on narrow-pointer targets; the cursor contract tests pin the overflowing-offset and overflowing-length forms - zero rustdoc warnings under RUSTDOCFLAGS=-D warnings Verification: cargo test (69+19+3 green), cargo test --all-features (124+54+4 green), cargo clippy --all-targets -- -D warnings green, cargo fmt --check green, RUSTDOCFLAGS=-D warnings cargo doc --no-deps green. |
||
|
|
97cdf682c4 |
docs(review): the wave-3 review gate — every gate check green
Reviewed the wave-3 commits (engine-sqlite, engine-sqlite-window, store-sqlite-construction, store-sweep-durable-window, suite-column-sqlite) against the cited ADR/spec text line by line: ADR-003/007/008 §2, ADR-010 §2, ADR-012 §1/§5/§6.7, store-api.md, implementation.md §Review gates — the engine-vs-contract conformance (suite column green server-less in the default build; the window family contract-tested per its applicability row; virgin-store semantics incl. the no-tables-yet form; the size metadata-only discipline proven index-riding), the durability posture (WAL + synchronous=NORMAL verified at open; the single-writer ceiling posture, not hedge), the durable sweep form vs ADR-008 §2/ADR-012 §6.7 (stage at mark_end + window; next-sweep execution with raced execution-time re-arbitration; schedule-relative exact counts; aborts stage and execute nothing), the seam reduction (gc_window only; no pin rows, no sweep-lock body, no EngineFleetState), and the blocking discipline (all rusqlite in spawn_blocking). Named design points audited as resolved; family conventions clean; no wave-4 amendments arise. Inline fixes (review-of-record): - src/gc.rs: the seam's window_stage doc retired the wave-1 lazy-create hedge for the shipped constructor-at-open posture (a tableless database surfaces the engine error on stage; per-engine tolerances are each engine's recorded posture) - tasks/engine-sqlite-window, store-sweep-durable-window, suite-column-sqlite: stray placeholder lines above the filled Notes/Summary removed (the wave-2 gate's same cleanup) - tests/conformance/README.md: trailing newline added Verification (all re-run green after the fixes): - cargo test (default): 69 lib + 19 conformance + 3 git-interop - cargo test --features mem / --all-features: 124 lib + 54 conformance + 4 git-interop each - cargo clippy --all-targets -- -D warnings (default + all-features) - cargo fmt --check - cargo doc --no-deps at the pre-existing warning baseline (26) Wave 4 (postgres) decomposition is unblocked. |
||
|
|
3f8e8158a7 |
feat(tests): the conformance suite's sqlite column — the durable-schedule SQL-backed column green (wave 3)
- The first SQL-backed column: the default-build production shape (kv = sqlite + large = local, feature sqlite default-on) runs the form-independent reference rows verbatim (INV-1, INV-6, the NoLivenessSources/ProtectFailed abort shapes, the kv-only rejection row against kv-only(sqlite)) and grows the durable-schedule sweep rows — exact counts asserted relative to the staged schedule (ADR-012 §6.7): two-sweep counts over the full protection set, the race across the window delay (a re-put landing during the window survives the executing sweep), the holder-stages-only lease shape, INV-5 through durable-schedule outcomes, and the schedule-relative INV-2/INV-3 readings - Dispatch rows mirror the local column's family with the placement oracle adapted by medium: the sqlite file's pool/window rows vs the root's committed files; virgin-store reads as no-ops on a fresh file (the no-tables-yet tolerance engine-instrumented, this row its facade form) - Factories: dual-tier sqlite+local and kv-only(sqlite), instance- tracked temp sites (fresh sqlite file + private root per open; idempotent panicking-row-safe teardown), the column-tuned threshold and the test window-delay tuning (Duration::ZERO — execute-on-next- sweep); the module is cfg(feature = "sqlite"), default-on — the column runs in every default cargo test, server-less, never skips - Shared fixtures moved to rows.rs (committed_file_count, staging_file_count, slice_digest, LatchProtect pub(crate)) so the columns share them without the mem gate - README: the schedule-relative reading for SQL-backed columns recorded (citing ADR-012 §6.7), the sqlite rows table inventory, Current state (three columns) Verification: cargo test (default) green — 69 lib + 19 conformance + 3 git-interop; cargo test --features mem green (124 lib + 54 conformance); cargo check --all-features --tests green; cargo clippy --all-targets -- -D warnings green; cargo fmt --check green. |
||
|
|
fb8326c4d5 |
feat(store): the sweep's durable form — staged candidates, next-sweep execution
The first durable delete window (single-node), beside the inline form: - sweep_window branches on the delete window's host — the kv-engine enum (KvEngine::Sqlite -> durable; KvEngine::Mem -> inline body, extracted verbatim so the wave-2 mem/local suites stay green unchanged) - the durable form, per sweep under the lease and after the shared protect consult: window_take(now) executes the overdue set with execution-time re-arbitration through the same arbitrated_delete path (consult_protect = false — the sweep's protect consult covers its execution phase; vanished candidates settle into the cancelled count), then marks unchanged, then stages the candidate-dead set at visible_after = mark_end + window (the constructor-tunable delay; ZERO = execute-on-next-sweep) - report counts exact relative to the schedule (staged-this-sweep vs executed-this-sweep); aborts execute nothing, stage nothing, delete nothing - unit tests: two-sweep exact-count shapes at delay 0, future-delay staging with idempotent re-stage, the race across the delay (put during the window survives; the unpinned twin deletes), the vanished-candidate form, and the three abort shapes on sqlite Verification: cargo test (default 69 lib tests), --all-features (124), --no-default-features --features mem (101), clippy -D warnings (default and --all-features), cargo fmt --check — all green. |
||
|
|
2993df0e36 |
feat(store): the sqlite constructor wiring — the default-build store shapes
- KvEngine::Sqlite slots in; all six kv ops dispatch; the kv cursor body dispatches through a new KvCursor sub-enum behind AnyCursor's one kv arm (the kv-arm-over-multiple-kv-engines design point, recorded) - dual_tier_sqlite_local (kv = sqlite + large = local, the default-build production shape) and kv_only_sqlite constructors — both feature-gated on sqlite (default-on), both taking the threshold and the delete-window delay, fallible where I/O is fallible (Err(Error::Io) per the dual_tier_mem_local pattern) - ensure_gc_schema runs at construction via the engine-neutral KvEngine call (sqlite's body creates gc_window only); the window-delay shipped default is the pinned DEFAULT_DELETE_WINDOW_DELAY (5 min, ADR-008 §2), constructor- tunable, Duration::ZERO legal — the store carries the value for the sweep's durable form to consume - the one-window-host posture recorded in the module doc (the durable window hosts on the kv engine when it is SQL-backed; wave 5's pg-lo dormancy slots in without reshaping) - stale empty-set scaffolding updated to the landed state: the #[allow(unused_variables)] attrs off the KvEngine methods; the scope notes in store.rs, backend.rs, sqlite.rs, gc.rs - constructor unit tests: mode/threshold/window-delay introspection, the GC schema observed in the fresh database (window table present, gc_pins never), kv-only rejection wiring unchanged, and the dual-tier shape round-tripping end-to-end through both tiers Verification: cargo test 62 passed (default; 5 new tests), cargo test --all-features 117, cargo test --features mem 117, --no-default- features --features mem 97; cargo clippy --all-targets -- -D warnings clean (default + all-features); cargo fmt --check clean; cargo doc --no-deps at the pre-existing warning baseline. |
||
|
|
51d8f6525f |
feat(gc): the sqlite engine-state window family — the durable single-node delete window
- impl EngineWindowState for SqliteBackend, the window family only per ADR-012 §5's sqlite row; no pin-clause or sweep-lock body exists — EngineFleetState is never implemented on this engine - ensure_gc_schema creates gc_window only (never gc_pins; the embedder's liveness table is never created — ADR-008 schema ownership) - window_stage is a key-keyed upsert: idempotent per (key, deadline), a re-stage at a new deadline updates the row - window_take is one atomic DELETE … RETURNING with the <= now boundary; future rows left in place; tableless take is a no-op read, tableless stage surfaces the engine error (the recorded read/write tolerance split) - visible_after encodes as signed i64 nanoseconds from the epoch (recorded design point; sub-second precision for test tunings) - six clause contract tests per the ADR-012 §5 sqlite row Verification: cargo test green (58 lib + 5 integration); cargo clippy --all-targets -- -D warnings green; cargo fmt --check green. |
||
|
|
908fb5d2dd |
feat(engines): the sqlite kv engine — the default engine, first SQL-backed (wave 3)
Implements the sqlite kv-tier engine per ADR-003/007/010 §1 behind feature `sqlite` (default-on): the B5 row shape (key BLOB PK / value BLOB / size INTEGER NOT NULL), INSERT OR IGNORE CAS first-writer-wins on both put forms, the EXISTS has probe, the materializing cursor body with len() from the size column, the degenerate in-memory stage with both hygiene forms, the indexed-column size probe (plan-tested), the no-tables-yet virgin-store read tolerance (tested against a tableless database file), WAL + synchronous=NORMAL verified at open, prepare_cached discipline, and every rusqlite operation in spawn_blocking under the recorded single-connection posture. Task: tasks/engine-sqlite.md (status: completed). The engine-state window family and store construction remain in their own tasks. Verification: - cargo test — 58 pass (11 new sqlite in-module tests) - cargo clippy --all-targets -- -D warnings — clean - cargo fmt --check — clean - cargo check --no-default-features — green |
||
|
|
758ccfb359 |
docs(tasks): decompose wave 3 — the sqlite kv engine, the durable delete window, the first SQL-backed column
Six tasks against the wave-2 contract surface as reviewed (no amendments from the gate): - engine-sqlite: the default kv engine — WAL + synchronous=NORMAL, prepared statements, the indexed size column probe (B5's row shape), has-as-EXISTS, virgin-store no-tables-yet tolerance, spawn_blocking discipline - engine-sqlite-window: the engine-state seam's window family only (ADR-012 §5's reduction — gc_window, stage/take; no pin rows, no sweep-lock body) - store-sqlite-construction: KvEngine::Sqlite slots in, the dual-tier/kv-only constructors with the constructor-tunable window delay (default 5 min), ensure_gc_schema at construction, the one-window-host posture - store-sweep-durable-window: the sweep's SQL-backed form — overdue rows execute with re-arbitration before/alongside marking, candidates stage at mark_end + window, counts exact relative to the schedule (ADR-012 §6.7); the inline form preserved verbatim - suite-column-sqlite: the first SQL-backed column, server-less — form-independent reference rows mirror directly; the sweep rows are schedule-relative (the recorded INV-4/5 mirror amendment) - review-wave-3: the engine-wave gate (engine-vs-contract conformance) Verification: dependency graph validated (all ids resolve, no cycles, topological order clean); docs-only change. Refs: docs/plans/implementation.md §Wave 3 |
||
|
|
79cf1f0220 |
docs(review): the wave-2 review gate — every gate check green
Read the wave-2 implementation against the pinned ADR text line by line (ADR-003/005/006/010/011/012/013 §2, store-api.md): the three numbered unknown-length rules each test-backed, the delete-window ordering invariants (arbitrate-at-delete-time, pin-before-publish on both put paths and both tiers, raced conversion ordering, delete-then-recover), abort-as-data with all three causes, dispatch purity + fall-through + constructor modes + kv-only's two put-time rejection points, stage-hygiene exact counts on both engines (incl. the Drop-cancellation story), both suite columns green/ADR-stamped/ traceable with the factory binding recorded, the dispatch-coverage shape exercised, all eight named design points recorded and consistent, and the staged-put key-at-commit amendment audited accepted. No wave-3 amendments arise. Inline fixes applied by the gate: - facade.rs: stale Batch::add dead-code allow + stale task-pointer comment removed (put_in_batch's call sites landed) - dispatch.rs: module-level allow(dead_code) removed (route/Tier now driven; clippy clean without it), scope note updated - backend.rs: the allow kept but its scope note corrected to what is actually dead (trait name, kv put until wave 3) - mem.rs: stale constructor doc line updated to the landed state - tasks/gc-protocol.md: stray placeholder lines removed Verification: cargo test (42 lib + 2 conformance + 3 git-interop), cargo test --features mem and --all-features (97 lib + 37 conformance + 4 git-interop), clippy --all-targets and --all-features --all-targets (-D warnings), fmt --check — all green post-fix. Wave 3 decomposition unblocked. |
||
|
|
45c456989f |
feat(tests): the conformance suite's local column — the dual-tier dispatch-coverage shape green (wave 2)
- tests/conformance/local_column.rs: the first dual-tier column, run against the kv = mem + large = local factory (ADR-012 §6.3) — fresh temp-root store per open, per-instance tracked roots, idempotent teardown safe against abandoned/panicking rows (structural tests included); never skips (mem-gated — the dual-tier constructor rides feature mem until wave 3 provides an ungated kv engine) - the reference INV row set (rows.rs) mirrored against the dual-tier factory at a column-declared tuned threshold (1024), so the shared matrix and race fixtures exercise both tiers' bodies (INV-7) - dispatch rows grown: pure-function routing across put paths and re-puts with an on-disk placement oracle (tier residency is facade-invisible; with kv = mem the medium is the oracle), threshold boundary legs at the tuned and shipped-default thresholds with the constructor-tuned re-route leg, and fall-through per op with both-tier miss shapes (ADR-003/ADR-012 §6.1/§6.3/§6.5) - local-specific rows: on-disk stage-hygiene exact counts over both put paths (the real stage form, INV-3), pread windows over committed files with slice digests verified independently (ADR-006/ADR-010), virgin-store no-ops on a fresh root, and exact sweep counts over a both-tiers pool (INV-4/INV-5's both-tiers form) - rows.rs shared fixtures made pub(crate); README records the column's row inventory (§Columns beyond the reference set), the declared- threshold posture, and the new current state - stat-as-stat rides the mirrored INV-6 row's over-threshold legs; the Drop-cancellation hygiene form stays per-engine (recorded) Verification: cargo test (default) green; cargo test --features mem green (97 lib + 37 conformance + 4 git-interop); cargo test --all-features green; clippy --all-targets -D warnings green in both feature shapes (default + all-features); cargo fmt --check green; cargo doc --no-deps shows only the pre-existing warning class |
||
|
|
f86e20a217 |
feat(tests): the conformance suite's mem column — the reference row set green (wave 2)
- rows.rs: the reference row set (INV-1…6 + two constructor-mode rows), factory-generic and engine-uniform, every row ADR-stamped and traced to the backlog: round-trips over the content matrix on both put paths, dedup (same/cross-path), stage hygiene (both paths, both injectable failure modes, kv-degenerate residue via sweep outcomes), sweep safety (full-protection-set happy shape, all three GcAbortCause abort shapes incl. the single-flight SweeperLock lease, the sweep-vs-pinned-put race), list correctness through exact sweep outcomes only, stat discipline, mem-only posture, and kv-only put-time rejection (threshold discovered by free probes; immediate/ mid-stream rejections and at-threshold-lands legs) - mem_column.rs: the column's factories — mem-only (reference host) and kv-only(mem) at a declared threshold — fresh isolated opens, no-op idempotent teardown; structural factory-contract tests cover isolation and teardown against a panicking row - README: the reference row set recorded (INV-7's basis — what other columns mirror), the row-lifecycle convention, column plan and current state updated - task suite-column-mem: status completed, notes and summary filled Verification: cargo test --features mem (97 lib + 18 conformance + 4 git-interop), cargo test (default), cargo test --all-features, clippy --all-features --all-targets -D warnings, fmt --check, and cargo doc --no-deps — all green |
||
|
|
539dabcbf7 |
feat(store): the put seam — two internal paths, the three numbered rules, Batch, kv-only rejection
- `put(len: Option<u64>, stream: impl AsyncRead + Unpin + Send) -> (Key, Pin)`: known-length one-pass flow; unknown-length buffer-to-threshold flow with the mid-stream fresh-stage flush (ADR-003 option (d), three numbered rules: flush-and-continue, derivation over the eventual stored artifact as a whole via buffer form and StagingCursor read-back form, zero residue on stream error) - staged-put seam amended (operator sign-off): `put_staged()` keyless handout, `StagingCursor::commit(self, key)` publishes under the store-core-resolved key — the canonical key completes at stream end and the wave-1 handout shape could not express the commit-rename flow - pin-before-publish rides the arbitration lock on both paths and both tiers; dedup re-puts refcount the existing entry - Batch seam: `Store::batch()` + `put_in_batch()`, members publish pin- before-publish, group pins drop together on `Batch::commit` (new public verb) or drop - kv-only rejection (ADR-012 §6.2): known-length over-threshold rejects immediately (stream never read), unknown-length at mid-stream overflow; zero residue; error carrier `Io(String)` (named design point) - declared-len mismatch rejects `Verification` zero-residue (named design point); stream shape generic-no-dyn (named design point) - incremental BlobHasher (pinned byte-equal to Algorithm::derive); AnyStaging cursor dispatch; git-interop put-path leg against git hash-object; eleven put-path behavior tests Verification: cargo test (default/--features mem/--all-features) green; clippy --all-targets -D warnings green in all three feature shapes; cargo fmt --check green; cargo doc --no-deps at the 24-warning baseline. |
||
|
|
889be4cada |
feat(gc): the in-process GC protocol — pins, liveness, delete windows, sweep, direct delete
The gc-protocol task (ADR-005; abort-as-data per ADR-012 §1; non-SQL inline window staging per ADR-012 §5) wired on the Store facade: - src/gc.rs — GcHome + RegistryCore: one arbitration lock over per-key pin refcounts, in-flight delete markers, and the registered source lists; async pin minting (acquire) that blocks on in-flight deletes (ADR-005's re-put case (a) delete-then-recover without engine I/O under the lock); the ordered-step convert primitive (pin release + registration in one section); batch one-step release; the single-node sweep lease (the in-process analog of ADR-013 §2). - src/facade.rs — Pin/Batch carry the registry back-reference and own their drop release (batch drop decrements every member in one ordered step); new public vocabulary LivenessSource, ProtectCallback, Registration, re-exported from lib.rs. - src/store.rs — sweep() (single-flight → zero-source safe default → protect consult → mark over both tiers' list() union → arbitrate at delete time → ~100/batch commit → exact-count report or abort-as-data), direct delete() (existence probe → full protection set per ADR-012 §6.4 → GcRefuse/Missing/delete through the fall-through), and the registration/unregistration verbs. - 16 new tests + the facade pin/batch shape test reworked on the wired machinery: exact counts across both tiers, abort shapes, dedup refcounts, batch drops, live-shared sources, the conversion ordered step sequentially and raced, sweep-vs-pinned-put (64 rounds, every schedule, byte-identical delete-then-recover), publish-after-mark, no-torn reads under a window, single-flight contention, and malformed-list sweeps as operational errors. - tasks/gc-protocol.md — status completed; named design points recorded in Notes. Verification: cargo test (default) 46 passed; --features mem and --all-features 89 passed each; cargo clippy --all-targets -- -D warnings (default and --all-features) clean; cargo fmt --check clean; cargo doc --no-deps at the pre-existing baseline (no new warnings). |
||
|
|
f1f505a910 |
feat(store): the read paths — get/stat/read_range/has/list on the fall-through rule
- Public `(len, stream)` shape: `Store::get` returns
`Option<(u64, Blob)>`; `Blob` (src/facade.rs) is a bounded, re-entrant
windowed reader over the new crate-internal `AnyCursor` tier dispatch
(mem materializing / local pread body families) — engine cursors
never leak onto the surface; no chunk-iterator machinery.
- Verification-on-get pinned against ADR-006: kv-tier materialized
reads whole-blob hash-check at get time (mismatch = Err(Verification),
never a further fall-through); the large tier's streaming open is
metadata + handle only (put-path verification + slice digests are its
story); `read_range` carries slice digests only, on both tiers.
- `stat` rides the trait's `size` probe (never a content load) into
`EntryMeta { len, algorithm }`; `has` is the pure existence probe;
both fall through kv -> large like every key-addressed op (ADR-012
§6.1) — consult order, not routing (never consults `tier_for`).
- Miss shapes enforced (the error module's behavioral note): both-tier
misses are Ok(None) on get/stat, false on has, Err(Missing) on
read_range; `src/error.rs` wave-2 deferral notes updated to enforced
state.
- `list` decided Vec<Key>: materialized, sorted total-order union of
both tiers (the sweep consumes the engines' enumeration directly);
malformed pool bytes are fatal `Err(KeyInvalid)`, never a silent
skip.
- `KeyInvalid` at the facade boundary: ops take opaque key bytes and
convert via `Key::from_bytes` once (`boundary_key`); engines never
see typed keys.
- Twelve read-path tests: virgin-store shapes per mode (dual-tier,
mem-only, kv-only), the `(len, stream)` windows, fall-through,
kv-first (decoy content in the large tier), both verification
postures, list union/sort/pool-fatality, and malformed-key
rejection on every op.
Task: tasks/store-read-paths.md (design points recorded in Notes for
the wave-2 review gate).
Verification: cargo test (default), cargo test --features mem, cargo
test --all-features green; cargo clippy --all-targets -- -D warnings
green in all three feature shapes; cargo fmt --check green; cargo doc
--no-deps clean beyond the pre-existing private-intra-doc-links class.
|
||
|
|
532adfa707 |
feat(store): the Store facade — constructor modes, size-threshold dispatch, the factory binding
The wave-2 facade skeleton and routing (tasks/store-construction.md):
the public surface consumers program against, with the ops methods
still to land against it in store-read-paths/gc-protocol/store-put-paths.
- src/store.rs — the public `Store` type (engine-blind, ADR-007/008/
010) over crate-internal tier enums (`KvEngine`{mem}, `LargeEngine`{
local}): the engine-representation design point decided as concrete
+ enum dispatch, generic rejected (pub(crate) engines, no dyn);
the three constructor modes as named constructors — `mem_only`,
`kv_only_mem`, `dual_tier_mem_local` (the ADR-012 §6.3
dispatch-coverage shape) — with `StoreMode` introspection; the
write-side routing seam (`tier_for`), the read-side fall-through
order (`kv()` then `large()`, ADR-012 §6.1), and the `GcHome`
GC-state field; the ADR-012 §3 fleet declaration ruled deferred to
the engine waves (nothing fleet-valid is constructible yet)
- src/dispatch.rs — the pure routing function (`route`, at-or-below
→ kv), `DEFAULT_DISPATCH_THRESHOLD` (128 KiB) exported at the
crate root, boundary-value unit tests (exactly-at-threshold,
degenerate tunings, purity)
- src/gc.rs — `GcHome`: the store-instance GC-state home the
gc-protocol task fills (in-process; fleet state stays on the
engine-state seam per ADR-010 §2/ADR-012 §4)
- tests/conformance — the wave-2 binding made as anticipated: the
factory's `Store` associated type collapsed into the concrete
`alkblobs::Store` (`open` vends it directly), recorded in
factory.rs + README; the wave-1 mock probe retired, replaced by
mem-gated binding demo factories (binding.rs) over both
constructible shapes
Verification: `cargo test` (default), `cargo test --features mem`,
`cargo test --all-features` green (41/53/53 unit + conformance
targets green in all three shapes); `cargo clippy --all-targets
-- -D warnings` green in all three feature shapes; `cargo fmt
--check` green; `cargo doc --no-deps` adds no warnings beyond the
pre-existing private-intra-doc-links class.
|
||
|
|
856d436f81 |
feat(engines): the local large-tier engine — sharded files, stage-then-commit-rename, pread cursors
- LocalBackend: flat {hex-prefix}/{hex-prefix}/{hex} sharding over
lowercase-hex-of-Key::as_bytes filenames (self-describing; the
list-decode round-trip recorded for the review gate); virgin-store
reads are no-ops
- staged put: unique stage files under {root}/staging/, commit-rename
is the publish, CAS first-writer-wins, every failure path (discard,
failed commit, Drop cancellation) converges on zero residue
- read cursor: pread loop over the immutable committed file, EOF
clamping, provided read_range composing the slice-digest convention
- all file I/O in spawn_blocking; size is a stat, never a content load
- list walks the committed tree and is fatal (Error::Io, path named)
on anything that does not decode — complete and well-formed
- 11 in-module unit tests incl. exact-count stage hygiene over all
failure forms and the success form
Verification: cargo test (44 green), clippy --all-targets/--all-features
-D warnings, fmt --check — all clean
Task: tasks/engine-local.md
|
||
|
|
0b43ea1449 |
feat(engines): the mem contract-reference engine — first real engine (wave 2)
- src/mem.rs: MemBackend implements Backend (seven methods + put_staged, name "mem") over Arc<parking_lot::Mutex<BTreeMap<..>>>; CAS first-writer-wins puts, materializing MemCursor body, degenerate in-memory MemStaging whose commit publishes via the same CAS clause and self-discards on failure; discard leaves zero residue. - CAS re-put is dedup for both put forms; reads are no-ops on the virgin store; delete carries Missing; size is metadata-only. - Eight in-module tests mirror the wave-1 double's assertions: method contract, list well-formedness, cursor body read contract + ADR-006 range digest, empty-entry round-trip, staged happy path, and both hygiene forms (fail_commit test hook stands in for media failure). - backend.rs: clamped promoted pub(crate) — the shared EOF-clamping arithmetic both mem cursor bodies compose (no per-engine duplicate). Verification: cargo test --features mem (33+7 green); cargo test (default) green; clippy --all-features --all-targets -D warnings green; fmt --check green. |
||
|
|
6bce301c55 |
docs(tasks): decompose wave 2 — mem + local engines, facade, in-process GC, first suite columns
Nine tasks against the wave-1 contract surface as reviewed (no amendments from the gate): - engine-mem / engine-local (parallel, dependency-free): the two engines; local owns the stage-hygiene exact-count tests and the Drop-cancellation story the wave-1 review recorded - store-construction: the Store type, constructor modes, size-threshold dispatch; binds the conformance factory's Store associated type - store-read-paths ∥ gc-protocol: read ops with the ADR-012 §6.1 fall-through rule; the ADR-005 in-process protocol (pins, liveness, delete windows, abort-as-data, GcRefuse with the full protection set) - store-put-paths: the two put paths behind one signature, ADR-003's three numbered rules, pin-before-publish, Batch, kv-only rejection - suite-column-mem → suite-column-local: the reference column, then the dual-tier dispatch-coverage shape mirroring it - review-wave-2: the gate (facade/dispatch/GC vs ADR-003/005/012) Verification: dependency graph validated (all ids resolve, no cycles, topological order clean); docs-only change. Refs: docs/plans/implementation.md §Wave 2 |
||
|
|
c1d9494c11 |
docs(tasks): the wave-1 review gate — the one-way-door audit green
Every gate check read against the cited ADR text: the key encoding holds byte-exact against ADR-002 (tags, lengths, rejects, sort order, preamble abstraction, live git-CLI interop, lowercase oid policy recorded); the trait + I/O seams + engine-state seam match ADR-003/ 010 and ADR-012 §5's reduced table; the error model's five variants and single-enum altitude reading match store-api.md/ADR-012 §6.5; the feature graph matches ADR-013 §4; all named design points audited consistent; the suite convention and backlog are traceable; family conventions clean. One inline fix: publish keyword `bao` -> `git` (excluded technology named in metadata). Wave 2 decomposition unblocked, no amendments. Verified: cargo test (26 lib + 4 conformance + 3 git-interop passed), cargo clippy --all-targets -- -D warnings, cargo clippy --all-features --all-targets -- -D warnings, cargo fmt --check, cargo check --all-features, cargo check --no-default-features --features pg-lo. |
||
|
|
327b952e4d |
feat(tests): the conformance suite's home — conventions, invariant backlog, harness skeleton (wave 1)
- tests/conformance/README.md: the convention doc — row naming (property- stated names, engine dimension is the column), the Contract stamp: format (the alkstore ADR-017 §4.2 convention adapted), the one- normative-owner-per-property rule, the factory contract (fresh isolated instance per open, idempotent teardown, safe over an abandoned instance), the SKIP posture (SQL-attached columns skip cleanly server-less; acceptance requires green against the server), the invariant backlog (the seven store-api.md §Invariants registered as the rows-to-grow table with pinning citations), and the mem-first column plan (mem is the contract-reference engine, ADR-011 §2) - harness skeleton, compile-able with zero rows: StoreFactory (fresh + isolated + idempotent-teardown contract; the Store associated type is wave 2's binding point for the facade type), stamp.rs (the marker + convention anchor), backlog.rs (in-code registry with a traceability gate asserting INV-1..7 mirror §Invariants in order) - no engine-specific code; no dependencies added Verification: cargo test (33 passed across lib + 3 targets), cargo clippy --all-targets -- -D warnings, cargo fmt --check |
||
|
|
0547c17876 |
feat(backend): the Backend trait, I/O seams, engine-state seam shape, and facade value types (ADR-003/010)
The wave-1 contract surface: everything engine-blind that waves 2-6 implement against. No engine bodies, no facade behavior logic. - src/backend.rs: the pub(crate) Backend trait — seven methods + put_staged, opaque byte keys/values, size metadata-only, list complete-and-well-formed, virgin-store reads as no-ops (ADR-003, ADR-008); the ReadCursor/StagingCursor I/O seams with the read_range slice-digest convention (ADR-006) at one seam; unit tests against both body-family doubles, a test-double engine, and the stage-hygiene invariant's two forms (discard; commit failure self-discards). - src/gc.rs: the engine-state companion seam as EngineWindowState / EngineFleetState — ADR-012 §5's applicability table expressed structurally (no dead machinery); JointTx ownership rule pinned in the signature (store holds the tx; engine contributes mechanics). - src/facade.rs: EntryMeta, Pin, Batch, SweepReport, GcAbortCause (x3, #[non_exhaustive]), PinState with lifecycle contracts (ADR-005, ADR-012 §1); re-exported from lib.rs. - Design points pinned for the wave-1 review gate in tasks/core-trait-and-types.md: pub(crate) trait visibility, associated-type put-form shapes, AFIT + associated types (no dyn). Verification: cargo test (26 lib + 3 git-interop), cargo test --all-features, clippy --all-targets -D warnings (default and --all-features), cargo fmt --check, cargo doc --no-deps — all green. |
||
|
|
fef05b4ec5 |
feat(keys): the content-address layer — canonical hash derivation and key encoding (ADR-002)
Wave 1 task core-hashing-keys: the crate's declared one-way door,
implemented byte-exact to ADR-002.
- src/hashing.rs: Algorithm (exactly GitBlobSha256 canonical /
GitBlobSha1 tolerated; BLAKE3 absent), Digest (known-length
derivation H("blob <len>\0" + content) as primary constructor;
from_git_oid_hex interop constructor, lowercase-only acceptance),
and the hashing::registry constants (0x01/0x02, 0x00 reserved; new
algorithm = new ADR)
- src/keys.rs: typed Key wrapper; as_bytes/from_bytes are the only
backend-boundary conversions; 1 algorithm byte + digest bytes
(33/21 total); rejects-never-guesses validation -> Error::KeyInvalid
- tests/git_interop.rs: the load-bearing test — byte-exact derivation
against the `git hash-object` CLI, both algorithms, small and
75-KiB content; external-oid round-trip into pool keys; both oid
formats coexisting in one flat pool
- named design point (oid hex policy): lowercase-only acceptance,
recorded in the task Notes and on from_git_oid_hex
Verified: cargo test (15 passed), cargo clippy --all-targets -- -D
warnings, cargo fmt --check, cargo test --all-features — all green.
|
||
|
|
fc5ab57d18 |
feat(errors): the one-enum error family with altitude discipline
- src/error.rs: thiserror Error enum with the five ADR-012 \u00a76.5 variants (Missing, Verification, Io(String), GcRefuse, KeyInvalid); doc comments carry store-api.md \u00a7Error model's semantics and the one-enum/two-altitude discipline (trait's effective members are Missing/Verification/Io/KeyInvalid; facade re-exposes with GcRefuse) - no #[non_exhaustive] per the ADR-pinned variant set; Error derives Debug/PartialEq/Eq - lib.rs re-exports Error - unit tests: variant construction, the single-enum altitude shape, and the Missing-is-never-get/stat-absence doc contract (facade behavioral enforcement lands in wave 2) - tasks/core-error-model.md: status completed; Notes + Summary filled Verified: cargo test, cargo clippy --all-targets -- -D warnings, cargo fmt --check |
||
|
|
24d1c68343 |
feat(crate): scaffold the ADR-013 §4 feature graph and module skeleton
- Declare the decided feature graph in the manifest: sqlite default-on (rusqlite 0.40 bundled), postgres (tokio-postgres 0.7 + deadpool-postgres 0.14), mem (dependency-free), pg-lo pulling the shared pg driver cluster only (no postgres implication, ADR-010 §1), ops (alkcall 0.8.1, default-off); local ungated - Replace the placeholder crate body with the module skeleton: lib.rs re-export shape, ungated stubs (hashing, error, backend, facade, dispatch, gc, local), engine stubs behind #[cfg(feature)] (mem, pg_lo, postgres, sqlite, ops); stubs are doc-comment-only so wave-1 tasks own the module bodies Verification: cargo test green (default); cargo check --all-features resolves the full graph; cargo check --no-default-features --features pg-lo resolves with rusqlite provably absent (tree-inverted match) — the ADR-010 §1 no-implication rule; clippy --all-targets -D warnings and cargo fmt --check clean. Task: tasks/scaffold-crate-features.md (wave 1) |
||
|
|
8a2df4fa57 |
docs(plans): implementation plan — wave-based decomposition; wave 1 decomposed into tasks/
- docs/plans/implementation.md: the meta plan (alkstore pattern) — nine waves, dependency logic, decided points, review gates. Scope authority: v0.1.0 ships the full surface (all five engines, fleet GC, ops). - Wave 1 decomposed: scaffold-crate-features, core-error-model, core-hashing-keys, core-trait-and-types, suite-scaffold, review-wave-1 (the one-way-door gate). - Waves 2-9 shaped in the plan; each decomposes after the prior wave's review gate. Verification: cargo test / clippy -D warnings / fmt --check green (docs-only change). |
||
|
|
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
|
||
|
|
bee86b0dbd |
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. |
||
|
|
7b9d904b8a |
docs(architecture): ADR-012 — pre-decomposition consistency rulings; tree verified single-valued
Full-tree review before task decomposition found five composition
defects (mechanisms specced correctly in isolation, composition
unruled) and a set of caller-facing gaps. ADR-012 rules each:
- Sweeps are never errors: aborts are GcAbortCause report data in
Ok(SweepReport) (ProtectFailed / SweeperLock / NoLivenessSources);
GcAborted retired from the error enum; direct-delete refusal is
the GcRefuse error covering the full protection set
- Pin token gains its wire shape: blobs/put response {token, digest};
blobs/have's token-renewal form (one digest + token); token
validity domain = the minting serving node, process-lifetime
mapping
- Fleet mode is an explicit constructor declaration (fleet: true),
never inferred from engine choice
- All fleet GC state hosts on the fleet's kv engine (postgres) — one
arbitration domain; large=pg-lo fleet nodes required onto the same
pg instance (composite predicate's new clause); large=local fleet
puts pin-row-first, publish-second
- Engine-state seam reduced: sqlite pin/sweep-lock bodies dropped
(dead machinery); non-SQL engines stage delete-window candidates
in-process; one-window-host rule per instance
- Facade clarifications: fall-through for all key-addressed ops,
kv-only put-time rejection, mem+local dual-tier valid, error-model
member/return-shape ruling (trait/facade family split), has ->
bool, PinState variants, fleet liveness-table registration form,
window executor = the next sweep
Alignment edits across all specs and ADR-005/008/009/010/011
(bracketed corrections per the established pattern); OQ-11 (pg-only-kv
feature graph) added to the parked index for auditability.
Verification: two independent review passes; all findings resolved;
verdict READY for task decomposition.
|
||
|
|
abf8c5ae9c |
docs(architecture): ADR-010/011 — I/O seams, GC-state home, vocabulary; pre-decomposition consistency round
ADR-010 (new): the Backend trait's I/O seams pinned before any code — 'get' returns a crate-internal read cursor (kv engines materialize; 'local' pread loop; pg-lo lo_get windows), 'put' has two named forms (whole-value kv / staged-put large); GC state is store-core-owned via a crate-internal contract-tested engine-state seam on the SQL-backed engines (backends never learn liveness — ADR-005 verbatim); the store core holds the joint entry+pin transaction (corrects ADR-009 §2's ownership statement); kv-tier fleet-validity rule added (mirror of ADR-008 §3); ops pin-token TTL race resolved (the token IS the pin; renewal rides have/re-put; pause-past-TTL falls to delete-then-recover); feature graph pinned (pg-lo does not imply postgres). ADR-011 (new): vocabulary pinned — tier (contract, exactly two) vs engine (concrete impl, exactly five); store instance / node / fleet split (fleet = pool-sharing, not node count — fixes requirements.md's self-contradiction with REQ-2); mem demoted from 'backend'/'testing tier' to the kv tier's third engine (contract-reference engine); fs tier renamed 'large' including the feature name (last free moment before code exists); constructor modes pinned (dual-tier default, kv-only, mem-only) plus the composite fleet-validity predicate. Consistency round across all specs and ADR-003/004/007/008/009: stale pg-lo passages resolved per ADR-009's supersession; ADR-008 owner 'node id' → store-instance id; threshold wording corrected (conservative edge, not midpoint, of the 128-256 KiB crossover zone); SweepReport shape specced; mem classification unified; 008/009 ADR files renamed to match the tier rename; README deferral-policy recap aligned (third category = decided-but-sequenced work, not a parking kind); POC crate list completed. Verification: two independent architecture review rounds (the first found 4 criticals — unpinned trait I/O shapes, missing fleet-state seam, node/fleet self-contradiction, pin-token/TTL conflict — all resolved; final round: zero criticals); all markdown links resolve; ADR tables complete (11 ADRs). |
||
|
|
9bbe6f3ad9 |
docs(architecture): ADR-009 — pg-lo admitted as the fs tier's second engine on POC #7
- ADR-009 (new): postgres Large Objects ship as the fs tier's second engine (feature pg-lo, default-off), admitted on POC #7's passed gate (contract 10/10, durable put ~= fs at >=1 MiB, lo_get window gets, transactional LO lifecycle = zero crash orphans). Engine shape: companion table (lo_entries) is the contract authority; put = one tx (lo_create + lowrite + row + pin); get = descriptorless lo_get windows (lo_*64 discipline); named operator deltas (catalog space never returned / autovacuum inherited / sweep for the legacy bypass class only) and named performance deltas (cached-get 20-50x behind page-cache fs single-stream, ~700 MB/s aggregate at 16 readers = the fleet serving picture). - Ripple: backends-and-dispatch (pg-lo shipped; fleet locality contract gains the whole-fleet-pg-lo option), overview (layer map, feature table + pg-lo row, OQ -> shipped pointer), requirements (engine def + REQ-2 note updated), README (current-state + ADR table), store-api (invariant 1/6 engine lists), ADR-008 Status amendment (pg-lo posture superseded by ADR-009), open-questions (audit trail completed: decided -> sequenced -> completed). Verification: docs-only; all cross-references resolve (ADR/REQ/POC); register, findings, spec, and both engine-ADR status lines agree. |
||
|
|
cee97defba |
docs(research): POC #7 — postgres Large Objects as the fs-tier pg-lo engine, passed
The ADR-008 pg-lo admission POC ran in a standalone crate (/workspace/alkblobs-pglo-poc): PgLoBackend over the ADR-003/008 trait contract (including size), 10/10 exact-count sweep-outcome contract tests, clippy/fmt clean; dockerized postgres:16-alpine on :15432, POC #5 driver stack (tokio-postgres + deadpool) via SQL lo_* functions, no new dependency. Gate verdict: passed, with named deltas. - Performance: durable put 60-65 MB/s at >=1 MiB, within 1.5x of — and below 1 MiB beating — durable local fs on this fsync-slow disk; cached gets 70-180 MB/s single-stream, ~0.7 GB/s aggregate over 16 readers (20-50x behind page-cache fs — the honest named delta) - Contract: companion table is the list()/size()/CAS authority (never the catalogs); stage-then-commit; GC-participating lo_unlink delete - Handles: the tx-scoped descriptor is real but pool-compatible via descriptorless lo_get(oid, off, len) windows — window gets keep handle-acquire p99 at 1-6 ms under readers <= pool; held descriptor is the fallback posture - Vacuum: pg_largeobject pages churn-reused, never returned; tracked by autovacuum; rel-size monitoring named as an ops requirement - Crash/orphan: LO creation is transactional — kill/terminate mid-write-tx leaves zero orphan pages; the only orphan class is a committed LO bypassing the companion table (planted, reaped by the ~7 ms/oid sweep; committed content survives byte-exact) - Harness lessons: lo_lseek is int4 — the 64 variants are the >2 GiB discipline; shared-table parallel tests are unsound (per-test CREATE DATABASE isolation) Docs: new poc-pglo-findings.md; poc-pglo-spec.md status passed; register OQ-BL-06 #7 marked passed; ADR-008 pg-lo bullet updated (duplicate bullet removed) + backends-and-dispatch/open-questions cross-references. Verification: cargo test --release (10 passed), clippy -D warnings, fmt --check in /workspace/alkblobs-pglo-poc. |
||
|
|
281c37876e |
docs(architecture): ADR-008 — trait size probe, fleet GC, fs-tier engines; requirements anchor
- requirements.md (new): the three use cases pinned as REQ-1..4 with the node/pool/fleet/engine vocabulary defined once — ends per-session re-derivation of consumer facts. REQ-2 (replicator fleet over one shared pg pool, incl. large-blob serving) is recorded as a planning fact predating all POCs, on the operator's authority. - ADR-008: Backend trait gains size(key) length probe (before any backend ships data — ADR-002 one-way-door discipline); fleet GC mechanism (DB-backed pin rows committed atomically with entries, TTL+renewal semantics, liveness = embedder-owned table, protect callback is single-node-only, advisory-locked single sweeper, staged re-arbitrated delete window on both engines); fs tier becomes engine-selectable (local default; pg-lo named candidate) with the fleet locality contract (shared media or re-routing; mixed tiers are a documented deployment invariant, not a constructor-provable one). - poc-pglo-spec.md (new): POC #7 spec — pg Large Objects as the fs tier's pg-lo engine; instruments, decision gate, registered in phase-0.md OQ-BL-06. - ADR-003/005 status amendments point to ADR-008's extensions; specs ripple (backends/store-api/gc/ops/overview/README). - open-questions.md: deferral-policy header gains the decisions-vs- sequenced-work distinction; pg-lo's why-not-parked audit trail recorded. - research fixes: postgres POC renumbered #4->#5 to the canonical register (phase-0 OQ-BL-06), redb cross-refs fixed, thinking- artifact sentence in B1 replaced with the honest reading. Verification: docs-only change; reference-integrity sweep across the tree (ADR/REQ/POC refs resolve); architecture-reviewer pass on the delta — original 3 criticals addressed, its follow-up (fleet liveness form, pin TTL, staged-delete semantics, enforceability, shared-media caveats) fixed in this commit. |
||
|
|
395d58cf3f |
docs(architecture): ADR-007 — two kv engines (sqlite + postgres) behind one trait
Dissolve OQ-10's circular "standing offer" framing and record the engine decision the POC evidence already supported: - ADR-007 (new): kv tier ships sqlite (default) + postgres (feature `postgres`, default-off), constructor-selected per node; records the deployment economics (classical kv+fs+relational downstream stack collapses to one relational engine + fs), the pg impl contract (POC #5 B5 posture deltas), the CI sweep-safety gate under --all-features, redb ruled out (POC #6), and posture defaults as deferred cost in the ADR-006 pattern - ADR-003: amended status (engine pin widened by ADR-007); kv entry notes the widened engine set - ADR-004: reconciliation note — the "two backends" count is tiers, not engines - backends-and-dispatch.md: tier≠engine distinction, pg engine section, co-tenancy note (same-file consumer writes share sqlite's single-writer ceiling), substitution-door precedent - open-questions.md: OQ-10 resolved-by-ADR-007, with the why-this-is-not-Schrödinger's-code record (a deployment running alkblobs cannot pre-exist the pg engine — OQ-09 precedent); parked index narrowed to OQ-07/OQ-08 - overview.md: layer map + dependency posture reflect the `postgres` feature; README.md: ADR-007 in tables + corollary applications - phase-0.md / pg+redb findings: promotion + supersession notes Trigger framing survives only as deployment guidance (which engine a node chooses — topology, not throughput), not as a gate on the crate's own work. Verified: cargo test, clippy --all-targets -D warnings, fmt --check. Docs-only change (Phase 1 architecture); no implementation yet. |
||
|
|
1af9399900 |
docs(architecture): OQ-10 — postgres as the second shipped kv engine, a standing offer on a deployment trigger
Records the two-engine story the POC #5/#6 round established, in the terms this crate's boundary uses (engine, not backend; trait contract as the admission gate): - OQ-10 (externally-owned standing offer): open the postgres-kv ADR when a multi-tenant replicator deployment materializes. Trigger is topology, not throughput — cross-machine write access to one pool (throughput pressure / sqlite's flatline is the early warning). Names the trigger-time work: PgKv impl + engine-posture deltas (synchronous_commit, pooling/prepared-statement discipline, autovacuum, ~40x storage overhead) + the sweep-safety proof (vacuum-stable list() cursor, exact-count sweep tests). Sequenced: sqlite stays Phase 1 mainline; evidence base is done and waiting. - poc-postgres-kv-findings.md: engine deployment posture section — the three use cases (self-hosted git serving, p2p replicator nodes, agent-workspace pools) mapped onto the measured curves; per-node choice as the form of the fork; honker parity stack named for the pg side (LISTEN/NOTIFY + pgboss-rs, deployment layer). No code changes; engine work parallelizes against the fixed contract. |
||
|
|
5eea656295 |
docs(research): POCs #5/#6 — postgres and redb as the kv engine, both substitution-seam inputs
Post-convergence engine studies against the ADR-003 kv pin, using POC #3's harness unchanged (sqlite/fs arms byte-identical) plus the concurrency axis POC #3 never measured: - #5 postgres (B1-B6): solo pg floor ~1 ms (fsync-dominated, strace- decomposed), ~40x storage overhead; decisive scale-out — pg PUTs ~12x at 12 conns / ~37k ops/s while sqlite flatlines at its ~1.2k WAL single-writer ceiling. Viable future ADR for the multi-client replicator shape; durability/ops posture change, not a drop-in. - #6 redb (C1-C6): the "2-7x over sqlite" folklore inverted — sqlite ~430x over redb at crash-consistent puts (redb Immediate = 1 fdatasync/commit, 8-30 ms on this disk; its None tier is not crash-consistent). Reads ~530k/s but irrelevant. Ruled out at a durability-tier mismatch: WAL-NORMAL's shipped tier is not expressible in redb 4.x's two-level API. - Net: sqlite's pin now has four-way triangulated evidence (fs vs sqlite #3, pg #5, redb #6, lineage citation); batched commits proven as the universal lever across all three engines. Code: /workspace/alkblobs-postgres-poc, /workspace/alkblobs-redb-poc (standalone crates per the POC placement convention; 14 inherited tests passing in each, clippy -D warnings + fmt clean). Verification: no src/ changes — docs only; both POC crates verified (test/clippy/fmt) with findings recorded above. |
||
|
|
e403b22e26 |
docs(architecture): clarify Backend trait scope and kv/sqlite framing
The backends spec read as if the Backend trait were a general storage abstraction and 'two backends, exactly' banned other storage in a deployment — so a downstream wanting sqlite manifests appeared forced into implementing a third backend, the exact weld ADR-004 forbids. - backends-and-dispatch.md: state plainly that the trait is a CAS-tier contract (immutable digest-keyed entries, GC-shaped list/delete) and never a host for manifest/index storage; consumer storage is beside-the-pool. 'Two, exactly' counts trait impls shipped, not storage per deployment. kv: tier name vs pinned sqlite engine made explicit. - ADR-004: add the explicit corollary under the consumer-seams point. |
||
|
|
b18521a0e6 |
docs(architecture): dissolve OQ-09's self-referential deferral
- OQ-09 resolved closed-by-default (deny-unlisted), decided on risk asymmetry in hand, not on a first deployment the crate itself must create; named reopen condition instead of a parked wait - delete oq-09-ops-visibility-tracker.md (watch-task for an event the crate itself gates — never collapses) - fix doc-lifecycle circularity: externally-owned OQs no longer block a spec's `reviewed` promotion - gc-and-namespaces: "until a consumer names a requirement" reframed as re-entry via a new ADR, not a parked question - README: state the corollary explicitly — facts this crate must create (its own first deployment/consumer) are never deciding inputs Verified: full-text sweep for stale OQ-09 deferral/tracker references comes up clean across docs/ and tasks/. |
||
|
|
4b5009d85a |
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 |
||
|
|
7d9ac8a6ec |
docs(research): correct layer vocabulary — alkcall has no transport code
- Verified against alkcall 0.8.1: no networking deps; Connection wraps a caller-provided established stream (ADR-012 dial/call decoupling) - 'transport' was being used loosely for 'wire/ops protocol'; now reserved for endpoints/dialing/byte-moving, which is in neither crate. Ops/framing/ACL surface = wire/ops protocol (alkcall's layer) - Vision sentence, scope line, OQ-BL-01 argument, and alkcall prior-art entry rewritten with the accurate layering; alkcall entry now states the transport-free fact explicitly - iroh-blobs-eval.md: 'welded to their transport' -> 'welded to their protocol stack (endpoints/transport weld on above it)' Verification: cargo test / clippy -D warnings / fmt --check clean |
||
|
|
b0b385d94e |
docs(research): settle OQ-BL-01 substrate — ops surface rides alkcall, with the argument recorded
- The 'alkcall usage undecided with the first wire consumer' posture was an inherited assumption, never a decision; it conflated iroh- blobs' actual weld (no Store trait, store inseparable from irpc — convention 7's rejection target) with having a protocol substrate under the ops layer at all - Settled: ops surface is alkcall-shaped — validated JSON control ops (OperationSpec/JSON Schema, ADR-016 error schemas), binary data channels via the channel_open marker (ADR-047; Sub ADR-021 fetch, Pub/Sink ADR-046 put), ACL via AccessControl + ownership (ADR-011) under the ADR-017 privilege model; an in-band op/auth scheme would duplicate reviewed family machinery (convention 6 already mandates the seam) - Store layer stays substrate-agnostic (principle 1 unchanged); alkcall enters only at the ops module/sibling boundary - Remaining Phase 1 ADR scope reduced to placement: feature-gated ops module (lean) vs sibling crate - Synced vision sentence, scope line, alkcall prior-art entry, OQ-BL-05 ACL line, recommended approach, decision table Verification: cargo test / clippy -D warnings / fmt --check clean |
||
|
|
1f905003d0 |
docs(research): Phase 0 convergence — settle OQs, add decision table + ADR backlog
- phase-0.md: fold chronological accretion into a settled posture; status -> converged; OQ-BL-01..06 restated as settled-base + named residuals; new Convergence section (recommended approach, decision table, Phase 1 ADR backlog); plan section marked complete - iroh-blobs-eval.md: fix crossover numbers (64-128 -> 128-256 KiB, matching POC #3 finding A4) and B-finding -> finding-A4 reference - poc-largeblob-findings.md: summary-table label fix (B-finding -> finding A4); align small-blob multiplier with measured 9-10x Verification: cargo test / clippy -D warnings / fmt --check clean |
||
|
|
6b278fb490 |
docs(research): POC #3 passed — large-blob streaming + sqlite-vs-fs benchmark
- POC #3 (register OQ-BL-06) executed and passed: streaming LFSObject-shaped put/get at the store layer; fanout seam (serve-while-persisting) validated as broadcast above the backends; fs range reads with the verification gap documented (preamble makes slice-vs-whole verification impossible — chunk-tree encoding sharpened to a transfer-layer conditional) - sqlite-vs-fs micro-benchmark: first-party anchor for the small-blob belief (~9-10x sqlite at 1-16 KiB, crossover ~128-256 KiB, 128 KiB default lean); harness re-runnable on real media - Stage-file hygiene tests caught a real leak (one temp file per failed put) — fixed; all failure paths converge on discard - Pack tension resolved in shape from the gix-odb read: packs as large blobs served by range reads; object-level dedup lives in the loose tier - iroh-blobs-eval.md written against the current checkout (e82cbdc / v0.103.0): actor/message-protocol pattern, fs backend layout, verification, do-not-inherit + keep lists - alknet probe re-check demoted to historical cross-check only: alknet's code is not a design input (decomposed; substrate is alkcall); its small-blob perf claim was asserted-never-measured, superseded by A4 - phase-0 register complete: no open POCs; next step is convergence -> Phase 1 Verification: POC crate (standalone /workspace/alkblobs-largeblob-poc) 14 tests pass, clippy -D warnings clean, fmt clean; digests byte-exact vs the real git hash-object CLI on both streaming put paths |
||
|
|
493516424a |
docs(research): POC #1 passed — trait contract + digest + dispatch
POC code: /workspace/alkblobs-trait-poc (17 tests passing, clippy -D warnings + fmt clean; digests validated byte-exact against the real git hash-object CLI, both object formats) Findings (poc-trait-dispatch-findings.md, 8): - lean Backend trait (opaque byte keys, complete list(), put-path pinning) is sufficient; Key typed wrapper lives at the boundary - redb traps: list() key-vs-value destructure (silent until GC catches it — malformed list is as deadly as incomplete), virgin-db TableDoesNotExist on first read, no in-memory db (temp file used) - pinning: Pinned RAII guard + refcount map works; batch-scope pins and the sweep-vs-put race are named Phase 1 requirements (iroh DeleteSet = prior art) - git-blob preamble abstraction validated; external-git oids address pool entries with zero indirection (the OQ-BL-03 resolution, implementation-confirmed) - tagged key format ([algo byte][digest]) makes git-sha256 + git-sha1 coexist; sort-stable for sweep diffs - dual dispatch works as simply as hoped; routing deterministic - namespace tables: protect callback MUST share live state (the snapshot-install bug proves the API lesson: register_liveness_source shape, not install_into) - GC semantics verified: exact deletion counts, abort-callback, delete-then-recover byte-identical re-put phase-0.md: POC #1 marked passed, #2 absorbed (validated), #4 covered in miniature (concurrency half deferred to Phase 1 impl work); OQ-BL-02 base settled; OQ-BL-03 implementation-validated; OQ-BL-05 mechanism settled with findings folded in; plan updated (POC #3 is the last open POC) |
||
|
|
cde279b76d |
docs: hash simplification — canonical git-family hash resolves BLAKE3
- OQ-BL-03 RESOLVED: BLAKE3's presence traced to pure iroh-blobs inheritance; with that rejected the multi-hash framing dissolves. Three tiers: canonical git-blob-sha-256 (git oids + every other consumer share one address domain — cross-consumer dedup free), tolerated git-blob-sha-1 (protocol necessity, git's hardened-SHA-1 threat model), conditional BLAKE3 (only if a bao-like chunk-tree encoding is ever adopted; confined to that encoding layer) - Principle 2 rewritten (one canonical hash, git-family derivation); alkgit consumer entry updated (the 'hash conflict' was an artifact of the BLAKE3 inheritance); p2p section notes strengthened dedup - OQ-BL-04: verification options no longer BLAKE3-mandatory; chunk- tree encoding note — verified wholes register under the canonical hash so chunked and whole-file paths dedup together - POC register: #2 absorbed into #1 (postamble abstraction + SHA-1 tolerance are #1's trait work); #3 gains the sqlite-vs-fs micro-benchmark pull-out - AGENTS.md convention 5 aligned (canonical git-family derivation) |
||
|
|
55450d6bf0 |
docs(research): consolidate GC/pooling round into phase-0
- iroh-blobs GC mechanics verified against current checkout: mark-and-sweep (named tags + TempTag RAII + ProtectCb with abort), format-aware mark traversal, DeleteSet write-safety, no namespaces (flat tags) — our namespace concept maps onto their machinery - rudolfs prior art section: StorageKey(ns, oid) composite key + decorator storage stack; THE ANTI-PATTERN inverted (physically namespaced storage defeats cross-tenant dedup); list() is load-bearing; verify-as-decorator; streaming LFSObject + fanout - OQ-BL-03: oid<->hash mapping DISSOLVED — git's oid derivation is itself a hash algorithm (domain-separated preamble); per-algorithm namespaces; mapping only reappears for transfer-vs-storage hashes - OQ-BL-05 consolidated: physically flat/logically namespaced; tags are the root table; references stay above the crate; mark-and-sweep with protect-callback lean (a) + TempTag pinning; traversal ownership (a/b/c) is the Phase 1 ADR - POC register updated: #1 adds list() completeness + pinning contract, #2 reframed as validating oid-dissolution, #4 as the GC worked example; 'what each POC decides' section added |
||
|
|
5e0a594cfd |
docs(research): fold p2p-git/dedup discussion into phase-0
- Consumers: add agent workspaces (third consumer); add the p2p shape section (contract/gossip/replicator sketch) and pin what it implies here: pooled CAS per node, repos/workspaces as hash-reference sets - Principles: add 6 (pooling is the point) and 7 (whole-file CAS default; CDC scoped to large-binary edits; fixed-size trees not inherited) - OQ-BL-01: ops surface upgraded from speculative to likely (have/need + verified fetch), crate-boundary question remains - OQ-BL-04: chunking scoped; new sub-question — chunking in store encoding vs manifest layer - New OQ-BL-05: pooling/GC — namespace shape, lifetime strategies, the pack tension (constrains store surface) - POC register: add #4 (pooled CAS + GC); note pack tension rides #3 - gix-odb: note alternates/object-pools as cross-repo dedup prior art |
||
|
|
7f7f70b220 |
feat: initial repo setup (Phase 0 scaffolding)
- AGENTS.md: repo conventions adapted from alkcall/alksocks (store posture, multi-hash requirement, no-iroh-wire rule; no fuzz gate yet) - Cargo.toml + src/lib.rs: bare lean lib skeleton (tokio async, thiserror) - docs/research/phase-0.md: Phase 0 draft — vision, prior art (iroh-blobs store, gix-odb, alknet appfile probe, alkcall), OQ register, POC plan - .gitignore, LICENSE-APACHE/MIT - Re-pointed stale alknet/alkcall references in .opencode/agents/ and docs/sdd_process.md Verification: cargo build/test, cargo clippy --all-targets -- -D warnings, cargo fmt --check, cargo doc --no-deps — all pass |
||
|
|
357311d27b | init |