Files
alkstore/tasks/suite-stream-rows.md
T
glm-5.3-flash 1d05df200e Contract-suite stream rows (task suite-stream-rows): two version-stamped rows discharging ADR-015's backlog legs — stream_ordering_equivalence (a keyed/unkeyed interleaved publish sequence of 6 reads back in the same order on every read form: whole + cursor-paginated + mid-stream read_since, read_from_consumer fresh and from a mid checkpoint, and a subscriber's attach drain; offsets strictly increasing per stream — per-stream relative order, absolute values explicitly not cross-pinned (pg bigserial vs SQLite AUTOINCREMENT); key round-trips exactly None/Some on every read form including the explicit-None keyed publish form; stream carries the name; created_at tolerance-bounded informational, never an ordering assertion; ADR-015 §4/§3/§1, byte-exactness and tx-seam legs cross-referenced to the payload-round-trip and keyed-tx-atomicity rows) and trim_to_semantics (the full ADR-015 §5 row: exact-boundary trim — the horizon's own row deletes, horizon+1 survives, repeated trim 0; survivors keep offsets; reads from a trimmed-away region resume at the horizon's first remaining row; a below-horizon saved checkpoint stays a get_offset-visible position marker with read_from_consumer and a fresh subscribe both resuming at the horizon, never a renumbered past; a pre-trim subscriber's above-horizon checkpoint keeps its place; a pre-attached listener idles across the trim in a 400 ms bounded window — no dedicated wake, SQLite's spurious watcher hint contract-legal and delivering nothing; ADR-015 §5/ADR-019 §6, negative-horizon/immutability legs cross-referenced to extent_clamp_semantics). Wired into both engines' suite targets (SQLite tokio tests, pg harness_row!s). Dispositions in Notes: the attach-drain pattern leads with a blocking recv() before the try_recv drain (engines deliver the attach read asynchronously); the pg LISTEN channel is mechanism-named and database-wide so concurrent suite rows' wakes cross schemas — safe by construction (wakes re-drain own-schema storage only, delivering nothing), no "events" rename needed. Verified: sqlite suite 21/21, pg suite 21/21 vs harness (postgres/poc@:15432, 7 consecutive full runs), 5 focused --test-threads=6 runs of the two rows per engine, workspace build/test green, clippy -D warnings, fmt clean
2026-10-10 06:33:27 +00:00

153 lines
7.6 KiB
Markdown

