From 009de420e161cd6a1dbca382ebedd33343eb18a7 Mon Sep 17 00:00:00 2001 From: "glm-5.3-flash" Date: Mon, 12 Oct 2026 14:22:55 +0000 Subject: [PATCH] =?UTF-8?q?docs(tasks):=20decompose=20wave=206=20=E2=80=94?= =?UTF-8?q?=20the=20fleet=20GC=20mechanism,=20the=20architecture's=20most?= =?UTF-8?q?=20intricate=20state=20machine=20under=20concurrency?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 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. --- docs/plans/implementation.md | 4 +- tasks/engine-pg-lo-joint.md | 145 +++++++++++++++++++++++ tasks/review-wave-6.md | 145 +++++++++++++++++++++++ tasks/store-fleet-construction.md | 180 ++++++++++++++++++++++++++++ tasks/store-fleet-put.md | 171 +++++++++++++++++++++++++++ tasks/store-fleet-sweep.md | 189 ++++++++++++++++++++++++++++++ tasks/suite-column-fleet.md | 150 ++++++++++++++++++++++++ 7 files changed, 982 insertions(+), 2 deletions(-) create mode 100644 tasks/engine-pg-lo-joint.md create mode 100644 tasks/review-wave-6.md create mode 100644 tasks/store-fleet-construction.md create mode 100644 tasks/store-fleet-put.md create mode 100644 tasks/store-fleet-sweep.md create mode 100644 tasks/suite-column-fleet.md diff --git a/docs/plans/implementation.md b/docs/plans/implementation.md index 632eb49..f119539 100644 --- a/docs/plans/implementation.md +++ b/docs/plans/implementation.md @@ -1,6 +1,6 @@ --- status: draft -last_updated: "2026-10-12 (wave 5 decomposed into tasks/)" +last_updated: "2026-10-12 (wave 6 decomposed into tasks/)" --- # alkblobs — Implementation plan @@ -53,7 +53,7 @@ ops payload surfaces; release readiness is last. | [3](#wave-3--sqlite-engine) | sqlite kv engine (default): WAL posture, prepared statements, engine-state window family, suite column | wave 2 | decomposed (2026-10-11) | | [4](#wave-4--postgres-kv-engine) | postgres kv engine: schema, pool, CAS rows, stable-cursor list, full engine-state clause families, suite column | wave 2 | decomposed (2026-10-12) | | [5](#wave-5--pg-lo-large-engine) | pg-lo large engine: LO staged put, descriptorless lo_get windows, companion rows, window-family seam clauses, suite column | wave 4 | decomposed (2026-10-12) | -| [6](#wave-6--fleet-gc) | Fleet GC mechanism: DB-backed pins, joint entry+pin tx, sweep lease, SQL delete arbitration, fleet-validity predicate + constructor declarations | waves 4 + 5 | pending | +| [6](#wave-6--fleet-gc) | Fleet GC mechanism: DB-backed pins, joint entry+pin tx, sweep lease, SQL delete arbitration, fleet-validity predicate + constructor declarations | waves 4 + 5 | decomposed (2026-10-12) | | [7](#wave-7--ops-surface) | `ops` feature: alkcall-backed op family, ACL mapping, pin tokens + renewal, fanout seam | wave 6 | pending | | [8](#wave-8--fuzzing) | Fuzzing: family pattern targets (key encoding, ops payloads), corpus-replay gate | wave 7 | pending | | [9](#wave-9--release-readiness) | Crate docs, feature-graph verification, README, publish prep | wave 8 | pending | diff --git a/tasks/engine-pg-lo-joint.md b/tasks/engine-pg-lo-joint.md new file mode 100644 index 0000000..540c5cf --- /dev/null +++ b/tasks/engine-pg-lo-joint.md @@ -0,0 +1,145 @@ +--- +id: engine-pg-lo-joint +name: the pg-lo joint contribution — staged-put mechanics into the store-held tx +status: pending +depends_on: [] +scope: moderate +risk: high +impact: component +level: implementation +tags: [wave-6, engines, pg-lo, gc] +--- + +## Description + +Land the **pg-lo engine's joint contribution** — the wave-5 deferred +design point, recorded in `src/pg_lo.rs`'s scope boundary ("the *fleet* +joint form (wave 6's store-held tx, ADR-012 §4/§5's ruling that pg-lo +contributes LO writes + companion-row insert into it) is a wave-6 +design point") and in `src/gc.rs`'s `EngineFleetState::joint_put` +clause doc ("an engine-task contribution against this `JointTx` +type"). Under the fleet consolidation posture (kv = postgres + +large = pg-lo, same pg instance — admitted by +`store-fleet-construction`'s predicate), a large entry's content +reaches the **same store-held transaction** the pin row commits in: +one tx over the shared instance — the pg-lo engine contributes its +staged-put mechanics (chunked `lowrite` + companion-row insert) into +it; the pin-row insert and the commit stay the store's (`pin_commit`, +the postgres host's clause — landed wave 4, unchanged here). + +- **The one-tx ruling** (ADR-012 §4): the store core holds one + transaction over the shared instance; the pg-lo engine contributes + LO writes + companion-row insert; the pin-row insert is the store's. + The engine sees pins only as rows; never as semantics (ADR-010 §2's + correction of ADR-009's ownership statement — nothing of + `EngineFleetState` lands on pg-lo itself; the contribution is + mechanics the store core drives). +- **The contribution's API shape** — the named design point wave 5 + deferred. The engine's existing staged put holds its own tx (the + held-tx posture: the staging cursor owns a pool checkout with an + explicit session-level `BEGIN`); the joint form inverts the + ownership — the store core's `PgJointTx` (the postgres engine's + held checkout, wave 4's recorded shape) is the tx the LO writes + ride. **Named design point — the contribution's API home**: decide + the shape consistent with the seam's ownership rule (the store + holds the tx; the engine contributes mechanics into it) — the + natural shapes are a crate-internal pg-lo method taking + `&mut PgJointTx` (the engine writes `lo_create`/`lowrite` + + the companion-row insert through the held checkout) or a joint + staging-cursor variant handed out against the held tx; decide, + record, and keep the public trait surface untouched (the + contribution is crate-internal, like every seam clause). The + `JointTx` type is the postgres engine's `PgJointTx` — the + consolidation posture's one shared instance is what makes the + type shareable (ADR-012 §4's checkable clause; + `store-fleet-construction` enforces it). +- **The staged-put mechanics, re-shaped not re-written**: the 512 KiB + `lowrite` knee (`LOWRITE_CHUNK`, the C2 citation), the CAS + existence check first (a re-put commits an empty contribution, + never `lo_create`), the companion-row insert, transactional LO + creation (zero crash orphans — C6, structurally: the LO lives and + dies with the store-held tx) — the engine's landed bodies carry; + what changes is whose tx they run in. The engine-level held-tx + staging cursor is **unchanged** (the non-fleet put path keeps its + own tx; the wave-5 hygiene tests stay green untouched). +- **Failure discipline**: every failure path inside the contribution + leaves the store-held tx in a state the driver can terminate — the + contribution never commits, never rolls back the store's tx + unilaterally (the driver owns termination; amendment A1's + discipline — the driver task owns the catch-propagate-terminate + design; this task's duty is that the contribution's error paths + compose with it: no partial LO state that outlives the tx's + rollback, which transactional LO creation already guarantees). +- **Contract tests** (engine-level, in `src/pg_lo.rs`'s test module, + the established env-driven convention): the contribution writes LO + content + the companion row into a `begin_joint_tx`-held tx and + `pin_commit` publishes entry + pin row in one step (byte-exact + round-trip after commit); a forced failure mid-contribution aborts + the whole tx with zero residue (no LO pages, no companion row, no + pin row, no kv row — the stage-hygiene invariant's tx form, the + wave-4 joint-atomicity test's pg-lo edition); the CAS re-put form + (a second contribution of the same key commits an empty + contribution; the pin row upsert-renews); the 512 KiB knee rides + (a multi-chunk contribution). **Tests need a live server** — the + alkstore harness convention: env-driven (`ALKBLOBS_PG_*`), skip + cleanly server-less, acceptance requires green against the harness + server (`pglo-poc`, :15432, db `blobs`, `postgres`/`poc` — verified + live 2026-10-12). Adopt the `SchemaJanitor`/per-test-database + isolation posture from the start (amendment A2's lesson — the + wave-4 leak is not re-learned). +- **Scope boundary**: the store-core driver that calls this + contribution on the fleet put path is `store-fleet-put`; this task + ships the engine-side mechanics and their contract tests. The + non-fleet pg-lo put path is untouched. + +## Acceptance Criteria + +- [ ] The pg-lo joint contribution exists behind the crate-internal + seam (the public trait surface untouched; nothing of + `EngineFleetState` implemented on pg-lo itself) +- [ ] The contribution's API shape decided and recorded (the + store-held `PgJointTx` is the tx the LO writes ride; the + ownership rule honored — the engine contributes, never owns) +- [ ] One-tx publish: LO content + companion row + pin row + kv row + commit in one step; byte-exact round-trip after `pin_commit` + (contract-tested) +- [ ] Zero residue on any failure path (no LO pages, no companion + row, no pin row, no kv row — contract-tested with a forced + failure); the contribution's error paths compose with the + driver's termination discipline (A1) +- [ ] The CAS re-put form (empty contribution, upsert-renewal) and + the 512 KiB knee contract-tested +- [ ] The non-fleet held-tx staging path and its wave-5 hygiene tests + unchanged and green +- [ ] `cargo test --features "pg-lo,postgres"` green server-less + (skipping) and against the harness server at acceptance; + `cargo clippy --all-targets -- -D warnings` and + `cargo fmt --check` green (default + the pg-lo variants); + `cargo doc --no-deps` clean + +## References + +- src/pg_lo.rs (the scope boundary recording this task; the held-tx + staging bodies this re-shapes; `LOWRITE_CHUNK`) +- src/gc.rs (`EngineFleetState::joint_put`'s clause doc — the + contribution's seam home; `JointTx`'s ownership rule) +- src/postgres.rs (`PgJointTx` — the held-checkout tx type; the + wave-4 recorded design points) +- docs/architecture/decisions/012-pre-decomposition-consistency-rulings.md + §4 (the one-tx ruling; the same-instance clause) +- docs/architecture/decisions/010-backend-io-seams-and-fleet-state-home.md + §2 (the ownership rule; the pg-lo correction) +- docs/architecture/decisions/009-pg-lo-large-tier-engine.md (the + engine shape the mechanics carry) +- docs/research/poc-pglo-findings.md (C2 the knee, C6 zero orphans) +- tasks/engine-pg-lo.md (the staged-put bodies this extends) +- tasks/review-wave-4.md (amendment A1 — the drop discipline the + contribution composes with) + +## Notes + +> To be filled by implementation agent + +## Summary + +> To be filled on completion \ No newline at end of file diff --git a/tasks/review-wave-6.md b/tasks/review-wave-6.md new file mode 100644 index 0000000..ebc10a9 --- /dev/null +++ b/tasks/review-wave-6.md @@ -0,0 +1,145 @@ +--- +id: review-wave-6 +name: Wave 6 review gate — the fleet gate +status: pending +depends_on: [suite-column-fleet] +scope: narrow +risk: low +impact: phase +level: review +tags: [wave-6, review] +--- + +## Description + +The wave-6 review gate — **the fleet gate** (docs/plans/implementation.md +§Review gates: "exact-count sweep outcomes under concurrency vs live +postgres, the joint entry+pin tx atomicity, the sweep lease semantics +vs ADR-013 §2, the composite fleet-validity predicate enforced at the +constructor, kv-only put-time rejection"), before wave 7 decomposes. +The gate reads the implementation against the pinned ADR text line by +line (the wave-1..5 gates' method) — the documents, not memory. + +Gate checks: + +- **Exact-count fleet sweep outcomes under concurrency vs live + postgres**: the fleet column green against the harness server + (server-less runs skip cleanly — a skip never substitutes for + acceptance); the two-instance survival shape, the lease-contention + abort, the cross-instance arbitration refusal, the race across the + window delay, and the maintenance-step reaping all exact-count + asserted (INV-4's engine-gated fleet form). +- **The joint entry+pin tx atomicity**: through the public `put` (not + only engine-level) — an independent connection never observes an + entry without its pin row or the reverse; the A1 drop discipline + audited as implemented (every path from `begin_joint_tx` onward + terminates the tx — the wave-4 gate's amendment closed, its owner + discharged); the pg-lo consolidation one-tx form (LO writes + + companion row + pin row + commit in one tx; zero residue on + failure); the large=local pin-first/publish-second ordering (the + reverse ordering nowhere coded). +- **The sweep lease semantics vs ADR-013 §2**: one sweeper per pool + across instances; atomic test-and-set; holdership ends at + release/owner death/session loss; contention is the + `GcAbortCause::SweeperLock` report abort (`Ok(SweepReport)`, nothing + deleted — ADR-012 §1), never an error. +- **The composite fleet-validity predicate enforced at the + constructor**: every fleet-valid configuration constructs; every + non-fleet-valid configuration is a constructor error (kv ≠ + postgres, `mem` anywhere, `local` without the shared-media + declaration, pg-lo cross-instance); the declaration is explicit, + never inferred (ADR-012 §3); the same-instance probe's design + audited (a failed probe errors closed). +- **kv-only put-time rejection** under the re-routing posture + (ADR-012 §6.2's machinery, fleet edition). +- **The TTL/3 renewal on the guard**: the renewal lifecycle audited + (no leaked tasks; drop/conversion/batch-contribution stops + renewal; a failed renewal never resurrects an expired row — + ADR-012 §2's expiry semantics verbatim); the TTL is a fleet + constructor parameter (default 60 s), never API. +- **The fleet liveness verb vs ADR-012 §6.6 / ADR-008 §2**: the named + table reference form; the schema-ownership rule (the store reads + the embedder's table, never owns it); the protect callback's + single-node-only posture recorded; the two-domain arbitration + composition (in-process + SQL) decided and recorded. +- **The one-window-host rule under fleet**: every fleet-valid + configuration's window hosts on the kv engine; the pg-lo window + clauses dormant; the form branch keys off the engine + representation, never a flag. +- **The non-fleet postures unchanged**: the in-process protocol + verbatim under `fleet: false` regardless of engine — the wave-2/3/4/5 + suites green untouched. +- **Code-smell / hygiene sweep** (the gate's standing hygiene block — + the twice-bitten lesson is now an explicit check, not a hope): + - **`cargo doc --no-deps` zero warnings** — the PGLO-1 class + (public-module doc links resolving to crate-internal items) has + bitten waves 4 and 5; the gate runs rustdoc and asserts clean, + and de-links rather than exports. + - **The `#![allow(dead_code)]` scope notes match their remaining + scope** — the fleet family's note in `src/gc.rs` (its consumers + land this wave) must shrink to its true remainder or vanish; the + postgres/pg-lo notes likewise audited. + - **Stale forward references updated** — every "wave 6" note in the + module docs (`store.rs`'s fleet-declaration paragraph, the + constructors' "slots in without reshaping" recordings, + `pg_lo.rs`'s scope boundary, `postgres.rs`'s scope note, the + batch-delete helper's consumer note) reads the landed state. + - **Family conventions**: no inline comments in code (doc comments + on public API fine), thiserror, tokio-only async, no + `unwrap`/`expect` outside tests, naming, one-module-per-file + re-export shape, no blocking I/O on the async path. + - **Task-description correctness**: each wave-6 task's Summary + matches what actually landed (the gate reads the diffs against + the task texts, not the Notes' claims alone). +- **Gates green**: `cargo test` (default), `cargo test --features + postgres` (server-less: skipping), `cargo test --features + "postgres,pg-lo"`, `cargo test --all-features` (against the + server), the pure builds (`cargo check --no-default-features + --features postgres` and `--features "postgres,pg-lo"`), `cargo + clippy --all-targets -- -D warnings` (default + the postgres/pg-lo + variants + `--all-features`), `cargo fmt --check`, + `cargo doc --no-deps`. + +Findings are fixed inline where cheap, or recorded as wave-7 +amendments where they shape the next decomposition (the ops wave's +pin-token contract rides the fleet machinery — a renewal-surface +finding lands beside it). The gate's completion is what unblocks +wave 7's decomposition. + +## Acceptance Criteria + +- [ ] Every gate check above read and verified against the cited ADR + text (not from memory of it) +- [ ] Named design points each decided, recorded, and consistent with + the specs +- [ ] Findings fixed inline or recorded as wave-7 amendments with + owners +- [ ] All gates re-run green after any fixes — including + `cargo doc --no-deps` explicitly (the PGLO-1 class) +- [ ] Wave 7 decomposition unblocked (recorded in this task's Summary) + +## References + +- docs/plans/implementation.md (§Wave 6, §Review gates — the fleet + gate) +- docs/architecture/decisions/008-trait-size-probe-fleet-gc-and-large-engines.md + §2/§3 +- docs/architecture/decisions/010-backend-io-seams-and-fleet-state-home.md §2/§4 +- docs/architecture/decisions/012-pre-decomposition-consistency-rulings.md §1–§6 +- docs/architecture/decisions/013-family-composition-reactive-boundary-and-feature-graph.md §2 +- docs/architecture/store-api.md (§Invariants 4 — the fleet form) +- docs/architecture/backends-and-dispatch.md (the composite + fleet-validity rule) +- tasks/review-wave-4.md, tasks/review-wave-5.md (the gate method; + the amendments A1/A2 this gate audits closed) +- tasks/store-fleet-construction.md, tasks/store-fleet-put.md, + tasks/store-fleet-sweep.md, tasks/engine-pg-lo-joint.md, + tasks/suite-column-fleet.md (the wave's tasks) + +## Notes + +> To be filled by implementation agent + +## Summary + +> To be filled on completion \ No newline at end of file diff --git a/tasks/store-fleet-construction.md b/tasks/store-fleet-construction.md new file mode 100644 index 0000000..1985887 --- /dev/null +++ b/tasks/store-fleet-construction.md @@ -0,0 +1,180 @@ +--- +id: store-fleet-construction +name: the fleet declaration — the composite fleet-validity predicate at the constructor +status: pending +depends_on: [] +scope: moderate +risk: medium +impact: component +level: implementation +tags: [wave-6, store, construction, fleet] +--- + +## Description + +Slot the **`fleet: true` constructor declaration** into the +fleet-capable constructors (ADR-012 §3; the recorded non-event the +wave-4/5 constructor tasks pinned: "the `fleet: true` declaration is +wave 6, slotting in without reshaping") — the composite +fleet-validity predicate enforced at the constructor, the fleet +constructor parameters, and the store's fleet state. Nothing here +turns the fleet *mechanism* on (the joint-tx put driver is +`store-fleet-put`, the fleet sweep is `store-fleet-sweep`); this task +is the admission gate and the state those tasks consume. + +- **The declaration is explicit, never inferred** (ADR-012 §3): a + store instance is constructed with fleet mode on or off. Under + `fleet: false` (every existing constructor's implicit posture, + unchanged): the ADR-005 in-process protocol verbatim regardless of + engine. Under `fleet: true`: the composite fleet-validity predicate + is enforced at construction, and the fleet machinery activates (the + later tasks' wiring). +- **The composite fleet-validity predicate** (backends-and-dispatch.md + "The composite fleet-validity rule", one sentence, both tiers): a + configuration is fleet-valid iff **kv = postgres AND (large = pg-lo + on the same pg instance as the kv engine OR large = local on + declared shared media OR large = none with the re-routing posture + carrying pool large content)** — plus the explicit `fleet: true` + declaration. `fleet: true` is a **constructor error** for every + non-fleet-valid configuration (ADR-012 §3): any kv engine other than + postgres, `mem` anywhere, `local` without the shared-media + declaration, `pg-lo` on a different pg instance than the kv engine. + The predicate is enforced where the enforceable seam is — the + constructor sees only its own node; cross-node truth (is the + declared shared media actually shared? is the undeclared instance + sharing our pool?) is the deployment's verified invariant + (ADR-008 §3's seam; ADR-012 §3's misconfiguration reading — the + detection symptom documented, not constructor-provable). +- **The fleet constructor parameters** (ADR-008 §2: "fleet constructor + parameters, not API"): + - **The pin TTL** — default 60 s (`DEFAULT_PIN_TTL_SECS`, already a + postgres backend field awaiting exactly this wiring — the wave-4 + note), constructor-tunable. The store owns the TTL and the TTL/3 + renewal cadence; the engine's `pin_renew` successor-time body + reads the TTL it was constructed with (the wave-4 recorded design + point: "constructor-tunable when wave 6's fleet constructor + lands"). + - **The large-tier declaration** for `large = local` under fleet — + the operator's explicit shared-media assertion (ADR-008 §3's + enforceable-seam bullet: a fleet-mode constructor requires an + explicit large-tier declaration — `local` with the assertion, + routed (kv-only), or `pg-lo`). **Named design point — the + declaration's type shape**: how the assertion rides the + constructor signature (a dedicated declaration type the caller + must name — e.g. a fleet-opts shape carrying the shared-media + assertion — versus a bare flag) — decide consistent with ADR-008 + §3's "explicit declaration" wording (a named type the caller + cannot construct accidentally is the reading the ADR's + enforceable-seam posture supports) and record it. + - **kv-only re-routing posture**: `kv_only_postgres` under + `fleet: true` is valid (large = none; the re-routing posture + carries pool large content via the ops surface — wave 7; the + client-node shape is this constructor plus that composition, + ADR-008 §3). The over-threshold put-time rejection (ADR-012 §6.2, + already landed) is unchanged under fleet. +- **The pg-lo same-instance clause** (ADR-012 §4 — the checkable + clause that makes the joint entry+pin tx expressible): under + `fleet: true`, `dual_tier_postgres_pglo`'s two DSN+opts pairs must + resolve to the **same pg instance**. Cross-instance is valid only + as `fleet: false`. **Named design point — the same-instance + probe**: textual DSN comparison is insufficient (aliases, IP vs + hostname); decide the server-side check (the instance's + `system_identifier` via `pg_control_system()` — PG14+ — compared + across one connection per engine, is the natural shape; the + harness server's PG 16.15 carries it) and record it. A mismatch is + the constructor error. +- **The store's fleet state** — what the Store carries once declared: + the fleet flag (the put/sweep paths branch on it — never on + re-deriving from the engine set), the pin TTL, and the + **store-instance id** (`owner` — ADR-011's vocabulary; the pin + row's identity half). **Named design point — the owner id's + minting**: the instance id's generation (random bytes at + construction is the natural shape — unique per instance, stable + for the instance's life, carried as the `owner` bytes of every + `PinRow` the store mints) and its width/encoding; record it. The + id is crate-internal (never public API — ADR-008: fleet parameters, + not API). +- **The one-window-host rule under fleet** — verify the landed + posture composes: every fleet-valid configuration has kv = postgres + (SQL-backed), so the durable window hosts on the kv engine and the + pg-lo window clauses stay dormant (the landed consolidation shape + already carries no `gc_window` in the pg-lo schema). The predicate's + enforcement must not disturb the form branch (it keys off the + engine representation, never a flag). +- **Constructor docs updated** — the "implicitly `fleet: false`" / + "wave 6 slots in without reshaping" recordings in the postgres + constructors' docs (and the store.rs module doc's fleet-declaration + paragraph) updated to the landed state. +- **Constructor unit tests** (env-driven, skipping cleanly + server-less): every fleet-valid configuration constructs (pg-lo + same-instance, local + shared-media declaration, kv-only re-routing) + and introspects its fleet state (flag, TTL, owner id); every + non-fleet-valid configuration errors (`mem` anywhere, sqlite, + `local` without the declaration, pg-lo cross-instance — two + distinct schemas on the harness server are the same instance, so + the cross-instance negative needs two DSNs that resolve to + different instances; if the dev environment has only one, the + negative is exercised by pointing one DSN at a non-existent + instance and asserting the probe errors closed — a probe failure is + a constructor error, never a silent pass); `fleet: false` postures + unchanged (the existing constructor tests stay green). **Tests need + a live server** — the alkstore harness convention: env-driven + (`ALKBLOBS_PG_*`), skip cleanly server-less, acceptance requires + green against the harness server (`pglo-poc`, :15432, db `blobs`, + `postgres`/`poc` — verified live 2026-10-12). + +## Acceptance Criteria + +- [ ] The fleet declaration slots into the fleet-capable constructors + without reshaping them (the recorded non-event lands as + recorded); `fleet: false` postures byte-unchanged in behavior +- [ ] The composite fleet-validity predicate enforced at the + constructor: every fleet-valid configuration constructs; every + non-fleet-valid configuration is a constructor error (unit- + tested per clause: kv≠postgres, mem anywhere, local without the + shared-media declaration, pg-lo cross-instance) +- [ ] The pg-lo same-instance probe decided and recorded; a mismatch + or a failed probe is a constructor error (never a silent pass) +- [ ] The pin TTL is a fleet constructor parameter (default 60 s) + wired to the engine's renewal body; the shared-media + declaration's type shape decided and recorded +- [ ] The store carries its fleet state (flag, TTL, owner id); the + owner id's minting decided and recorded (crate-internal) +- [ ] The one-window-host posture verified intact under fleet-valid + configurations; the form branch still keys off the engine + representation +- [ ] Constructor docs updated to the landed state (the wave-6 + forward references resolved) +- [ ] `cargo test` (default) green; `cargo test --features postgres` + green server-less (skipping) and against the harness server at + acceptance; `cargo clippy --all-targets -- -D warnings` and + `cargo fmt --check` green (default + `--features postgres`); + `cargo doc --no-deps` clean + +## References + +- src/store.rs (the constructors and their recorded non-fleet + postures; the module doc's fleet-declaration paragraph) +- docs/architecture/backends-and-dispatch.md ("The composite + fleet-validity rule" — the one-sentence predicate) +- docs/architecture/decisions/012-pre-decomposition-consistency-rulings.md + §3 (the declaration, never inferred; the constructor-error set), §4 + (the GC-state host; the pg-lo same-instance clause; the + pin-first/publish-second ordering this task's declaration admits) +- docs/architecture/decisions/008-trait-size-probe-fleet-gc-and-large-engines.md + §2 (the TTL, fleet constructor parameters), §3 (the enforceable + seam; the explicit large-tier declarations) +- docs/architecture/decisions/011-vocabulary-tiers-engines-and-fleet-state.md + §4 (the constructor table) +- src/postgres.rs (`DEFAULT_PIN_TTL_SECS` — the wave-4 note awaiting + this wiring) +- tasks/store-postgres-construction.md, tasks/store-pg-lo-construction.md + (the recorded slotting-in postures this task lands) + +## Notes + +> To be filled by implementation agent + +## Summary + +> To be filled on completion \ No newline at end of file diff --git a/tasks/store-fleet-put.md b/tasks/store-fleet-put.md new file mode 100644 index 0000000..c688b71 --- /dev/null +++ b/tasks/store-fleet-put.md @@ -0,0 +1,171 @@ +--- +id: store-fleet-put +name: the fleet put path — the joint entry+pin tx driver and the pin guard's fleet form +status: pending +depends_on: [store-fleet-construction, engine-pg-lo-joint] +scope: broad +risk: high +impact: phase +level: implementation +tags: [wave-6, store, gc, fleet, put] +--- + +## Description + +Route the put paths through the **joint entry+pin transaction** under +`fleet: true` (ADR-008 §2; ADR-010 §2; ADR-012 §3/§4) — the store core +holds the tx from `begin_joint_tx` to `pin_commit`, engines contribute +mechanics, and publish + protection land in one step — plus the pin +guard's fleet form: the **TTL/3 renewal duty** on the guard. The +non-fleet put paths are byte-unchanged (the ADR-005 in-process +protocol verbatim; every wave-2/3/4/5 put test stays green). + +- **The joint-tx driver** (the store core, `src/store.rs`): under + fleet mode, a put whose entry lands on the kv engine (postgres) + runs `begin_joint_tx` → `joint_put` (the engine's CAS row write + inside the held tx) → `pin_commit(tx, [PinRow { key, owner, + expiry }])` — the pin row carries the store-instance id + (`store-fleet-construction`'s owner id) and `expiry = now + TTL` + (the store resolves the expiry; ADR-012 §2's rename — the TTL lives + with the store's renewal schedule). The public `put` signature is + unchanged; the routing branches on the fleet flag, never on + re-deriving from the engine set. +- **Amendment A1 — the joint-tx drop discipline** (the wave-4 gate's + owner lands here): the driver owes tx termination on **every** path + from `begin_joint_tx` onward — the natural `?` propagation between + the driver's steps must never return a mid-flight `PgJointTx` to + the pool (deadpool's `Fast` recycle never resets transaction + state — PG-2's hazard). **Named design point — the termination + design**: catch-propagate-terminate (every error path between + begin and commit terminates the tx before propagating) per A1's + recorded options — an explicit abort verb would need a new ADR (the + seam shape is pinned; `pin_commit`'s terminal-consume reading + stays). Record the design; the zero-residue atomicity is already + the engine clause's contract (wave 4) — the driver's duty is never + abandoning the tx mid-flight. +- **The pin guard's fleet form** (ADR-008 §2: "the pin guard renews + its row every TTL/3"): the `Pin` guard returned by a fleet put + carries the **renewal duty** — while the guard lives, its pin row's + expiry moves forward every TTL/3 (via the engine's `pin_renew(key, + owner)` — the never-resurrect semantics: an expired row is never + revived; the guard's renewal failing after expiry is the + delete-then-recover backstop's territory, ADR-008's expiry + semantics verbatim). **Named design points — the renewal + lifecycle**: the duty's mechanism (a per-guard tokio task on a + TTL/3 interval vs a shared renewal sweep keyed off the guard + registry — decide, record), the abort-on-drop discipline (a + dropped/converted/batch-contributed guard stops its renewal — no + leaked tasks, no renewal past the guard's life), and the failure + posture (a failed renewal is logged-and-continued or silent — the + row's expiry is the protection, a missed renewal degrades to the + expiry semantics, never an error on the put path). The guard's + in-process refcount machinery continues to function (instance-local + bookkeeping — the batch's one-step release, the ordered-step + conversion); the row is the cross-instance protection. Batch puts + (`put_in_batch`): each member's joint tx commits its row; the + batch's one-step release ends the members' renewals (the rows + expire and the sweeper's maintenance step reaps them — + `store-fleet-sweep`). +- **The dedup re-put under fleet**: CAS dedup means no new entry — + the joint tx commits the pin row alone (an empty `joint_put` + contribution is the engine's recorded CAS form) and the row + upsert-renews `(key, owner)` (the wave-4 recorded shape). A + different instance's re-put is a different pin row — protection + composes by row (ADR-010 §2's refcount-in-rows reading). +- **`large = local` on declared shared media — pin-first, + publish-second** (ADR-012 §4): the joint tx is unachievable across + media, so the ordering is pinned: the pin row commits on the kv + engine (a joint tx carrying only the pin row — `begin_joint_tx` → + `pin_commit` without `joint_put`) **before** the stage file's + commit-rename publishes the entry. The invariant survives (an entry + becomes visible only after its pin row is durable — sweep + arbitration can never observe a visible-unpinned entry); the crash + window moves to the harmless side (an orphan pin row, reaped by the + expiry machinery — no new recovery mechanism). The reverse ordering + is rejected (ADR-012 §4) — do not implement it. +- **The pg-lo consolidation put** (kv = postgres + large = pg-lo, + same instance): the large entry's content reaches the store-held tx + through the pg-lo engine's joint contribution (`engine-pg-lo-joint`) + — LO writes + companion-row insert inside the tx, the pin-row + insert + commit via `pin_commit`. One tx over the shared instance; + the dispatch threshold routes to the joint form exactly as the + non-fleet path routes to the staged form. +- **Unit tests** (env-driven, skipping cleanly server-less; the + harness server `pglo-poc`, :15432, db `blobs`, `postgres`/`poc` — + verified live 2026-10-12): the kv joint put publishes entry + pin + row atomically (an independent connection sees both or neither); + the forced-failure zero-residue form (A1's discipline exercised — + a mid-driver failure leaves no dirty pooled session); the guard's + renewal moves the row's expiry (a short test TTL; the renewal + cadence observed); the never-resurrect form (an expired row is not + renewed back to life); the batch form (members' rows commit; batch + release ends renewals); the dedup re-put's upsert-renewal; the + local-shared-media pin-first ordering (the pin row is durable + before the committed file exists); the pg-lo consolidation + round-trip through the public `put` (both tiers, one tx — the + byte-exact large-entry form); the non-fleet paths unchanged (the + existing suites green). + +## Acceptance Criteria + +- [ ] Under `fleet: true`, kv-tier puts route through the joint tx + (entry + pin row in one step; an independent connection never + observes one without the other); under `fleet: false` the put + paths are byte-unchanged (all existing suites green) +- [ ] Amendment A1 landed: the driver terminates the tx on every path + from `begin_joint_tx` onward (the termination design recorded; + a forced mid-driver failure leaves zero residue and returns no + dirty session to the pool) +- [ ] The pin guard's fleet form: renewal every TTL/3 while the guard + lives; drop/conversion/batch-contribution stops the renewal + with no leaked tasks; a failed renewal never resurrects an + expired row (the lifecycle design points decided and recorded) +- [ ] The dedup re-put commits the pin row alone (CAS; upsert- + renewal); a different instance's re-put composes protection by + row +- [ ] `large = local` fleet puts: pin-row-first, publish-second + (contract-tested — the pin row is durable before the committed + file exists; the orphan-pin crash window is the expiry + machinery's) +- [ ] The pg-lo consolidation put round-trips through the public + `put` (one tx over the shared instance; byte-exact large-entry + form) +- [ ] `cargo test` (default) green; `cargo test --features postgres` + and `--features "postgres,pg-lo"` green server-less (skipping) + and against the harness server at acceptance; + `cargo clippy --all-targets -- -D warnings` and + `cargo fmt --check` green (default + the postgres/pg-lo + variants); `cargo doc --no-deps` clean + +## References + +- src/store.rs (the put seam's two internal paths; the fleet + declaration's flag from `store-fleet-construction`) +- src/gc.rs (`EngineFleetState` — the clauses the driver calls; + `PinRow`; the ownership rule) +- src/postgres.rs (`PgJointTx` — PG-2's hazard doc; the wave-4 + joint-clause bodies) +- src/pg_lo.rs (the joint contribution from `engine-pg-lo-joint`) +- docs/architecture/decisions/008-trait-size-probe-fleet-gc-and-large-engines.md + §2 (pins are rows; the TTL/3 renewal; the expiry semantics) +- docs/architecture/decisions/010-backend-io-seams-and-fleet-state-home.md + §2 (the store holds the tx; the engine contributes mechanics), §4 + (the guard-drops-at-response-time reading — the ops edition; the + public-API guard lives as long as the caller holds it) +- docs/architecture/decisions/012-pre-decomposition-consistency-rulings.md + §3 (activation), §4 (the host; pin-first/publish-second for + large=local; the one-tx consolidation) +- docs/architecture/decisions/013-family-composition-reactive-boundary-and-feature-graph.md + §1 (the renewal worker's cadence is embedder composition above the + facade — the guard's own TTL/3 duty is the in-crate half) +- tasks/review-wave-4.md (amendment A1 — this task is its owner) +- tasks/store-fleet-construction.md, tasks/engine-pg-lo-joint.md (the + declaration and the contribution this drives) + +## Notes + +> To be filled by implementation agent + +## Summary + +> To be filled on completion \ No newline at end of file diff --git a/tasks/store-fleet-sweep.md b/tasks/store-fleet-sweep.md new file mode 100644 index 0000000..d115377 --- /dev/null +++ b/tasks/store-fleet-sweep.md @@ -0,0 +1,189 @@ +--- +id: store-fleet-sweep +name: the fleet GC protocol — SQL sweep lease, SQL-level arbitration, pin reaping, the fleet liveness verb +status: pending +depends_on: [store-fleet-construction] +scope: broad +risk: high +impact: phase +level: implementation +tags: [wave-6, store, gc, fleet, sweep] +--- + +## Description + +Grow the sweep and the 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 architecture's most intricate state machine extended +across instances, its invariants unchanged (a visible pool entry is +never deleted while liveness protects it; an in-flight put is never +deleted by a sweep started before it committed). The non-fleet forms +(inline + durable, single-node) are byte-unchanged. + +- **The sweep lease** (ADR-013 §2's named exclusive semantics; the + shipped provider is the wave-4 advisory-lock clause): under fleet + mode the sweep acquires `sweep_lock_acquire()` on the fleet host + before sweeping and holds it for the whole sweep; contention is the + `GcAbortCause::SweeperLock` report abort (`Ok(SweepReport)`, nothing + deleted — ADR-012 §1); holdership ends at release, owner death, or + session loss (the clause's session-scoped semantics, wave 4). The + in-process single-flight flag composes (both held — the instance- + local guard stays cheap and uniform). **Named design point — the + lease's hold across the sweep body**: the lease's held checkout + must not deadlock the sweep's own engine work (the wave-4 lease + holds one pooled connection for its duration; the sweep's other + clauses ride further checkouts — verify the pool sizing composes + and record the posture). +- **SQL-level delete arbitration** (ADR-008 §2): under fleet mode the + delete-time decision is the seam's `arbitrate(key)` — the + `SELECT … FOR UPDATE` finger-lock folding un-expired pin rows + + registered liveness tables into one answer — on **both** the direct + `delete()` path (protected → `Error::GcRefuse`, the facade owns the + refusal) and the durable window's execution-time re-arbitration. + **Named design point — the two arbitration domains' composition**: + the in-process `GcHome` machinery (markers, in-process pins, + callbacks, in-process sources) and the SQL `arbitrate` both run — + the in-process domain guards this instance's in-flight state (the + marker mechanism closes the local sweep-vs-put race without engine + I/O under the lock), the SQL domain is the cross-instance + authority; decide the composition order (in-process first — cheap, + local; SQL second — fleet-authoritative) and record it. The + engine-side `pin_check` classification stays available to the store + core where a pins-only answer suffices (its consumer beyond + `arbitrate` may be wave 7's ops renewal probe — do not force it + here). +- **The expired-pin reaping maintenance step** (ADR-008 §2's GC-of-GC + cleanup: "an expired pin is abandoned … and is reaped by the + sweeper's maintenance step"): the fleet sweep gains the maintenance + step deleting expired pin rows. **Named design point — the clause + home**: `EngineFleetState` has no reap clause; the seam is the + named home for fleet-clause growth (ADR-010 §2: "If fleet clauses + ever grow, they grow here") — add the clause body on the postgres + host (e.g. `pin_reap(now) → count`, one statement, contract-tested + per the applicability row) rather than any raw-SQL side-channel + (rejected by ADR-010 §2). The step runs under the lease, before or + alongside the overdue execution (record the ordering). +- **The fleet liveness registration verb** (ADR-012 §6.6): "the same + verb with a fleet-shaped argument" — the facade's + `register_liveness_source` gains its fleet form, registering a + **named table reference** in the shared engine (the embedder's own + table per ADR-008 §2; the store core reads it, never owns it — the + schema-ownership rule). The engine-side registration landed wave 4 + (`PostgresBackend::register_liveness_source` — quoted-identifier + boundary, idempotent re-registration, malformed refs error). **Named + design points — the verb's shape**: how the fleet form rides the + public verb (a fleet-shaped argument vs a sibling method — decide + consistent with §6.6's "same verb" wording and the existing + signature's infallibility: the engine-side registration is + fallible, so the fleet form's error surface needs deciding), and + whether in-process `LivenessSource` registrations remain legal + under fleet mode (ADR-008: in-process callback views are not a + *fleet* liveness form — an instance-local registration's protection + is instance-local only; decide allow-and-document vs reject and + record it). Deregistration composes (the correction flow, ADR-012 + §6.4). +- **The protect callback's fleet posture** (ADR-008 §2): single-node- + only — under fleet mode the callback remains consultable by *this* + instance's sweeps (its protection is instance-local; the embedder's + fleet-wide protection materializes into its liveness table or pin + rows before invoking sweep). Record the posture in the verb docs; + no new machinery. +- **The fleet sweep body** (per sweep, under the lease): the protect + consult (instance-local, abort `ProtectFailed` precedes everything) + → the maintenance step (expired-pin reaping) → **execute overdue** + (`window_take` on the postgres host — the one-window-host rule + holds: every fleet-valid configuration's window hosts on the kv + engine; each overdue candidate re-arbitrated at execution time via + the SQL arbitration, deleted where it lives — the fall-through + delete across both tiers) → **mark** (the pool enumerated; the live + set from the in-process sources — the SQL liveness tables are + consulted at arbitration time, not enumerated into the mark set) → + **stage** at `visible_after = mark_end + window` → release the + lease. Report counts exact relative to the schedule (the durable + form's reading carries: staged-this-sweep vs executed-this-sweep are + different sets). +- **The sweep-vs-put race under fleet, closed by the row**: a fleet + put's pin row commits atomically with its entry (the joint tx, + `store-fleet-put`), so a sweep's window execution can never observe + a visible-unpinned entry; a put landing during the window delay has + its row committed before its entry is visible and the execution-time + SQL arbitration cancels the candidate. Unit-test the race across + the delay in its fleet form (the wave-3 durable-form race test's + fleet edition). +- **Unit tests** (env-driven, skipping cleanly server-less; the + harness server `pglo-poc`, :15432, db `blobs`, `postgres`/`poc` — + verified live 2026-10-12): the lease's fleet form (contention + between two instances of one store shape → the second aborts + `SweeperLock` with nothing deleted; release/loss reaping); SQL + arbitration on the direct-delete path (a pin row from a *foreign* + owner id refuses the delete — the cross-instance protection proof; + `GcRefuse`); the maintenance step reaps exactly the expired set + (un-expired rows survive); the fleet liveness verb registers a + test-owned embedder table and its rows protect (the schema- + ownership rule — the engine never creates the table); the + two-instance exact-count sweep shape (instance A pins, instance B + sweeps — the pinned entry survives; unpinned twins delete); the + race across the delay; the non-fleet forms unchanged (all existing + suites green). + +## Acceptance Criteria + +- [ ] Under `fleet: true`, the sweep acquires the SQL lease (the + ADR-013 §2 semantics: atomic test-and-set, one sweeper per + pool across instances, contention = `SweeperLock` report abort + with nothing deleted); the in-process flag composes; under + `fleet: false` the sweep forms are byte-unchanged +- [ ] Delete-time arbitration is SQL-level under fleet mode on both + the direct path (`GcRefuse`) and the window execution + (execution-time re-arbitration); a foreign owner's un-expired + pin row protects (cross-instance proof); the two-domain + composition decided and recorded +- [ ] The expired-pin reaping maintenance step landed as a new + `EngineFleetState` clause on the postgres host (the seam is the + named home; no raw-SQL side-channel), contract-tested, run + under the lease +- [ ] The fleet liveness registration verb landed (the named table + reference form; the verb-shape and in-process-source design + points decided and recorded; the schema-ownership rule holds) +- [ ] The protect callback's fleet posture recorded (instance-local; + materialization is the embedder's fleet-wide duty) +- [ ] The fleet race property unit-tested across the window delay (a + put landing during the delay survives via the row + the + execution-time SQL re-arbitration) +- [ ] `cargo test` (default) green; `cargo test --features postgres` + green server-less (skipping) and against the harness server at + acceptance; `cargo clippy --all-targets -- -D warnings` and + `cargo fmt --check` green (default + `--features postgres`); + `cargo doc --no-deps` clean + +## References + +- src/store.rs (the sweep's two forms; `arbitrated_delete`; the + fleet flag from `store-fleet-construction`) +- src/gc.rs (`GcHome` — the in-process domain; `EngineFleetState` — + the clauses; the seam-as-named-home note) +- src/postgres.rs (the lease, `arbitrate`, `pin_check` — the wave-4 + clause bodies this drives; the liveness registration) +- docs/architecture/decisions/008-trait-size-probe-fleet-gc-and-large-engines.md + §2 (the fleet mechanism: SQL arbitration, one sweeper per pool, the + maintenance step, the protect callback's single-node-only posture) +- docs/architecture/decisions/013-family-composition-reactive-boundary-and-feature-graph.md + §2 (the named exclusive sweep lease — the semantics this implements) +- docs/architecture/decisions/012-pre-decomposition-consistency-rulings.md + §1 (abort-as-data), §5 (the one-window-host rule), §6.4 (the full + protection set), §6.6 (the fleet liveness verb's API home), §6.7 + (the executor is the next sweep) +- docs/architecture/decisions/010-backend-io-seams-and-fleet-state-home.md + §2 (the seam clauses; the raw-SQL rejection) +- tasks/store-sweep-durable-window.md (the durable form's shape — the + single-node structural twin) +- tasks/store-fleet-put.md (the joint puts whose rows this sweep + arbitrates against) + +## Notes + +> To be filled by implementation agent + +## Summary + +> To be filled on completion \ No newline at end of file diff --git a/tasks/suite-column-fleet.md b/tasks/suite-column-fleet.md new file mode 100644 index 0000000..d24138a --- /dev/null +++ b/tasks/suite-column-fleet.md @@ -0,0 +1,150 @@ +--- +id: suite-column-fleet +name: Conformance suite — the fleet column (exact-count fleet sweep outcomes under concurrency) +status: pending +depends_on: [store-fleet-put, store-fleet-sweep] +scope: moderate +risk: medium +impact: phase +level: implementation +tags: [wave-6, suite, fleet, postgres] +--- + +## Description + +Grow the **fleet column** — the suite's proof that the architecture's +most intricate state machine holds under concurrency (the wave's +gate: "exact-count fleet sweep outcomes against live postgres", +docs/plans/implementation.md §Wave 6). The column runs under +`--features postgres` (fleet mode requires kv = postgres — the +composite predicate), rides the SKIP posture (server-less runs skip +cleanly; acceptance requires green **against the server**), and +extends the factory contract to the fleet shape: **two store +instances over one shared pool**. + +- **The fleet factory** (`tests/conformance/fleet_column.rs`): the + postgres column's schema-per-open pattern adapted — a fleet factory + mints **one schema** and vends **two (or more) `Store` instances + over it**, each `fleet: true`, each with its own owner id (the + instances share the pool; the pin rows compose by owner — the + fleet's actual definition, ADR-011/ADR-012 §3). Teardown drops + exactly the minted schemas (`DROP SCHEMA … CASCADE`, the postgres + column's registry pattern — panicking-row-safe, idempotent, + owned). The test tuning: window delay `0` (execute-on-next-sweep) + and a **short test TTL** (the fleet constructor parameter — the + renewal/expiry rows need observable cadences; the TTL is a + constructor parameter, not API, so the column declares its tuning). + A kv-only fleet factory (the re-routing posture's client shape) and + a consolidation fleet factory (kv = postgres + large = pg-lo, same + instance) join as the column's second/third shapes. +- **The fleet-validity rows** (the predicate at the constructor): + every fleet-valid configuration constructs; every non-fleet-valid + configuration is a constructor error (kv ≠ postgres, `mem` + anywhere, `local` without the shared-media declaration, pg-lo + cross-instance) — the predicate's clause-per-row form. +- **The joint entry+pin tx rows** (through the public `put`, not the + seam): entry + pin row commit together (an independent connection + sees both or neither); the dedup re-put upsert-renews the row; a + second instance's re-put composes a second row (protection by + row); the pg-lo consolidation round-trip (one tx over the shared + instance, byte-exact large-entry form). +- **The TTL/renewal rows**: the guard's TTL/3 renewal moves the row's + expiry (the short test TTL makes the cadence observable); a guard + dropped past expiry never resurrects its row (the never-resurrect + semantics through the facade); a paused holder (renewal stopped) + loses protection at expiry — the delete-then-recover backstop (a + byte-identical re-put recovers). +- **The exact-count fleet sweep rows** (the wave's gate — INV-4's + engine-gated fleet form, store-api.md §Invariants 4: "fleet + postures assert it under DB-backed pins + the advisory-locked + sweep, with the exact-count sweep-outcome shape run against + dockerized postgres"): + - the **two-instance exact-count shape**: instance A pins an entry + (its row), instance B sweeps — the pinned entry survives, the + unpinned twins delete, counts exact against the staged schedule; + - the **lease contention row**: two instances sweep concurrently — + exactly one sweeps, the other aborts `SweeperLock` as report + data with nothing deleted; + - the **cross-instance arbitration row**: a foreign owner's + un-expired pin row refuses instance B's direct delete + (`GcRefuse`); after the row expires (renewal stopped, short TTL), + the delete proceeds; + - the **race across the window delay** in its fleet form: a put + landing during the delay (its row committed before its entry is + visible) survives the next sweep's execution-time SQL + re-arbitration; + - the **maintenance-step row**: expired rows reaped exactly (the + GC-of-GC cleanup), un-expired rows survive; + - the **fleet liveness row**: a test-owned embedder table's rows + protect through the fleet registration verb (the schema-ownership + rule asserted — the engine never created the table). +- **The kv-only re-routing row**: the fleet kv-only client shape + rejects over-threshold puts at put time (ADR-012 §6.2's machinery, + fleet edition) — the re-routing posture's store-side half. +- **README amendments** (the convention's normative test-side home): + the fleet rows table in §Columns beyond the reference set; the + column plan's SKIP-posture note naming the fleet column; current + state. +- **Connection settings ride the environment** — the established + harness convention: `ALKBLOBS_PG_HOST/PORT/USER/PASSWORD/DB`, the + reachability probe gating the rows. **The harness server**: + dockerized `pglo-poc` (`postgres:16-alpine`) on :15432, db `blobs`, + user/password `postgres`/`poc` — verified live 2026-10-12; the + container is long-running in the dev environment. Concurrency rows + are deterministic by construction (the lease/arbitration outcomes + are decided by the server, not by timing) — where a row needs + ordering, the window delay and the TTL tunings provide it; no + sleep-based assertions. + +## Acceptance Criteria + +- [ ] The fleet column green **against the harness server** + (`pglo-poc`, :15432): the fleet-validity rows, the joint-tx + rows, the TTL/renewal rows, the exact-count fleet sweep rows + (two-instance survival, lease contention, cross-instance + arbitration, the race across the delay, the maintenance step, + the fleet liveness row), and the kv-only re-routing row — every + row ADR-stamped and traceable to the backlog +- [ ] The fleet factory contract holds: one schema, two+ instances + over it (distinct owner ids), teardown drops exactly the minted + schemas, panicking-row-safe — structurally exercised +- [ ] Server-less runs skip cleanly (the reachability probe; the + default-build gates stay green with the column skipped) +- [ ] README amendments recorded (the fleet rows table; current + state) +- [ ] `cargo test` (default) green — the column compiles out; + `cargo test --features postgres` green server-less (skipping) + and green against the server at acceptance; + `cargo test --all-features` green (the consolidation factory's + home); `cargo clippy --all-targets -- -D warnings` and + `cargo fmt --check` green (default + the postgres/pg-lo + variants + `--all-features`) + +## References + +- tests/conformance/README.md (the conventions this grows against; + §The factory contract, §The SKIP posture, §The schedule-relative + reading, §Columns beyond the reference set) +- tests/conformance/postgres_column.rs (the SQL-backed column pattern + — the schema-per-open factory this extends to the fleet shape) +- tests/conformance/factory.rs (the factory contract) +- docs/architecture/store-api.md (§Invariants 4 — the engine-gated + fleet form this column asserts) +- docs/architecture/decisions/008-trait-size-probe-fleet-gc-and-large-engines.md + §2 (the fleet mechanism the rows assert) +- docs/architecture/decisions/012-pre-decomposition-consistency-rulings.md + §3 (the declaration), §4 (the host), §6.6 (the fleet liveness verb) +- docs/architecture/decisions/013-family-composition-reactive-boundary-and-feature-graph.md + §2 (the lease semantics the contention row asserts) +- docs/architecture/decisions/007-two-kv-engines-sqlite-and-postgres.md + §5 (the exact-count sweep-outcome gate against dockerized postgres) +- tasks/store-fleet-put.md, tasks/store-fleet-sweep.md, + tasks/store-fleet-construction.md (the machinery the rows exercise) + +## Notes + +> To be filled by implementation agent + +## Summary + +> To be filled on completion \ No newline at end of file