decompose wave 5 — contract suite: audit-first split of the verification backlog (engines' columns already discharge most rows; map in review-wave-5's appendix), seven mechanism-grouped suite-row tasks (queue depth, scheduler, tx commit-atomicity, streams, locks, wakes, opts/backoff equivalence), two engine-side hardening tasks (sqlite commit-error arm, pg F-1 defense-in-depth), and the review-wave-5 gate that flips the engine specs to stable
This commit is contained in:
1 parent
9a00fb8a7e
commit
250511d480
11 files changed
+911
-2
No files matched your search
@@ -1,6 +1,6 @@
|
||||
---
|
||||
status: draft
|
||||
last_updated: 2026-10-09 (pg-fix-tx-wake landed — the second HIGH fix, retiring F-1's engine arm; wave 5's remaining gate is the fix batch's review gate)
|
||||
last_updated: 2026-10-10 (wave-4 fix batch landed and gate-verified — review-wave-4-fixes passed; wave 5 decomposed)
|
||||
---
|
||||
|
||||
# alkstore — Implementation plan
|
||||
@@ -36,7 +36,7 @@ parallel).
|
||||
| [2](#wave-2--sqlite-substrate-fork) | honker-core fork into `alkstore-sqlite/src/substrate/`: port, deltas, provenance, test floor | wave 1 (workspace scaffold only) | implemented + reviewed (2026-10-08) |
|
||||
| [3](#wave-3--sqlite-engine) | SQLite engine: connection architecture, re-derived queue ops on contract v1, scheduler/outbox, tx seam, SQLite backlog column | waves 1 + 2 | implemented + reviewed (2026-10-08) |
|
||||
| 4 | [Postgres engine](#wave-4--postgres-engine): schema bootstrap, pool/open, listener/forwarder, all mechanisms, tx seam, pg backlog column | wave 1 | implemented + reviewed (2026-10-08) |
|
||||
| 5 | Contract suite: the cross-engine equivalence properties (core-contract.md §Verification backlog), version-stamped per ADR-017 | waves 3 + 4 | not yet decomposed |
|
||||
| 5 | [Contract suite](#wave-5--contract-suite): the cross-engine equivalence properties (core-contract.md §Verification backlog), version-stamped per ADR-017 | waves 3 + 4 | decomposed (2026-10-10) |
|
||||
| 6 | Release readiness: crate docs, deployment matrix final pass, README (written last, honestly), publish prep; mem-engine and fuzzing decisions | wave 5 | not yet decomposed |
|
||||
|
||||
## Wave 1 — Foundations
|
||||
@@ -181,6 +181,69 @@ natural realization — the mechanism name *is* the wake channel
|
||||
streams), legal by construction under the shared namespace — with
|
||||
the reserved reconnect-wake channel staying the only reserved string.
|
||||
|
||||
## Wave 5 — Contract suite
|
||||
|
||||
Filling the scaffolded suite (`alkstore-contract-suite`) with the
|
||||
remaining verification-backlog rows and running the compatibility
|
||||
gate. Waves 3 and 4 each landed a nine/ten-row backlog column; the
|
||||
wave-5 decomposition started from an audit of what those columns
|
||||
already discharge versus what the backlog still lacks.
|
||||
|
||||
**Already discharged by the engines' columns** (the wave-5 review
|
||||
gate verifies each claim by reading the pinning row, not by trusting
|
||||
the audit): name validation (the exemplar, incl. `schedule()`'s
|
||||
queue-argument validation), the numeric-domain rows (extents +
|
||||
durations), `encode_payload` typed failure + round-trip, the
|
||||
`PayloadTooLarge` occurrence asymmetry (both engine-scoped arms),
|
||||
drop-=-rollback no-ghosts, in-tx read-your-own-writes (which also
|
||||
discharges the `save_offset_tx` exactly-once backlog row across
|
||||
itself and the drop-rollback row), receiver close/save arms + monotone
|
||||
composition (both columns), and — pinned engine-side rather than as
|
||||
suite rows, per the sqlite-engine-integration task's scoping call —
|
||||
plain-path SQLite open and the watcher-cadence knob
|
||||
(`open_tests.rs`; SQLite-scoped backlog rows).
|
||||
|
||||
**Wave 5's task set** (the gaps):
|
||||
|
||||
- Queue depth: the job-handle validity predicate, the ADR-010 depth
|
||||
properties (reclaim-eats-attempt, dead-letter moves, the
|
||||
no-stranded-rows sweep), and M-1's retention-failure pin
|
||||
(`suite-queue-depth-rows`).
|
||||
- Scheduler: boundary fires, bounded catch-up, leadership discipline —
|
||||
with a documented extension of the suite's determinism posture to
|
||||
admit runner-driving rows (`suite-scheduler-rows`).
|
||||
- Tx seam: `outbox_enqueue_tx` and `publish_with_key_tx`
|
||||
commit-atomicity, plus N-5's `with_tx` panic-disposition probe —
|
||||
the panic path pinned against real engines for the first time
|
||||
(`suite-tx-commit-atomicity-rows`).
|
||||
- Streams: the cross-engine ordering-equivalence row and the full
|
||||
`trim_to` semantics row (`suite-stream-rows`).
|
||||
- Locks: TTL/expiry re-acquisition and the concurrent-`try_lock`
|
||||
loser posture — the SQLite busy-path is the open question the row
|
||||
exists to answer (`suite-lock-rows`).
|
||||
- Wakes: the pinned `WakeReceiver` shapes, tolerance-bounded
|
||||
(`suite-wake-rows`).
|
||||
- Arithmetic equivalence: the backoff-curve row and the two
|
||||
wall-clock-adjacent opts-resolution legs the existing row deferred
|
||||
to this wave (`suite-opts-backoff-rows`).
|
||||
- Engine-side hardening riding the window: the wave-3 review's
|
||||
deferred SQLite commit-error-arm coverage (`sqlite-commit-error-arm`)
|
||||
and F-1's defense-in-depth candidates — explicitly not load-bearing
|
||||
since `pg-fix-tx-wake` retired the flake's root cause
|
||||
(`pg-suite-infra-hardening`).
|
||||
- The review gate (`review-wave-5`): the backlog discharge audit,
|
||||
stamp verification, green-on-both-engines, the parked-disposition
|
||||
audit — and the gate that flips `engine-sqlite.md` /
|
||||
`engine-postgres.md` to `stable`.
|
||||
|
||||
Judgment calls the decomposition parked in task Notes (the review
|
||||
gate audits each): where the M-1 retention-failure pin, the
|
||||
beyond-64-cap skip-forward leg, and the backoff 1-hour-cap leg land
|
||||
when the public surface cannot express them (engine-side is the
|
||||
expected answer, recorded per case); whether the scheduler rows
|
||||
demand the pg fire-wake parity the wave-4 fix-batch gate recorded;
|
||||
and what the SQLite lock busy-path actually returns under contention.
|
||||
|
||||
## Decided points
|
||||
|
||||
- **Contract-suite layout — option (a)**: a small internal
|
||||
@@ -261,6 +324,12 @@ decomposes. Specific gates:
|
||||
`tasks/review-wave-4.md`) and making `tx_publishes_compose_with_the_handle`
|
||||
deterministic (20 consecutive solo runs green); the remaining fixes
|
||||
ride the wave-4 fix-batch decomposition below.
|
||||
- **Wave 5 decomposition** (2026-10-10) — audit-first: the engines'
|
||||
backlog columns already discharge most of the §Verification backlog
|
||||
(map recorded in `tasks/review-wave-5.md`'s appendix for the gate to
|
||||
verify); the wave fills the gaps with seven suite-row tasks grouped
|
||||
by mechanism, two engine-side hardening tasks, and the gate. See the
|
||||
Wave 5 section.
|
||||
- **Wave 4 decomposition** (2026-10-08) — shaped as wave 3's
|
||||
structural twin; wave-3 outcomes absorbed (constructors exist;
|
||||
arithmetic re-owned per engine; per-engine `@every` parser;
|
||||
|
||||
@@ -0,0 +1,63 @@
|
||||
---
|
||||
id: pg-suite-infra-hardening
|
||||
name: Postgres test-infra hardening — F-1's defense-in-depth candidates
|
||||
status: pending
|
||||
depends_on: []
|
||||
scope: single
|
||||
risk: low
|
||||
impact: isolated
|
||||
level: implementation
|
||||
tags: [wave-5, postgres-engine, tests]
|
||||
---
|
||||
|
||||
## Description
|
||||
|
||||
Land the defense-in-depth test-infra hardening the wave-4 review
|
||||
recorded for F-1 (`tasks/review-wave-4.md` Notes) — **explicitly not
|
||||
load-bearing since `pg-fix-tx-wake` retired F-1's engine-side root
|
||||
cause** (the compose test is deterministic, 20/20 solo runs green at
|
||||
the fix-batch gate). This task exists because wave 5 was the named
|
||||
home ("wave 5's suite hardening, or a wave-5-adjacent test-infra
|
||||
task") and the candidates are cheap; if the wave-5 window gets tight,
|
||||
cancelling this task loses nothing but insurance.
|
||||
|
||||
Work (the review's candidate dispositions, cheapest first):
|
||||
|
||||
- **(a) Harden `must_recv_event`'s deadline** — replace the 20 ms
|
||||
polling loop with a parked `recv().await` under a timeout wrapper;
|
||||
the deadline assert still bounds the property, the poll-loop's
|
||||
wake-subscription interaction surface goes away.
|
||||
- **(b) A `LOG_LAGGED`-style diagnostic counter on the bridge** —
|
||||
discriminates "wake never arrived" from "wake arrived, re-drain
|
||||
missed" if a wake-shaped flake ever recurs. Land only if it is a
|
||||
small, honest addition (a test-observation counter, `#[cfg(test)]`-
|
||||
gated or eprintln-postured per the house test style); skip and
|
||||
record if it wants more machinery than that.
|
||||
|
||||
Candidate (c) (leave the skip-posture as-is) is the do-nothing arm —
|
||||
adopted by default for anything not landed.
|
||||
|
||||
## Acceptance Criteria
|
||||
|
||||
- [ ] `must_recv_event` parks on `recv().await` under a timeout
|
||||
wrapper (no polling loop); the pg stream tests stay green
|
||||
against the harness server (a full stream-module run)
|
||||
- [ ] Candidate (b) either landed small or recorded as skipped with
|
||||
the reason
|
||||
- [ ] `cargo test -p alkstore-postgres` green (server-less skips
|
||||
clean); clippy `-D warnings`; fmt clean
|
||||
|
||||
## References
|
||||
|
||||
- tasks/review-wave-4.md (F-1's Notes — the candidate dispositions)
|
||||
- tasks/review-wave-4-fixes.md (F-1 retired; defense-in-depth posture)
|
||||
- tasks/pg-fix-tx-wake.md (the engine-side fix that made this
|
||||
non-load-bearing)
|
||||
|
||||
## Notes
|
||||
|
||||
> To be filled by implementation agent
|
||||
|
||||
## Summary
|
||||
|
||||
> To be filled on completion
|
||||
@@ -0,0 +1,127 @@
|
||||
---
|
||||
id: review-wave-5
|
||||
name: Wave 5 review gate — the suite as compatibility instrument
|
||||
status: pending
|
||||
depends_on: [suite-queue-depth-rows, suite-scheduler-rows, suite-tx-commit-atomicity-rows, suite-stream-rows, suite-lock-rows, suite-wake-rows, suite-opts-backoff-rows, sqlite-commit-error-arm, pg-suite-infra-hardening]
|
||||
scope: broad
|
||||
risk: low
|
||||
impact: project
|
||||
level: review
|
||||
tags: [wave-5, review-gate]
|
||||
---
|
||||
|
||||
## Description
|
||||
|
||||
The wave-5 review gate (docs/plans/implementation.md §Review gates):
|
||||
"the suite as compatibility instrument — every backlog row present,
|
||||
version-stamped, green on both engines; this is the gate that flips
|
||||
the engine specs to `stable`."
|
||||
|
||||
Primary lenses:
|
||||
|
||||
1. **Backlog audit — every row present.** Walk
|
||||
core-contract.md §Verification backlog item by item against the
|
||||
suite (and the engine-side pins for the SQLite-scoped rows) and
|
||||
record the discharge map. The decomposition's audit (below) is the
|
||||
starting point — verify each "discharged" claim by reading the
|
||||
pinning row/test, not by trusting the map.
|
||||
2. **Version stamps.** Every row's `Contract stamp:` items cite ADR §
|
||||
per `version_stamp.rs`'s convention; stamps accumulate on
|
||||
amendment (the extended `enqueue_opts_resolution` row is the
|
||||
amendment case to check); greppable via `STAMP_MARKER`. Cross-check
|
||||
against ADR-017 §2's no-silent-change discipline: every stamp's
|
||||
cited ADR § actually pins the behavior tested.
|
||||
3. **Green on both engines.** The SQLite column green server-less;
|
||||
the pg column green against the harness server (twice, the
|
||||
wave-4 gate's posture). Workspace gates green
|
||||
(build/test/clippy/fmt).
|
||||
4. **Dispositions audited.** The decomposition parked several
|
||||
judgment calls in task Notes — verify each was made and recorded:
|
||||
the M-1 retention-failure pin's location (suite row vs engine-side),
|
||||
the beyond-64 skip-forward leg's location, the backoff cap leg's
|
||||
location, the scheduler fire-wake parity call, the lock-row
|
||||
divergence call (if any), the SQLite reconnect-success test
|
||||
taken/not-taken.
|
||||
5. **Conformance spot-checks.** The rows assert what the ADR text
|
||||
pins (no weaker, no stronger — a row pinning beyond the ADR text
|
||||
is a suite defect: it would fail a correct engine); the
|
||||
determinism posture holds (tolerance-bounded state assertions
|
||||
only, no timing-value asserts, no two-task races) including the
|
||||
scheduler rows' documented posture extension; no duplication
|
||||
between rows' legs.
|
||||
6. **Flip the engine specs to `stable`** — `engine-sqlite.md` and
|
||||
`engine-postgres.md` frontmatter `status: draft` → `status:
|
||||
stable`, with dated `last_updated` annotations citing this gate.
|
||||
Update `docs/plans/implementation.md`: the wave table's wave-5
|
||||
status, a review-rounds entry for this gate.
|
||||
|
||||
## Acceptance Criteria
|
||||
|
||||
- [ ] Backlog discharge map recorded (every §Verification backlog item
|
||||
→ its pinning row/test or an explicit gap with a disposition)
|
||||
- [ ] Every row version-stamped per convention; amendment stamps
|
||||
accumulated; stamps verified against the cited ADR text
|
||||
- [ ] Suite green on both engines (SQLite server-less; pg vs harness,
|
||||
two consecutive full runs); workspace gates green
|
||||
- [ ] All parked dispositions from the wave-5 tasks audited and
|
||||
recorded
|
||||
- [ ] Engine specs flipped to `stable` with dated annotations;
|
||||
implementation.md updated (wave table + review rounds)
|
||||
- [ ] Findings recorded; wave 6 decomposition may proceed
|
||||
|
||||
## References
|
||||
|
||||
- docs/plans/implementation.md §Review gates (wave 5), §The waves
|
||||
- docs/architecture/core-contract.md §Verification backlog
|
||||
- docs/architecture/decisions/017-contract-versioning.md §4.2
|
||||
- docs/architecture/decisions/022-contract-suite-layout.md
|
||||
- alkstore-contract-suite/src/version_stamp.rs (the stamp convention)
|
||||
|
||||
## Notes
|
||||
|
||||
> To be filled by implementation agent
|
||||
|
||||
## Summary
|
||||
|
||||
> To be filled on completion
|
||||
|
||||
## Appendix — the decomposition's backlog audit (verify, don't trust)
|
||||
|
||||
Discharged by existing rows (waves 1/3/4 — verify each):
|
||||
|
||||
| Backlog item | Discharged by |
|
||||
|---|---|
|
||||
| Name validation at every entry point (ADR-008 §4) | `name_validation_rejects_empty_and_reserved` (exemplar) |
|
||||
| `schedule()` queue-argument validation (ADR-021 §3) | same exemplar row |
|
||||
| Numeric-domain semantics (ADR-023 §2) | `extent_clamp_semantics` + `duration_refusal_on_non_positive_ttl` |
|
||||
| `encode_payload` typed failure + round-trip (ADR-023 §1, ADR-020 §4) | `payload_round_trip_stores_exact_encoding` |
|
||||
| `PayloadTooLarge` occurrence asymmetry (ADR-016 §5) | `payload_too_large_never_produced_on_sqlite` + `payload_too_large_produced_on_pg` |
|
||||
| Drop = rollback no-ghosts (ADR-021 §4) | `drop_rollback_leaves_no_ghosts` |
|
||||
| In-tx read-your-own-writes (ADR-021 §1) | `in_tx_reads_see_own_writes` |
|
||||
| `save_offset_tx` exactly-once-within-a-business-tx shape | discharged across `in_tx_reads_see_own_writes` (own-save visibility) + `drop_rollback_leaves_no_ghosts` (the save never lands on rollback) — verify the combination genuinely covers the backlog row's claim |
|
||||
| Receiver error/close arms + save-offset monotone composition (ADR-021 §5, ADR-019 §6) | `receiver_close_and_save_arms` (both columns; the pg reconnect-stays-open leg pins engine-side — verify that pin exists) |
|
||||
| Plain-path SQLite open (ADR-023 §3) | engine-side: `uri_shaped_open_path_is_a_literal_filename` (`open_tests.rs`) — SQLite-scoped backlog row per the sqlite-engine-integration task |
|
||||
| Watcher cadence default + knob (ADR-023 §4) | engine-side: `poll_interval_flows_to_the_watcher_config` (`open_tests.rs`) — same scoping |
|
||||
|
||||
Added by wave-5 tasks (verify presence + stamps):
|
||||
|
||||
| Backlog item | Wave-5 task |
|
||||
|---|---|
|
||||
| Job-handle validity predicate (ADR-010 §2) | `suite-queue-depth-rows` |
|
||||
| ADR-010 depth: reclaim-eats-attempt, dead-letter, no-stranded-rows sweep + M-1 retention pin | `suite-queue-depth-rows` |
|
||||
| Scheduler boundary/catch-up/leadership (ADR-009) | `suite-scheduler-rows` |
|
||||
| `outbox_enqueue_tx` commit-atomicity (ADR-014) | `suite-tx-commit-atomicity-rows` |
|
||||
| `publish_with_key_tx` commit-atomicity (ADR-015 §2) | `suite-tx-commit-atomicity-rows` |
|
||||
| `with_tx` panic disposition (N-5) | `suite-tx-commit-atomicity-rows` |
|
||||
| Cross-engine stream ordering equivalence (ADR-015 §3/§4) | `suite-stream-rows` |
|
||||
| `trim_to` full semantics (ADR-015 §5) | `suite-stream-rows` |
|
||||
| Lock TTL/expiry re-acquisition + concurrent loser (ADR-008 §7) | `suite-lock-rows` |
|
||||
| Wake semantics under `WakeReceiver` shapes (ADR-008 §3) | `suite-wake-rows` |
|
||||
| Backoff-curve equivalence (ADR-010 §3) + deferred opts clock legs (ADR-020) | `suite-opts-backoff-rows` |
|
||||
|
||||
Engine-side hardening riding the wave-5 window:
|
||||
|
||||
| Item | Task |
|
||||
|---|---|
|
||||
| SQLite commit-error-arm coverage (wave-3 review deferral) | `sqlite-commit-error-arm` |
|
||||
| F-1 defense-in-depth (pg test-infra; not load-bearing) | `pg-suite-infra-hardening` |
|
||||
@@ -0,0 +1,68 @@
|
||||
---
|
||||
id: sqlite-commit-error-arm
|
||||
name: SQLite engine — commit-error-arm coverage (wave-3 review's deferred test)
|
||||
status: pending
|
||||
depends_on: []
|
||||
scope: narrow
|
||||
risk: medium
|
||||
impact: isolated
|
||||
level: implementation
|
||||
tags: [wave-5, sqlite-engine, tests]
|
||||
---
|
||||
|
||||
## Description
|
||||
|
||||
Land the wave-3 review gate's deferred coverage item
|
||||
(`tasks/review-wave-3.md` Notes: "Coverage of the commit-error arm —
|
||||
no test induces a failing `COMMIT` ... natural home is wave 5's suite
|
||||
hardening"): a test that drives a failing `COMMIT` through the SQLite
|
||||
engine's `commit` path and pins the fix the review verified by
|
||||
code-read — the writer slot is replenished via the `reopen` closure on
|
||||
the failed `COMMIT` (no stranding), the error surfaces as the opaque
|
||||
`Database`, and the store remains fully usable afterward (subsequent
|
||||
`begin_tx`/auto-commit ops work).
|
||||
|
||||
The injection mechanism is this task's to find — candidates: a
|
||||
`PRAGMA max_page_count` squeeze forcing `SQLITE_FULL` on a
|
||||
space-consuming commit, or a `cfg(test)` fault seam in the seam layer
|
||||
if the PRAGMA route cannot hit the COMMIT arm deterministically. The
|
||||
wave-4 fix batch's `cfg(test)` config-seam precedent
|
||||
(`pg-fix-forwarder-reconnect`) is the house pattern if a seam is
|
||||
needed. Prefer the least-invasive mechanism that deterministically
|
||||
reaches the arm; document the choice.
|
||||
|
||||
If the same session has cheap room for it, the waves-1–2 review's
|
||||
optional add — the watcher reconnect-*success* path test (upstream's
|
||||
W-1 test drives only the failure path; "a reconnect success test would
|
||||
require a file appearing mid-run") — rides here **only if** the
|
||||
engine kept the current watcher shape and the test is genuinely cheap;
|
||||
otherwise leave it recorded as not-taken in Notes (it was a
|
||||
suggestion, not an order).
|
||||
|
||||
## Acceptance Criteria
|
||||
|
||||
- [ ] A test induces a failing `COMMIT` on the SQLite engine and pins:
|
||||
error surfaces (`Database`), writer slot replenished (a
|
||||
subsequent `begin_tx` succeeds), no partial-commit residue
|
||||
- [ ] The injection mechanism documented in Notes (PRAGMA vs seam, and
|
||||
why)
|
||||
- [ ] The watcher reconnect-success test either landed or explicitly
|
||||
recorded as not-taken with the reason
|
||||
- [ ] `cargo test -p alkstore-sqlite` green server-less; clippy
|
||||
`-D warnings`; fmt clean
|
||||
|
||||
## References
|
||||
|
||||
- tasks/review-wave-3.md (Notes: the deferred commit-error-arm coverage)
|
||||
- tasks/sqlite-engine-seam-tx.md (the `commit` replenish fix the test pins)
|
||||
- tasks/pg-fix-forwarder-reconnect.md (the `cfg(test)` seam precedent)
|
||||
- docs/reviews/001-waves-1-2-general-review.md §2 (the watcher
|
||||
reconnect-success suggestion)
|
||||
|
||||
## Notes
|
||||
|
||||
> To be filled by implementation agent
|
||||
|
||||
## Summary
|
||||
|
||||
> To be filled on completion
|
||||
@@ -0,0 +1,78 @@
|
||||
---
|
||||
id: suite-lock-rows
|
||||
name: Contract-suite rows — lock TTL/expiry re-acquisition + concurrent try_lock loser
|
||||
status: pending
|
||||
depends_on: []
|
||||
scope: narrow
|
||||
risk: medium
|
||||
impact: phase
|
||||
level: implementation
|
||||
tags: [wave-5, contract-suite, locks]
|
||||
---
|
||||
|
||||
## Description
|
||||
|
||||
Add the named-locks 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): "Named-lock TTL/expiry
|
||||
re-acquisition on SQLite" (pinned on pg by the POC's lock probe; on
|
||||
SQLite it rests on the forked substrate's lock machinery — the suite
|
||||
pins both engines to identical behavior) and "Concurrent `try_lock`
|
||||
loser/error behavior on SQLite" (the substrate's busy-path under lock
|
||||
contention is the thing to pin — the pg side returns cleanly).
|
||||
|
||||
Rows to add:
|
||||
|
||||
- **`lock_ttl_expiry_and_reacquisition`** — the ADR-008 §7 guarantee
|
||||
row: mutual exclusion bounded by TTL + renewal. Pin: a held lock
|
||||
excludes a second acquirer (`None`); after TTL expiry exclusion
|
||||
lapses *silently* (no revocation event, no error — a second owner
|
||||
acquires); the original holder's post-expiry `renew` is refused
|
||||
(`false` — the deadline-lapse refusal, ADR-010 §2's uniform
|
||||
predicate shape on the lock handle); `release` on the second owner
|
||||
frees the name for a third acquirer. Short TTL (1 s) + tolerance
|
||||
sleeps — sequential single-store, fits the determinism posture.
|
||||
- **`concurrent_try_lock_loser_is_a_value`** — contention posture:
|
||||
with a lock held, contender `try_lock` calls return the clean
|
||||
no-work value (`None`) on **both** engines — never a `Database`
|
||||
error, never a busy-throw surfacing (the SQLite substrate's
|
||||
busy-path is the risk site; the pg side is the reference behavior).
|
||||
Drive several contenders sequentially against one held lock, and a
|
||||
release-then-contend cycle proving the loser path leaves no state
|
||||
that blocks a later acquire.
|
||||
|
||||
Risk note: what the SQLite busy-path *actually* returns under
|
||||
contention is the open question this row exists to answer — if it
|
||||
surfaces `Database` where pg returns `None`, that is a real
|
||||
conformance finding: stop, record it in Notes, and raise it (an
|
||||
engine fix or an ADR-level asymmetry call) rather than pinning the
|
||||
divergence silently.
|
||||
|
||||
## Acceptance Criteria
|
||||
|
||||
- [ ] Two rows exist, version-stamped (ADR-008 §7, ADR-019 §1,
|
||||
ADR-023 §2 as applicable)
|
||||
- [ ] Rows wired into both engines' `contract_suite.rs` targets
|
||||
- [ ] SQLite column green server-less; pg column green against the
|
||||
harness server
|
||||
- [ ] Any engine divergence found is recorded in Notes with its
|
||||
disposition (fix or documented asymmetry) — not pinned silently
|
||||
- [ ] `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 (lock
|
||||
TTL/expiry re-acquisition; concurrent try_lock loser)
|
||||
- docs/architecture/decisions/008-contract-v1-pinning.md §7
|
||||
- docs/architecture/decisions/019-mechanism-handle-surfaces.md §1
|
||||
- docs/architecture/engine-sqlite.md (the substrate's lock machinery)
|
||||
|
||||
## Notes
|
||||
|
||||
> To be filled by implementation agent
|
||||
|
||||
## Summary
|
||||
|
||||
> To be filled on completion
|
||||
@@ -0,0 +1,81 @@
|
||||
---
|
||||
id: suite-opts-backoff-rows
|
||||
name: Contract-suite rows — backoff-curve equivalence + the deferred enqueue-opts clock legs
|
||||
status: pending
|
||||
depends_on: []
|
||||
scope: narrow
|
||||
risk: low
|
||||
impact: phase
|
||||
level: implementation
|
||||
tags: [wave-5, contract-suite, queues]
|
||||
---
|
||||
|
||||
## Description
|
||||
|
||||
Complete the arithmetic-equivalence backlog rows
|
||||
(core-contract.md §Verification backlog): "Backoff-curve and
|
||||
stamp-resolution equivalence across engines" and the two
|
||||
wall-clock-adjacent legs the existing `enqueue_opts_resolution` row
|
||||
explicitly deferred ("The `run_at`-alone-literal and
|
||||
neither-field-resolves-to-now legs ... pin with wave 5's equivalence
|
||||
row").
|
||||
|
||||
Work:
|
||||
|
||||
- **Extend `enqueue_opts_resolution` in place** (one owner per
|
||||
property — the row's own doc comment names this wave-5 completion;
|
||||
stamps accumulate per the convention): the `run_at`-alone literal
|
||||
leg (a future `run_at` is the row's ready time verbatim; a past
|
||||
`run_at` resolves to ready-now — the boundary-total rule, ADR-023
|
||||
§2) and the neither-field leg (ready now — tolerance-bounded
|
||||
proximity, never a tight timing assert).
|
||||
- **Add `backoff_curve_equivalence`** — the equal-jitter exponential
|
||||
curve (ADR-010 §3): `delay ∈ [base·2^(a−1)/2, base·2^(a−1)]` per
|
||||
attempt, uniform jitter over the lower half — *the range, not a
|
||||
jitter label, is the definition*. Drive claim → `retry(err, None)`
|
||||
cycles on a queue with a small `backoff_base_s` stamp (2 s keeps
|
||||
the integer-second row resolution honest: ranges [1,2], [2,4],
|
||||
[4,8] across the first attempts; the inter-attempt waits stay
|
||||
tolerance-bounded and sequential), reading the computed delay back
|
||||
via `get_job`'s `run_at` minus now, asserting range membership per
|
||||
attempt on both engines — identical bounds is the equivalence pin
|
||||
(ADR-012 §2: engine-side arithmetic, suite-pinned identical
|
||||
outputs). Include the explicit-delay override leg
|
||||
(`retry(err, Some(d))` honored — a `Some(0)` resolves ready-now per
|
||||
the boundary-total rule). **The 1-hour cap leg is not
|
||||
suite-pinnable** (reaching capped attempts requires waiting out
|
||||
minute-scale delays); it stays pinned engine-side (both engines'
|
||||
unit tests already pin the curve against the ADR text — the
|
||||
wave-3/4 reviews verified it) — record that disposition in Notes
|
||||
for the review gate.
|
||||
|
||||
## Acceptance Criteria
|
||||
|
||||
- [ ] `enqueue_opts_resolution` extended with both clock legs; its
|
||||
doc comment's deferral note updated (stamps accumulate)
|
||||
- [ ] `backoff_curve_equivalence` exists, version-stamped (ADR-010 §3,
|
||||
ADR-020 §1–§3, ADR-012 §2, ADR-023 §2)
|
||||
- [ ] Rows wired into both engines' `contract_suite.rs` targets
|
||||
- [ ] SQLite column green server-less; pg column green against the
|
||||
harness server
|
||||
- [ ] The cap-leg disposition recorded in Notes
|
||||
- [ ] `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 (backoff-curve
|
||||
and stamp-resolution equivalence; enqueue-opts resolution equivalence)
|
||||
- docs/architecture/decisions/010-queue-semantics-depth.md §3
|
||||
- docs/architecture/decisions/020-enqueue-opt-semantics-and-bridges.md §1–§3
|
||||
- docs/architecture/decisions/012-forked-substrate-design.md §2
|
||||
- alkstore-contract-suite/src/properties.rs (`enqueue_opts_resolution`'s
|
||||
deferral note)
|
||||
|
||||
## Notes
|
||||
|
||||
> To be filled by implementation agent
|
||||
|
||||
## Summary
|
||||
|
||||
> To be filled on completion
|
||||
@@ -0,0 +1,94 @@
|
||||
---
|
||||
id: suite-queue-depth-rows
|
||||
name: Contract-suite rows — job-handle validity predicate + ADR-010 queue depth
|
||||
status: pending
|
||||
depends_on: []
|
||||
scope: moderate
|
||||
risk: medium
|
||||
impact: phase
|
||||
level: implementation
|
||||
tags: [wave-5, contract-suite, queues]
|
||||
---
|
||||
|
||||
## Description
|
||||
|
||||
Add the queue-depth contract rows to
|
||||
`alkstore-contract-suite/src/properties.rs` and wire them into both
|
||||
engines' suite targets (`alkstore-sqlite/tests/contract_suite.rs`
|
||||
directly; `alkstore-postgres/tests/contract_suite.rs` via the
|
||||
`harness_row!` macro), discharging the backlog rows "Job-handle
|
||||
validity predicate on both engines" and the ADR-010 depth properties
|
||||
(core-contract.md §Verification backlog). One owner per property
|
||||
(ADR-012 §2 mirror): the rows are engine-independent, run against both
|
||||
factories, version-stamped per the crate's convention.
|
||||
|
||||
Rows to add:
|
||||
|
||||
- **`job_handle_validity_predicate`** — the uniform predicate
|
||||
(ADR-010 §2): an op succeeds only while the row is `processing` and
|
||||
the claim deadline is unexpired. Pin: the late-heartbeat boundary
|
||||
(heartbeat refused exactly when the deadline has lapsed — a short
|
||||
`visibility_timeout_s` stamp plus a tolerance sleep past it; the
|
||||
sleep-then-assert-state shape is sequential single-store and fits
|
||||
the suite's determinism posture), the post-lapse ack refusal
|
||||
(at-least-once: the worker's ack does not land after lapse),
|
||||
`ack_batch` applying the predicate per id (lapsed or non-claimed ids
|
||||
silently not counted — the returned count reflects only live
|
||||
acks), `retry`'s refusal being `false` with the row untouched, and
|
||||
the engine-default strings landing identically (`fail(None)` →
|
||||
`"failed"`, exhaustion → `"max attempts exceeded"`).
|
||||
- **`queue_depth_reclaim_and_dead_letter`** — the ADR-010 depth
|
||||
properties: a visibility reclaim consumes an attempt (expired claim
|
||||
→ reclaim → `attempts` incremented, `claimed_at` refreshed),
|
||||
exhaustion moves the row to dead storage (`get_job` sees the dead
|
||||
row with `last_error`/`died_at`), and `cancel` is an unconditional
|
||||
delete (not an interrupt).
|
||||
- **`sweep_no_stranded_rows`** — `sweep_expired()` moves *every*
|
||||
past-expiry row (any state — pending-expired, processing-lapsed,
|
||||
dead-past-retention) and enforces dead-letter retention; the
|
||||
no-stranded-rows property (ADR-010 §5). Include the M-1 follow-up
|
||||
pin here (the waves-1–2 general review's suggested wave-5 add): the
|
||||
sweep's retention half rolling back with the sweep on failure —
|
||||
**with an explicit disposition**: if a retention-DELETE failure is
|
||||
not injectable through the contract surface (it likely is not —
|
||||
the failure is storage-level), pin that leg engine-side
|
||||
(SQLite substrate test seam) and record the disposition in this
|
||||
task's Notes for the review gate to audit. The plan ordered "M-1's
|
||||
retention-failure test → wave 5's contract-suite rows"; where the
|
||||
pin actually lands is the implementer's honest call, documented.
|
||||
|
||||
Timing posture: short stamps (1 s visibility) + tolerance sleeps
|
||||
(bounded well above the transition's resolution), asserting state
|
||||
outcomes never timing values. No two-task races.
|
||||
|
||||
## Acceptance Criteria
|
||||
|
||||
- [ ] Three rows exist in `properties.rs`, `pub async fn(&dyn
|
||||
StoreFactory)`, version-stamped (ADR-010 §2/§5, ADR-019 §3,
|
||||
ADR-008 §5 as applicable; stamps cite ADR § per the
|
||||
`version_stamp.rs` convention)
|
||||
- [ ] Rows wired into both engines' `contract_suite.rs` targets
|
||||
- [ ] SQLite column green server-less; pg column green against the
|
||||
harness server
|
||||
- [ ] The M-1 retention-failure pin's location decided and recorded in
|
||||
Notes (suite row or engine-side, with the reason)
|
||||
- [ ] `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 (job-handle
|
||||
validity predicate; ADR-010 depth properties)
|
||||
- docs/architecture/decisions/010-queue-semantics-depth.md §2/§5
|
||||
- docs/architecture/decisions/019-mechanism-handle-surfaces.md §3
|
||||
- docs/reviews/001-waves-1-2-general-review.md (M-1, §4's suggested adds)
|
||||
- tasks/sqlite-engine-integration.md (the wave-5 lesson: rows are
|
||||
mock-blind until a real single-writer engine runs them)
|
||||
|
||||
## Notes
|
||||
|
||||
> To be filled by implementation agent
|
||||
|
||||
## Summary
|
||||
|
||||
> To be filled on completion
|
||||
@@ -0,0 +1,93 @@
|
||||
---
|
||||
id: suite-scheduler-rows
|
||||
name: Contract-suite rows — scheduler boundary/catch-up/leadership (+ determinism-posture extension)
|
||||
status: pending
|
||||
depends_on: []
|
||||
scope: moderate
|
||||
risk: medium
|
||||
impact: phase
|
||||
level: implementation
|
||||
tags: [wave-5, contract-suite, scheduler]
|
||||
---
|
||||
|
||||
## Description
|
||||
|
||||
Add the scheduler contract rows to
|
||||
`alkstore-contract-suite/src/properties.rs` and wire them into both
|
||||
engines' suite targets, discharging the backlog row "Scheduler + depth
|
||||
properties on Postgres" (core-contract.md §Verification backlog): the
|
||||
tick machinery — boundary advance, bounded catch-up, leadership-loss
|
||||
discipline — pinned engine-uniform (SQLite inherits the substrate's
|
||||
test-pinned implementation; pg re-derived; the suite pins identical
|
||||
*behavior*).
|
||||
|
||||
**Posture extension first** (test-side, ADR-017 §2 class 4): the
|
||||
suite's determinism statement (`properties.rs` module doc) currently
|
||||
says runner spawning stays engine-test-side. The backlog explicitly
|
||||
demands these rows in the suite, so amend the module doc to admit
|
||||
**runner-driving rows**: a spawned `run_schedules(stop)` task with a
|
||||
`StopToken`, assertions on state outcomes (jobs fired, leadership
|
||||
returned) with tolerances bounded well above the boundary resolution —
|
||||
never timing-value assertions, never two-task race windows. Document
|
||||
the extension where the posture is stated.
|
||||
|
||||
Rows to add:
|
||||
|
||||
- **`scheduler_boundary_fires`** — register `@every 1s`, run the
|
||||
runner briefly, stop: boundary fires land as ordinary claimable jobs
|
||||
stamped with the `ScheduleOpts`/derived-default resolution
|
||||
(ADR-009 §3, ADR-020 §3); no double-fire per boundary while one
|
||||
leader runs (fire + boundary-advance commit atomically).
|
||||
- **`scheduler_bounded_catchup`** — register, delay before starting
|
||||
the runner (several elapsed boundaries), then run: missed boundaries
|
||||
replay boundary-by-boundary (at-least-once per elapsed boundary),
|
||||
the fired count bounded by the 64-cap (a multi-second downtime stays
|
||||
far below it — the ≤64 catch-up leg is the suite-pinnable one).
|
||||
**The beyond-cap skip-forward leg needs >64 elapsed boundaries
|
||||
(~65+ s) and row-backdating the public surface does not express —
|
||||
pin it engine-side where the schedule row's `next_fire_at` is
|
||||
directly manipulable (both engines' scheduler tests), and record
|
||||
that disposition in Notes for the review gate.**
|
||||
- **`scheduler_leadership_discipline`** — two concurrent runners:
|
||||
exactly one leader (fires not duplicated), the loser surfaces
|
||||
`LeadershipLost` (ADR-009 §6) rather than silently co-ticking;
|
||||
a stopped runner's clean-stop return shape per ADR-019 §4.
|
||||
|
||||
**Disposition note from the wave-4 fix-batch gate** (recorded, not a
|
||||
defect): the pg tick's in-tx enqueue fires issue no `pg_notify` wake
|
||||
(SQLite's watcher wakes on those commits) — a cross-engine
|
||||
wake-latency-parity gap the re-poll safety net covers for correctness.
|
||||
If this row's shape demands the parity, the one-call `wake_tx` inside
|
||||
the tick's tx rides **this** task; otherwise record the row's
|
||||
latency-posture in Notes and leave the engines as they are.
|
||||
|
||||
## Acceptance Criteria
|
||||
|
||||
- [ ] The determinism-posture extension documented in
|
||||
`properties.rs`'s module doc (runner-driving rows admitted,
|
||||
tolerance-bounded state assertions only)
|
||||
- [ ] Three rows exist, version-stamped (ADR-009 §3/§4/§6, ADR-019 §4,
|
||||
ADR-020 §3 as applicable)
|
||||
- [ ] Rows wired into both engines' `contract_suite.rs` targets
|
||||
- [ ] SQLite column green server-less; pg column green against the
|
||||
harness server
|
||||
- [ ] The skip-forward-leg disposition and the fire-wake-parity
|
||||
disposition recorded in Notes
|
||||
- [ ] `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 (scheduler
|
||||
+ depth properties on Postgres)
|
||||
- docs/architecture/decisions/009-scheduler-collapse.md §3/§4/§6
|
||||
- docs/architecture/decisions/019-mechanism-handle-surfaces.md §4
|
||||
- tasks/review-wave-4-fixes.md (the fire-wake-parity recorded-for-later note)
|
||||
|
||||
## Notes
|
||||
|
||||
> To be filled by implementation agent
|
||||
|
||||
## Summary
|
||||
|
||||
> To be filled on completion
|
||||
@@ -0,0 +1,80 @@
|
||||
---
|
||||
id: suite-stream-rows
|
||||
name: Contract-suite rows — cross-engine stream ordering equivalence + trim_to semantics
|
||||
status: pending
|
||||
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
|
||||
|
||||
- [ ] Two rows exist, version-stamped (ADR-015 §3/§4/§5, ADR-019 §6
|
||||
as applicable)
|
||||
- [ ] Rows wired into both engines' `contract_suite.rs` targets
|
||||
- [ ] SQLite column green server-less; pg column green against the
|
||||
harness server
|
||||
- [ ] No duplication with the existing rows' legs (cross-reference in
|
||||
each row's doc comment which row owns which leg)
|
||||
- [ ] `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
|
||||
|
||||
> To be filled by implementation agent
|
||||
|
||||
## Summary
|
||||
|
||||
> To be filled on completion
|
||||
@@ -0,0 +1,83 @@
|
||||
---
|
||||
id: suite-tx-commit-atomicity-rows
|
||||
name: Contract-suite rows — outbox/keyed-publish tx commit-atomicity + with_tx panic probe
|
||||
status: pending
|
||||
depends_on: []
|
||||
scope: moderate
|
||||
risk: low
|
||||
impact: phase
|
||||
level: implementation
|
||||
tags: [wave-5, contract-suite, tx-seam]
|
||||
---
|
||||
|
||||
## Description
|
||||
|
||||
Add the tx-seam commit-atomicity rows to
|
||||
`alkstore-contract-suite/src/properties.rs` and wire them into both
|
||||
engines' suite targets, discharging three backlog rows
|
||||
(core-contract.md §Verification backlog): "`outbox_enqueue_tx`
|
||||
commit-atomicity on both engines" (ADR-014), "`publish_with_key_tx`
|
||||
commit-atomicity on both engines" (ADR-015 §2), and the N-5
|
||||
`with_tx` panic-disposition probe (the waves-1–2 general review's
|
||||
ordered wave-5 add — the panic path "can only be fully pinned once a
|
||||
real engine's handle exists").
|
||||
|
||||
Rows to add:
|
||||
|
||||
- **`outbox_enqueue_tx_commit_atomicity`** — rollback drops the
|
||||
backing-queue job row together with the business write (no ghost
|
||||
job; `run_once` afterwards claims nothing); commit makes the job
|
||||
claimable by `run_once` exactly when the business write commits
|
||||
(drive a real delivery through the `Delivery` closure); the stamped
|
||||
opts are the outbox's derived 60/5/5 set, `get_job`-visible and
|
||||
identical on both engines (ADR-014 §1, ADR-010 §3a). The
|
||||
reserved/empty outbox-name validation legs already pin in the
|
||||
exemplar row — do not duplicate them.
|
||||
- **`publish_with_key_tx_commit_atomicity`** — rollback drops the
|
||||
keyed event row with the business write (no ghost event); commit
|
||||
makes it visible to `read_since` (and a subscriber attach); the key
|
||||
round-trips on the committed event. The empty-`Some`-key
|
||||
`InvalidName` leg already pins in the exemplar — do not duplicate.
|
||||
- **`with_tx_panicking_closure_rolls_back`** — a closure that panics
|
||||
mid-flight ⇒ rollback with no residue (the uniform no-ghosts list:
|
||||
job/event/notify/offset — ADR-021 §4's drop = rollback through the
|
||||
panic path), and the store remains usable afterward (the SQLite
|
||||
writer slot replenished — the wave-3 stranding fix's property, now
|
||||
pinned at contract level). Mechanics hint: the panic crosses an
|
||||
await inside `with_tx`'s own future, so drive it via
|
||||
`tokio::spawn` + `JoinError::is_panic()` (a `catch_unwind` around an
|
||||
async closure does not see the panic point); assert the panic
|
||||
surfaced *and* the post-panic state. This is N-5's probe against
|
||||
real engines for the first time — the SQLite handle-on-lease Drop
|
||||
impl and the pg pooled-object discard arm must both survive it.
|
||||
|
||||
## Acceptance Criteria
|
||||
|
||||
- [ ] Three rows exist, version-stamped (ADR-014 §1, ADR-015 §2,
|
||||
ADR-021 §4, ADR-007 as applicable)
|
||||
- [ ] Rows wired into both engines' `contract_suite.rs` targets
|
||||
- [ ] SQLite column green server-less; pg column green against the
|
||||
harness server
|
||||
- [ ] The panic probe passes on both engines (no writer-slot stranding,
|
||||
no pooled-object leak) — if an engine fails here, that is a real
|
||||
defect: stop, record, fix engine-side before completing the task
|
||||
- [ ] `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 (outbox tx
|
||||
commit-atomicity; keyed-publish tx commit-atomicity)
|
||||
- docs/architecture/decisions/014-outbox-tx-enqueue.md §1
|
||||
- docs/architecture/decisions/015-streams-depth.md §2
|
||||
- docs/architecture/decisions/021-tx-reads-and-value-shape-fixes.md §4
|
||||
- docs/reviews/001-waves-1-2-general-review.md (N-5)
|
||||
- tasks/sqlite-engine-seam-tx.md (the Drop impl N-5's probe exercises)
|
||||
|
||||
## Notes
|
||||
|
||||
> To be filled by implementation agent
|
||||
|
||||
## Summary
|
||||
|
||||
> To be filled on completion
|
||||
@@ -0,0 +1,73 @@
|
||||
---
|
||||
id: suite-wake-rows
|
||||
name: Contract-suite row — wake semantics under the pinned WakeReceiver shapes
|
||||
status: pending
|
||||
depends_on: []
|
||||
scope: narrow
|
||||
risk: low
|
||||
impact: phase
|
||||
level: implementation
|
||||
tags: [wave-5, contract-suite, notify]
|
||||
---
|
||||
|
||||
## Description
|
||||
|
||||
Add the wake-semantics contract row to
|
||||
`alkstore-contract-suite/src/properties.rs` and wire it into both
|
||||
engines' suite targets, discharging the backlog row "Wake semantics
|
||||
under `WakeReceiver` shapes" (core-contract.md §Verification
|
||||
backlog): the POCs verified wake delivery/coalescing through their own
|
||||
probe types; the pinned `Wake { channel }` / recv forms (ADR-008 §3)
|
||||
need the suite's own property on both engines.
|
||||
|
||||
Row to add:
|
||||
|
||||
- **`wake_receiver_shapes`** — the wake contract's pinnable core,
|
||||
tolerance-bounded (wakes are best-effort hints — the row asserts
|
||||
state outcomes, never delivery counts or latencies):
|
||||
- a pre-attached listener receives a wake after a committed
|
||||
`notify` on its channel (`recv`/`try_recv`/`recv_timeout` forms
|
||||
all exercised; the wake's `channel` field matches the listened
|
||||
channel — the one piece of semantic content a wake carries);
|
||||
- a burst of notifies yields *at least one* wake — never an exact
|
||||
per-notify count (coalescing is the documented engine asymmetry:
|
||||
possibly coalesced on SQLite, per-notify on pg; the row pins the
|
||||
floor, not the shape);
|
||||
- a listener attached *after* a commit never sees that commit's
|
||||
notify (no-replay — `recv_timeout` idles; the pg no-replay hole
|
||||
and SQLite's burst coalescing are both legal under this pin);
|
||||
- wakes from a *different* channel do not arrive on this listener
|
||||
(channel-scoped delivery — the invalidation-key property caching
|
||||
subscribers rely on).
|
||||
|
||||
The failure-surface arms (watcher death → `recv() -> None` on SQLite;
|
||||
the synthetic reconnect-wake on pg) are the engine-differing close
|
||||
arms — already pinned engine-side and in the receiver-arms row's
|
||||
disposal leg; this row does not re-pin them (cross-reference in the
|
||||
doc comment).
|
||||
|
||||
## Acceptance Criteria
|
||||
|
||||
- [ ] The row exists, version-stamped (ADR-006, ADR-008 §3)
|
||||
- [ ] Wired into both engines' `contract_suite.rs` targets
|
||||
- [ ] SQLite column green server-less; pg column green against the
|
||||
harness server
|
||||
- [ ] The row's doc comment cross-references the close-arm pins it
|
||||
deliberately does not duplicate
|
||||
- [ ] `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 (wake
|
||||
semantics under WakeReceiver shapes); §notify/listen
|
||||
- docs/architecture/decisions/006-wake-and-delivery-contract.md
|
||||
- docs/architecture/decisions/008-contract-v1-pinning.md §3
|
||||
|
||||
## Notes
|
||||
|
||||
> To be filled by implementation agent
|
||||
|
||||
## Summary
|
||||
|
||||
> To be filled on completion
|
||||
Reference in new issue
Block a user