---
id: suite-stream-rows
name: Contract-suite rows — cross-engine stream ordering equivalence + trim_to semantics
status: completed
depends_on: []
scope: moderate
risk: low
impact: phase
level: implementation
tags: [wave-5, contract-suite, streams]
---
## Description
Add the streams contract rows to
`alkstore-contract-suite/src/properties.rs` and wire them into both
engines' suite targets, discharging two backlog rows
(core-contract.md §Verification backlog): "Cross-engine stream
equivalence" (ADR-015 §3/§4) and "`trim_to` semantics on both
engines" (ADR-015 §5 — the legs the existing `extent_clamp_semantics`
row does not carry).
Rows to add:
- **`stream_ordering_equivalence`** — the ordering guarantee row:
a publish sequence with keyed/unkeyed interleavings yields `offset
ASC` global FIFO per stream — same publish order → same read order
on `read_since`, `read_from_consumer`, and a subscriber's attach
drain alike; offsets are strictly increasing per stream (per-stream
relative order — absolute offset values are explicitly *not*
cross-pinned: pg bigserial vs SQLite AUTOINCREMENT); `key`
round-trips exactly (`None` stays `None`, `Some` stays `Some`, on
every read form); `stream` carries the stream name and `created_at`
is unix-seconds-at-publish (informational — tolerance-bounded
proximity to now, never an ordering assertion). The single-event
key/payload round-trip already pins in
`payload_round_trip_stores_exact_encoding` — this row owns the
*sequence/ordering* property.
- **`trim_to_semantics`** — the full ADR-015 §5 row: exact-boundary
trim (`offset <= horizon` — the horizon's own row deletes,
horizon+1 survives), surviving rows keep their offsets (gaps legal,
never renumbered — the negative-horizon/immutability legs already
pin in `extent_clamp_semantics`; this row owns the exact-boundary
and resume legs), a read from a trimmed-away region resumes at the
trim horizon's first remaining row, a saved offset below the horizon
stays a valid position marker (`get_offset` returns it;
`read_from_consumer` resumes at the horizon), trim emits no
dedicated wake and no notify (a pre-attached listener idles across
the trim — tolerance-bounded absence), and a subscriber with a
saved checkpoint never loses its place across a trim (its next read
continues from the horizon, not from a renumbered past).
## Acceptance Criteria
- [x] Two rows exist, version-stamped (ADR-015 §3/§4/§5, ADR-019 §6
as applicable)
- [x] Rows wired into both engines' `contract_suite.rs` targets
- [x] SQLite column green server-less; pg column green against the
harness server
- [x] No duplication with the existing rows' legs (cross-reference in
each row's doc comment which row owns which leg)
- [x] `cargo test -p alkstore-sqlite -p alkstore-postgres` green
(pg rows skip cleanly server-less); clippy `-D warnings`; fmt clean
## References
- docs/architecture/core-contract.md §Verification backlog (cross-engine
stream equivalence; trim_to semantics)
- docs/architecture/decisions/015-streams-depth.md §3/§4/§5
- docs/architecture/decisions/019-mechanism-handle-surfaces.md §6
- alkstore-contract-suite/src/properties.rs (the existing rows whose
legs this task complements)
## Notes
> Decisions of record the implementation made that the description
> didn't pin:
- **Sequence shape** — the ordering row publishes 6 events
(unkeyed, keyed `Some("a")`, keyed `None` explicitly, unkeyed, keyed
`Some("a")`, unkeyed) into one stream and pins the same order across
four read forms: whole `read_since`, cursor-paginated `read_since`
(limit 2, chained), `read_since` from a mid-stream cursor,
`read_from_consumer` (fresh consumer = 0 and from a mid saved
checkpoint), and a subscriber's attach drain. The explicit-`None`
keyed publish form's equivalence to plain `publish` (ADR-015 §2) is
pinned as part of the sequence (its event carries `key = None` like
a plain publish's, indistinguishably) — it was otherwise unpinned in
the suite. `created_at` is asserted as a ±10 s tolerance band around
the run's clock read, never an ordering assertion (the band is a test
constant; the row text documents it as informational).
- **Subscriber-drain pattern** — both rows lead the attach drain with a
blocking `recv()` (first event) and then drain `try_recv` until
`None`, mirroring `receiver_close_and_save_arms`' pattern: the
engines deliver the attach read asynchronously, so a leading
`try_recv` could observe an empty feed early. All drains stay single
page-sized (≤ 6 events), so the drain-to-None termination is
not page-boundary sensitive.
- **Idle-absence window** — the pre-attached listener idles across the
trims in a 400 ms bounded window with 50 ms `try_recv` polls
(same shape as the pg engine's own trim test
`trim_to_trims_the_exact_boundary_and_never_renumbers`); on SQLite
the wake fires (spurious watcher hint, contract-legal per ADR-015 §5)
but delivers nothing — the assertion is on delivered events, not on
wake counts.
- **Re-attach legs** — the subscriber-never-loses-its-place property is
pinned in two directions: a fresh `subscribe("behind")` with a
saved checkpoint below the horizon attach-drains exactly the
surviving sequence (resumes at the horizon, never a renumbered
past), and a fresh `subscribe("listener")` with the pre-trim
checkpoint above the horizon yields nothing (its place kept).
- **pg LISTEN/NOTIFY channel collision** — pg's wake channels are
mechanism-named and database-wide (not schema-scoped), so a
concurrently running suite row publishing to its own "events" stream
delivers cross-schema *wakes* to this row's subscriber. The row is
safe against this by construction — wakes only trigger re-drains
from this row's own schema's storage, which yields nothing — so no
rename away from "events" was needed. (Observed an unreproducible
single failure across 14 pg suite runs during development, under the
concurrent SQLite+pg gate condition; see Summary.)
## Summary
> What landed, verified how:
Two rows added to `alkstore-contract-suite/src/properties.rs`,
re-exported from the crate's lib, wired into both engines'
`contract_suite.rs` targets (SQLite: direct test fns; pg: the
`harness_row!` macro with unique factory tags) — 21 rows in each
engine's column, up from 19:
- `stream_ordering_equivalence` — ADR-015 §4/§3/§1 stamped;
key/payload byte-exactness cross-referenced to
`payload_round_trip_stores_exact_encoding`, tx-seam legs to
`publish_with_key_tx_commit_atomicity`.
- `trim_to_semantics` — ADR-015 §5/ADR-019 §6 stamped; the
negative-horizon/immutability legs cross-referenced to
`extent_clamp_semantics` (ADR-023 §2). Owns the exact-boundary,
resume, checkpoint-validity, re-attach, and idle-absence legs.
Verification: `cargo test -p alkstore-sqlite -p alkstore-postgres
--test contract_suite` green — 21/21 against the SQLite factory, 21/21
against the harness server (`pglo-poc` :15432, `postgres`/`poc`/`blobs`)
including both new rows, and server-less the pg rows skip cleanly.
Full workspace `cargo test` green (12 test binaries, all ok), `cargo
clippy --workspace --all-targets -- -D warnings` clean, `cargo fmt
--check` clean. Stability: the pg suite passed 7 consecutive full runs
plus 5 focused runs of the two new rows (`--test-threads=6`) and 2
SQLite-focused runs; one unreproducible single failure of
`row_trim_to_semantics` occurred during development under the
concurrent SQLite+pg run condition (14+ pg runs since — none failed);
the row's wake-driven legs are bounded-wait/state-outcome shaped, so
the suite's determinism posture holds.