From 5abd351c86d6d0b0a761f3bf199ccaeb5dc3f1de Mon Sep 17 00:00:00 2001 From: "glm-5.3-flash" Date: Sat, 10 Oct 2026 09:57:01 +0000 Subject: [PATCH] =?UTF-8?q?Wave-6=20decomposition=20(mem=20engine):=20elev?= =?UTF-8?q?en=20tasks=20per=20the=20pg-wave=20rhythm=20=E2=80=94=20foundat?= =?UTF-8?q?ions=20(crate=20scaffold,=20MemStore::new=20isolation,=20guarde?= =?UTF-8?q?d=20state=20core,=20wake=20routing,=20wasm32=20gate=20from=20bi?= =?UTF-8?q?rth),=20seam-tx=20(the=20load-bearing=20tx=20overlay:=20eleven?= =?UTF-8?q?=20staged=20=5Ftx=20methods,=20commit-merge=20+=20wake-at-commi?= =?UTF-8?q?t=20=E2=80=94=20pg-fix-tx-wake's=20discipline=20structural=20fr?= =?UTF-8?q?om=20the=20start,=20drop=20=3D=20discard=20trivial,=20with=5Ftx?= =?UTF-8?q?=20panic=20posture),=20mechanisms=20mutually=20parallel=20(noti?= =?UTF-8?q?fy-listen=20close-at-engine-drop/no-replay/never-coalesced;=20q?= =?UTF-8?q?ueues=20=3D=20ADR-010=20machine=20driver-free=20with=20the=20eq?= =?UTF-8?q?ual-jitter=20curve=20and=20opts=20resolution=20as=20ADR-012=20?= =?UTF-8?q?=C2=A72's=20third=20owner;=20streams=20log+offsets+trim;=20lock?= =?UTF-8?q?s=20TTL=20registry,=20no=20busy-path=20analog),=20scheduler-out?= =?UTF-8?q?box=20(tick/catch-up-64/leadership=20through=20the=20=5F=5Falks?= =?UTF-8?q?tore=5Fscheduler=20lock,=20engine-uniform=20per=20ADR-009=20?= =?UTF-8?q?=C2=A74;=20outbox=20over=20queues=20with=20the=2060/5/5=20seam?= =?UTF-8?q?=20stamp),=20integration=20(suite=20third=20StoreFactory=20colu?= =?UTF-8?q?mn=20green=20on=20host=20under=20a=20current-thread=20flavor,?= =?UTF-8?q?=20wasm32=20acceptance=20items,=20engine-mem.md=20to=20implemen?= =?UTF-8?q?tation-complete),=20and=20the=20review-wave-6=20gate=20(born-pi?= =?UTF-8?q?nned=20audit:=20conformance=20read,=20discharge-map=20wiring,?= =?UTF-8?q?=20wasm=20items,=20three-way=20equivalence=20green,=20class-1?= =?UTF-8?q?=20window=20discipline).=20Window=20riders:=20suite-harness-cur?= =?UTF-8?q?rent-thread=20=E2=80=94=20the=20wave's=20one=20suite-side=20pre?= =?UTF-8?q?-task,=20past=5Fstamp=5Fsleep's=20blocking=20sleep=20goes=20asy?= =?UTF-8?q?nc=20per=20review=20003's=20properties.rs:1683=20posture=20note?= =?UTF-8?q?,=20enabling=20the=20current-thread=20flavor;=20pg-fix-schedule?= =?UTF-8?q?r-tick-seam=20=E2=80=94=20review=20003=20Finding=201,=20the=20c?= =?UTF-8?q?fg(test)=20transient-fault=20seam=20driving=20the=20real=20tick?= =?UTF-8?q?=5Fwith=5Fretries=20through=20retry-then-success=20and=20exhaus?= =?UTF-8?q?tion=20arms=20(the=20last=20uncovered=20wave-4=20hardening=20ar?= =?UTF-8?q?m,=20sqlite-commit-error-arm=20pattern).=20Task=20hygiene:=20pg?= =?UTF-8?q?-fix-open-path=20frontmatter=20fixed=20(unquoted=20'max=5Fsize:?= =?UTF-8?q?=200'=20colon-space=20inside=20the=20name=20scalar=20broke=20YA?= =?UTF-8?q?ML=20parsing=20=E2=80=94=20taskgraph=20validate=20had=20been=20?= =?UTF-8?q?red=20one-error=20since=20the=20wave-4=20fix-batch=20decomposit?= =?UTF-8?q?ion;=20also=20restores=20review-wave-4-fixes'=20dependency=20re?= =?UTF-8?q?solution).=20implementation.md:=20wave-6=20table=20row=20?= =?UTF-8?q?=E2=86=92=20decomposed,=20task-set=20listing=20with=20the=20par?= =?UTF-8?q?allelism=20note=20(rider=20+=20pre-task=20independent=20of=20th?= =?UTF-8?q?e=20mem=20chain;=20rider=20closes=20before=20the=20gate),=20wav?= =?UTF-8?q?e-6=20decomposition=20bullet=20in=20the=20review-rounds=20recor?= =?UTF-8?q?d.=20Docs-only=20change;=20verified=20taskgraph=20validate=20(6?= =?UTF-8?q?0=20tasks,=20no=20cycles),=20cargo=20fmt=20--check,=20cross-ref?= =?UTF-8?q?erence=20read?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/plans/implementation.md | 58 ++++++++++++++-- tasks/mem-engine-foundations.md | 92 ++++++++++++++++++++++++++ tasks/mem-engine-integration.md | 95 +++++++++++++++++++++++++++ tasks/mem-engine-locks.md | 65 ++++++++++++++++++ tasks/mem-engine-notify-listen.md | 73 ++++++++++++++++++++ tasks/mem-engine-queues.md | 86 ++++++++++++++++++++++++ tasks/mem-engine-scheduler-outbox.md | 87 ++++++++++++++++++++++++ tasks/mem-engine-seam-tx.md | 95 +++++++++++++++++++++++++++ tasks/mem-engine-streams.md | 80 ++++++++++++++++++++++ tasks/pg-fix-open-path.md | 2 +- tasks/pg-fix-scheduler-tick-seam.md | 68 +++++++++++++++++++ tasks/review-wave-6.md | 81 +++++++++++++++++++++++ tasks/suite-harness-current-thread.md | 62 +++++++++++++++++ 13 files changed, 939 insertions(+), 5 deletions(-) create mode 100644 tasks/mem-engine-foundations.md create mode 100644 tasks/mem-engine-integration.md create mode 100644 tasks/mem-engine-locks.md create mode 100644 tasks/mem-engine-notify-listen.md create mode 100644 tasks/mem-engine-queues.md create mode 100644 tasks/mem-engine-scheduler-outbox.md create mode 100644 tasks/mem-engine-seam-tx.md create mode 100644 tasks/mem-engine-streams.md create mode 100644 tasks/pg-fix-scheduler-tick-seam.md create mode 100644 tasks/review-wave-6.md create mode 100644 tasks/suite-harness-current-thread.md diff --git a/docs/plans/implementation.md b/docs/plans/implementation.md index 9c6ffa2..c41f6b8 100644 --- a/docs/plans/implementation.md +++ b/docs/plans/implementation.md @@ -1,8 +1,9 @@ --- status: draft -last_updated: 2026-10-10 (ADR-024 — the mem engine adopted as wave 6 -and decomposed-in-place documentation; release readiness renumbered -to wave 7. Waves 1–5 implemented and reviewed; engine specs stable.) +last_updated: 2026-10-10 (ADR-024 — the mem engine adopted as wave 6, +release readiness renumbered to wave 7; wave 6 decomposed — eleven +tasks per the pg-wave rhythm with the tx overlay load-bearing. Waves +1–5 implemented and reviewed; engine specs stable.) --- # alkstore — Implementation plan @@ -41,7 +42,7 @@ core crate plus the finished suite (wave 5) — born pinned, per | [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](#wave-5--contract-suite): the cross-engine equivalence properties (core-contract.md §Verification backlog), version-stamped per ADR-017 | waves 3 + 4 | implemented + reviewed (2026-10-10) | -| 6 | [Mem engine](#wave-6--mem-engine): separate `alkstore-mem` crate, full contract v1 over in-memory mechanisms (tx overlay, queue/scheduler/streams/locks/wakes), the suite's third factory column (born pinned), wasm32 compile-clean acceptance | waves 1 + 5 | not yet decomposed | +| 6 | [Mem engine](#wave-6--mem-engine): separate `alkstore-mem` crate, full contract v1 over in-memory mechanisms (tx overlay, queue/scheduler/streams/locks/wakes), the suite's third factory column (born pinned), wasm32 compile-clean acceptance | waves 1 + 5 | decomposed (2026-10-10) | | 7 | [Release readiness](#wave-7--release-readiness): crate docs, deployment matrix final pass, README (written last, honestly), publish prep; fuzzing decision | wave 6 | not yet decomposed | ## Wave 1 — Foundations @@ -311,6 +312,49 @@ transient-error retry loop, MEDIUM — engine-side, small, mem-independent); Findings 2–3 follow their park recommendation into wave 7's pre-release pass. +**Wave 6's task set** (the decomposition, 2026-10-10 — the pg wave's +rhythm: foundations → seam → mechanisms in parallel → scheduler/outbox +→ integration → review gate): + +- `mem-engine-foundations` — crate scaffold (core + tokio sync+time + only), `MemStore::new()` isolation, guarded state core, wake-routing + substrate, clock accessor; the wasm32 compile gate starts here and + stays green onward. +- `mem-engine-seam-tx` — the load-bearing task: the tx overlay (all + eleven `*_tx` staged, commit-merge + wake-at-commit, drop = + discard, `with_tx` panic posture) + the `Store` trait surface + + entry-point numeric-domain guards (`suite-tx-commit-atomicity-rows` + is what consumes it once integrated). +- `mem-engine-notify-listen` — channels wake substrate surface, + three recv forms, close-at-engine-drop, no-replay, never coalesced; + depends on the seam (tx-staged notify). +- `mem-engine-queues` — the ADR-010 state machine driver-free; + equal-jitter curve and opts resolution as the third owner. +- `mem-engine-streams` — in-memory log, per-consumer offsets, + `trim_to`, receiver arms. +- `mem-engine-locks` — TTL registry; the SQLite busy-path question + has no mem analog (loser is a clean `None`). +- `mem-engine-scheduler-outbox` — tick/boundary/catch-up/leadership + through the reserved scheduler lock; outbox over queues. +- `suite-harness-current-thread` — the wave's one suite-side pre-task + (async `past_stamp_sleep`); engine-independent, required before the + mem column's current-thread flavor. +- `mem-engine-integration` — full-surface wiring, the suite's third + `StoreFactory` column green on host under the current-thread + flavor, the wasm32 acceptance items, `engine-mem.md` to + implementation-complete. +- `pg-fix-scheduler-tick-seam` — review 003 Finding 1, riding the + window (engine-side, mem-independent; the last uncovered wave-4 + hardening arm). +- `review-wave-6` — the born-pinned gate: conformance read, third- + column discharge audit, wasm acceptance audit, spec disposition; + depends on integration + the rider so the window closes everything. + +Parallelism note: the foundation task blocks the seam; the seam blocks +the mechanisms; the mechanisms are mutually parallel. The suite pre-task +and the Findings-1 rider are independent of the mem chain entirely and +can run any time in the window (the rider must close before the gate). + ## Wave 7 — Release readiness The original wave 6, renumbered when the mem engine inserted at wave @@ -446,6 +490,12 @@ decomposes. Specific gates: structural twin; wave-3 outcomes absorbed (constructors exist; arithmetic re-owned per engine; per-engine `@every` parser; SQLite-scoped deferred notes stay put). See the Wave 4 section. +- **Wave 6 decomposition** (2026-10-10) — ADR-024 adopted the mem + engine the same day the wave-5 gate closed; the task set follows the + pg wave's rhythm with the tx overlay as the load-bearing task, the + suite's third column as the born-pinned deliverable, the harness's + async-sleep pre-task, and review 003 Finding 1 riding the window. + See the Wave 6 section. - **Wave-4 fix-batch decomposition** (2026-10-09) — the general review's findings decomposed into seven `pg-fix-*` tasks + a `review-wave-4-fixes` gate: Finding 1 → `pg-fix-forwarder-reconnect` diff --git a/tasks/mem-engine-foundations.md b/tasks/mem-engine-foundations.md new file mode 100644 index 0000000..7e0bdc3 --- /dev/null +++ b/tasks/mem-engine-foundations.md @@ -0,0 +1,92 @@ +--- +id: mem-engine-foundations +name: Mem engine — crate scaffold, `MemStore::new()`, guarded state core, wake routing +status: pending +depends_on: [] +scope: moderate +risk: medium +impact: component +level: implementation +tags: [wave-6, mem-engine] +--- + +## Description + +The mem engine's foundations (wave 6 — [ADR-024](../architecture/decisions/024-mem-engine.md)): +a new workspace member `alkstore-mem` and the state core every later +task builds on. + +- **Crate + manifest**: workspace member per + [ADR-024](../architecture/decisions/024-mem-engine.md) §1 — + dependencies are `alkstore` (versioned path pin per + [ADR-017](../architecture/decisions/017-contract-versioning.md) §4.1, + matching the other engines' manifests) plus tokio restricted to the + **wasm-supported subset (sync + time only)**. No driver + dependencies. `thiserror` via core; no other crates.io surface. +- **Constructor**: `MemStore::new()` — engine-native, no connection + string, no file path (the one documented exception to the + core-contract open prose, amended by + [ADR-024](../architecture/decisions/024-mem-engine.md) §4). Each + `new()` yields a fully isolated instance sharing nothing (the suite + factory's isolation contract); teardown is drop. No `PgOpts`-equivalent + opts struct exists — nothing to configure (if a knob surfaces during + the wave, `MemOpts` with `..Default::default()` follows the family + opts pattern; not assumed now). +- **Guarded per-mechanism state core**: the store instance is the + state — per-mechanism structures (queues+jobs, dead letters, + streams+offsets, locks, schedules) behind the family's locking + discipline (parking_lot short-held internal locks per AGENTS + convention 3; tokio primitives for cross-await coordination). + Layout is module-per-file under `src/`, mirroring the other + engines. Subsequent mechanism tasks own their tables' shapes; this + task owns the shared scaffolding: the guarded-state container idiom, + name validation at entry (same `InvalidName`/`ReservedName` rules as + every other engine — reserved prefix `__alkstore_`, the one + reserved string `__alkstore_listener_reconnected__` unused here but + still rejected), and the wake-routing substrate (in-process channels; + a mechanism registers its commit-ordered wake source, receivers + attach per channel). +- **Wake substrate shape**: no batching boundary exists — wakes + deliver at the commit boundary directly to each open receiver, + per-subscriber FIFO by commit order (engine-mem.md §Architecture; + coalescing not applicable). The routing core lives here; the + `listen()`/`WakeReceiver` surface lives in `mem-engine-notify-listen`. +- **Clock posture**: every timestamp (unix-seconds stamps, visibility + deadlines, TTLs, scheduler boundaries) reads the process clock — + there is no server clock to defer to. Keep the clock behind one + internal accessor so tests can observe timestamps deterministically + the same way the SQL engines' row shapes do. + +The `Store` trait impl itself is ADR-007-shaped and lands in +`mem-engine-seam-tx` (the tx overlay is the load-bearing half); +this task lands everything up to the trait boundary. + +## Acceptance Criteria + +- [ ] `alkstore-mem` is a workspace member compiling clean; manifest + dependency surface is core + tokio (sync + time) only +- [ ] `MemStore::new()` constructs; two instances share nothing + (pinned by a unit test) +- [ ] The guarded state core, validation rules, wake-routing + substrate, and clock accessor exist with unit pins +- [ ] `cargo check --target wasm32-unknown-unknown -p alkstore-mem` + clean from this task onward (tokio subset discipline enforced + here, not retro-fitted) +- [ ] Workspace gates green (`cargo build`, `cargo test`, clippy + `-D warnings`, fmt) + +## References + +- docs/architecture/decisions/024-mem-engine.md §1/§4 +- docs/architecture/engine-mem.md §Identity and posture, §Architecture +- docs/architecture/core-contract.md §Naming / reserved namespace +- tasks/pg-engine-schema.md (the wave-4 rhythm this wave mirrors — its + "foundations first" slot) + +## Notes + +> To be filled by implementation agent + +## Summary + +> To be filled on completion \ No newline at end of file diff --git a/tasks/mem-engine-integration.md b/tasks/mem-engine-integration.md new file mode 100644 index 0000000..0186d71 --- /dev/null +++ b/tasks/mem-engine-integration.md @@ -0,0 +1,95 @@ +--- +id: mem-engine-integration +name: Mem engine — integration (full-surface wiring, suite third column, wasm32 acceptance) +status: pending +depends_on: [mem-engine-notify-listen, mem-engine-streams, mem-engine-queues, mem-engine-locks, mem-engine-scheduler-outbox, mem-engine-seam-tx, suite-harness-current-thread] +scope: moderate +risk: medium +impact: phase +level: implementation +tags: [wave-6, mem-engine, integration] +--- + +## Description + +The integration task: wire the finished mechanisms into a complete +`Store`, adopt the contract suite as the third column, and carry the +wasm acceptance items through ([ADR-024](../architecture/decisions/024-mem-engine.md) +§5; the born-pinned posture — engine-mem.md §Isolation and the suite +factory): + +- **Full-surface wiring**: every handle constructible off one + `MemStore`; trait-object plumbing; crate docs (module docs + a + crate-level doc carrying the honest-ephemeral posture statement — + no durability, single-process, ephemeral by design, never + fleet-valid, per [ADR-024](../architecture/decisions/024-mem-engine.md) + §2 — and the wasm identity line per engine-mem.md §Identity). +- **Suite third column**: a `tests/contract_suite.rs` target + implementing `StoreFactory` (fresh `MemStore::new()` per run, + teardown = drop — the factory's isolation contract verbatim) and + running the shared rows wired per engine: mem's column carries the + mechanism rows that express on it and omits the engine-shaped rows + that cannot (reconnect-wake, watcher-death close, watcher-cadence — + the per-engine wiring the suite's rows already use; + `PayloadTooLarge`'s non-occurrence arm *wires* — the arm is the + pin). The column runs under a **current-thread runtime flavor** + (the pre-task's async-sleep harness makes this clean). +- **wasm32 acceptance**: `cargo check --target wasm32-unknown-unknown + -p alkstore-mem` (and the workspace's other crates untouched) + clean; the manifest's tokio features verified as the sync+time + subset. On-target suite execution stays scoped out (the named + collapse condition — a wasm-bindgen-test adapter crate — recorded in + the task Notes at completion, not an OQ). +- **No `spawn_blocking` anywhere**: grep-verified; internals + current-thread-clean by construction, now proven by the flavor the + column runs under. +- Suite column green is the wave's center-of-mass: 25 rows' worth of + contract v1 assertions against a third, independent implementation — + any red row is either a mem defect or (if the row pins + implementation detail a correct engine could fail) a found suite + defect to raise with the reviewer *before* editing the row (rows pin + ADR text; a wobble is amend-in-place material under the class-1 + window). + +## Acceptance Criteria + +- [ ] `StoreFactory` implemented for the mem engine; the third suite + column wired and **green on the host** (same row set per the + wiring table above) +- [ ] The column runs under the current-thread flavor; no blocking + calls in mem internals (grep-verified) +- [ ] `cargo check --target wasm32-unknown-unknown -p alkstore-mem` + clean +- [ ] `engine-mem.md` updated from `draft`-initial to + implementation-complete (the spec grows with the wave per its + frontmatter note); crate docs carry the posture + wasm identity +- [ ] Workspace gates green; clippy `-D warnings`; fmt clean +- [ ] The class-1 window discipline respected: any suite-row change + or contract wobble surfaced to the reviewer, not silently + patched + +## Dependencies note + +`mem-engine-seam-tx` is listed because the overlay's commit-merge and +`*_tx` read shapes are what the suite's tx rows exercise on mem; the +mechanism tasks consume it, but a late seam fix may be needed if the +suite surfaces one — the task owns absorbing it. + +## References + +- docs/architecture/decisions/024-mem-engine.md §5 (acceptance items); + 022-contract-suite-layout.md (the factory both engines already + implement — the shape to mirror) +- docs/architecture/engine-mem.md (spec of record) +- tasks/sqlite-engine-integration.md / tasks/pg-engine-integration.md + (the structural twins' integration tasks of record) +- tasks/review-wave-5.md appendix (the discharge map the third column + rides) + +## Notes + +> To be filled by implementation agent + +## Summary + +> To be filled on completion \ No newline at end of file diff --git a/tasks/mem-engine-locks.md b/tasks/mem-engine-locks.md new file mode 100644 index 0000000..c125234 --- /dev/null +++ b/tasks/mem-engine-locks.md @@ -0,0 +1,65 @@ +--- +id: mem-engine-locks +name: Mem engine — named locks (in-memory TTL registry) +status: pending +depends_on: [mem-engine-seam-tx] +scope: narrow +risk: low +impact: component +level: implementation +tags: [wave-6, mem-engine, locks] +--- + +## Description + +The named-locks mechanism over an in-memory TTL registry +(engine-mem.md §Mapping the contract, locks row): + +- `store.try_lock(name, owner, ttl)` — acquire or fail (`Option + `; no-work is a value); validation at entry (empty → `InvalidName`, + reserved → `ReservedName`); **duration guard at entry** — + `ttl <= 0` rejects with opaque `Err` + ([ADR-023](../architecture/decisions/023-fourth-review-round.md) §2, + durations kind), never a silent immediately-expired lease. +- **`Lock` handle** ([ADR-019](../architecture/decisions/019-mechanism-handle-surfaces.md)): + `renew(ttl)` (absolute reset; renews past expiry refused) and + `release`. Registry entries carry owner + deadline; expiry is a + clock check at try/renew boundaries (tokio time; no ambient + revoker) — after TTL expiry exclusion lapses *silently* (the + ADR-008 §7 row), re-acquirable; the holder may still release. +- **Single-instance contention semantics**: two `try_lock`s on the + same name within the registry are serialized by the guarded-state + discipline — first wins, second gets `None` cleanly (the SQLite + busy-path question has no mem analog; loser is a value, never an + error). Not part of the tx seam (no `lock_tx` — contract-verbatim: + a lock held across a business tx is held by explicit acquisition; + the overlay neither sees nor stages locks). + +## Acceptance Criteria + +- [ ] `try_lock`/`renew`/`release` over the TTL registry; entry + validation (names + duration guard) — pinned +- [ ] TTL expiry: silent lapse, re-acquisition, renew-past-expiry + refused — pinned (clock-controlled tests via the state core's + clock accessor) +- [ ] Concurrent-contention shape: loser is `None`, winner's lease + intact — pinned +- [ ] `cargo check --target wasm32-unknown-unknown -p alkstore-mem` + clean; workspace gates green + +## References + +- docs/architecture/core-contract.md §named locks +- docs/architecture/decisions/008-contract-v1-pinning.md §7; + 019-mechanism-handle-surfaces.md; 023 §2 +- docs/architecture/engine-mem.md §Mapping the contract (locks row) +- tasks/pg-engine-locks.md (the structural twin's task of record — + the duration guards land identically) + +## Notes + +> To be filled by implementation agent + +## Summary + +> To be filled on completion \ No newline at end of file diff --git a/tasks/mem-engine-notify-listen.md b/tasks/mem-engine-notify-listen.md new file mode 100644 index 0000000..b5ee833 --- /dev/null +++ b/tasks/mem-engine-notify-listen.md @@ -0,0 +1,73 @@ +--- +id: mem-engine-notify-listen +name: Mem engine — notify/listen surface (`WakeReceiver`, close-at-engine-drop) +status: pending +depends_on: [mem-engine-seam-tx] +scope: narrow +risk: medium +impact: component +level: implementation +tags: [wave-6, mem-engine, notify] +--- + +## Description + +The notify/listen mechanism over the state core's wake-routing +substrate (engine-mem.md §Architecture): + +- `store.notify(channel, payload)` — validation like every engine + (empty → `InvalidName`, reserved → `ReservedName`); encode via + core's `encode_payload` (`Codec` on failure, ADR-023 §1); delivers + committed-notify wakes to each open receiver on that channel at the + commit boundary, per-subscriber FIFO by commit order, never + coalesced (no batching boundary exists). No size limit at any size — + `PayloadTooLarge` is never produced (engine asymmetry classification, + [ADR-024](../architecture/decisions/024-mem-engine.md) §3). +- `store.listen(channel) -> Box` — starts from + "now" (attaches at the current commit watermark; a listener attached + after a commit never sees it). Delivers opaque + `Wake { channel }` signals — `recv`/`try_recv`/`recv_timeout` forms + per the pinned shapes ([ADR-008](../architecture/decisions/008-contract-v1-pinning.md) + §3). +- `notify_tx` — staged in the tx overlay by `mem-engine-seam-tx`; this + task's contract obligation is the close arms: **one close path — + engine drop closes open receivers** (process exit/shutdown; + terminal `recv() -> None`, cause = the documented engine asymmetry + realized as drop). No reconnection concept exists; the + reconnect-wake channel is never emitted (the reserved string stays + reserved but unused — and still rejected as a consumer name). + +Unit tests pin: channel-scoped delivery + foreign-channel silence +(with the field carried, per the suite row's scoping observable), +burst floor (≥1 wake), attach-after-commit no-replay, close-at-drop +terminal `None`, commit-boundary delivery timing through +`notify_tx` (nothing pre-merge). + +## Acceptance Criteria + +- [ ] listen/notify surface trait-complete with entry validation and + core payload encoding +- [ ] Wake delivery: commit-ordered, channel-scoped, no-replay, + never coalesced — unit-pinned +- [ ] Engine drop closes open receivers (terminal `None`) — pinned +- [ ] `notify_tx` delivers wakes only after the overlay merge — + pinned with the seam task's commit-atomicity tests +- [ ] `cargo check --target wasm32-unknown-unknown -p alkstore-mem` + clean; workspace gates green + +## References + +- docs/architecture/core-contract.md §notify / listen +- docs/architecture/decisions/006-wake-and-delivery-contract.md; + 008-contract-v1-pinning.md §3; 021-tx-reads-and-value-shape-fixes.md §5 +- docs/architecture/engine-mem.md §Architecture, §Asymmetry classification +- tasks/suite-wake-rows.md (the suite row the mem column will run — + the observable shapes it pins) + +## Notes + +> To be filled by implementation agent + +## Summary + +> To be filled on completion \ No newline at end of file diff --git a/tasks/mem-engine-queues.md b/tasks/mem-engine-queues.md new file mode 100644 index 0000000..3ad9063 --- /dev/null +++ b/tasks/mem-engine-queues.md @@ -0,0 +1,86 @@ +--- +id: mem-engine-queues +name: Mem engine — queues (in-memory job table, ADR-010 state machine, engine-owned backoff curve) +status: pending +depends_on: [mem-engine-seam-tx, mem-engine-notify-listen] +scope: moderate +risk: medium +impact: component +level: implementation +tags: [wave-6, mem-engine, queues] +--- + +## Description + +The queue mechanism: the +[ADR-010](../architecture/decisions/010-queue-semantics-depth.md) state +machine realized over an in-memory job table — all driver-free. + +- **`Queue` handle surface** ([ADR-019](../architecture/decisions/019-mechanism-handle-surfaces.md) + §1): `enqueue` (auto-commit, resolving over the handle's opts), + `claim_one`/`claim_batch` (extents: `n <= 0` claims nothing), + `ack_batch` (per-id uniform predicate, lapsed/non-claimed silently + not counted), `cancel` (unconditional delete), `get_job`, and the + queue-scoped `sweep_expired` (no-stranded-rows: *every* past-expiry + row in any state moves to dead + retention enforcement). +- **Claim discipline**: exactly-once handout, `worker_id` claimant + identity (non-empty), claim ordering priority DESC → ready-time → + enqueue FIFO. Visibility expiry and reclaim — **reclaim consumes an + attempt**; the dual-execution window is contract-honest (a worker + past its deadline may complete its work but its ack will not land). + Under a single engine instance the claim race is in-process — the + guarded merge discipline must still make claim-vs-claim atomic and + claim-vs-expiry deterministic at the row level. +- **JobHandle ops** ([ADR-019](../architecture/decisions/019-mechanism-handle-surfaces.md) + §3): `ack`/`retry`/`fail` one-shot terminal, `heartbeat(&self)` + repeatable absolute-reset renewal; the uniform validity predicate + (processing + unexpired deadline); deadline lapse refuses all ops, + false not error. `retry(err, None)` computes the queue's + **equal-jitter exponential delay — mem's own implementation** + ([ADR-012](../architecture/decisions/012-forked-substrate-design.md) + §2's one-owner rule, third owner; the SQLite engine's `resolution.rs` + is reference, not a dependency); `retry(err, Some(d))` overrides + verbatim; `fail` = immediate dead-letter with the engine-default + strings (`"failed"` / `"max attempts exceeded"`). +- **Opts stamping** ([ADR-020](../architecture/decisions/020-enqueue-opt-semantics-and-bridges.md)): + resolution rules (delay-over-`run_at`, relative `expires`) are + mem-owned (third copy); handle-enqueue stamps the handle's opts; + the tx/no-handle shapes stamped plain 300/3/5/none in + `mem-engine-seam-tx`. `get_job` shows resolved values. +- **Dead letters**: move-to-dead storage, `get_job` sees dead rows + (`last_error`/`died_at`), `dead_letter_retention_s` (None = forever) + enforced by `sweep_expired`. No redrive API. +- Timestamps and expiry checks are tokio-time/clock reads against the + state core's clock accessor; expiry is *checked* at claim/renew + boundaries (no ambient sweeper — `sweep_expired` is caller-driven, + cadence recipe unchanged). + +## Acceptance Criteria + +- [ ] Full `Queue`/`JobHandle` surfaces over the in-memory job table +- [ ] The ADR-010 state machine properties unit-pinned: visibility + reclaim consumes an attempt, dead-letter moves, no-stranded-rows + sweep, handle-op validity predicate, late-heartbeat boundary +- [ ] Equal-jitter curve + opts resolution pinned to the identical + numeric behavior the suite's equivalence rows assert (per the + engine-side pin posture used for the cap legs) +- [ ] Numeric-domain guards (extents) at entry — pinned +- [ ] `cargo check --target wasm32-unknown-unknown -p alkstore-mem` + clean; workspace gates green + +## References + +- docs/architecture/queues.md; core-contract.md §queues +- docs/architecture/decisions/010-queue-semantics-depth.md; + 019-mechanism-handle-surfaces.md §1/§3; 020 §1–§3; 023 §2 +- docs/architecture/engine-mem.md §Mapping the contract (queues row) +- tasks/pg-engine-queues.md (the structural twin's task of record — + the wave-4 rhythm and the resolution rule set) + +## Notes + +> To be filled by implementation agent + +## Summary + +> To be filled on completion \ No newline at end of file diff --git a/tasks/mem-engine-scheduler-outbox.md b/tasks/mem-engine-scheduler-outbox.md new file mode 100644 index 0000000..f6d696e --- /dev/null +++ b/tasks/mem-engine-scheduler-outbox.md @@ -0,0 +1,87 @@ +--- +id: mem-engine-scheduler-outbox +name: Mem engine — scheduler (`schedule`/`unschedule`/`run_schedules`) and outbox helper +status: pending +depends_on: [mem-engine-queues, mem-engine-locks] +scope: moderate +risk: medium +impact: component +level: implementation +tags: [wave-6, mem-engine, scheduler, outbox] +--- + +## Description + +The collapsed scheduler and the outbox helper +([ADR-009](../architecture/decisions/009-scheduler-collapse.md), +[ADR-014](../architecture/decisions/014-outbox-tx-enqueue.md)): + +- **Scheduler surface**: `schedule(name, spec, queue, payload, opts)` + (upsert by name; *both* name-bearing arguments validated — schedule + name and queue argument identically, `InvalidName`/`ReservedName`), + `unschedule(name) -> bool`, and the opt-in + `run_schedules(stop: StopToken)` runner over a mem-owned tick task. +- **Tick machinery — engine-uniform** ([ADR-009](../architecture/decisions/009-scheduler-collapse.md) + §4's rule; single-instance leadership is trivially owned but the + machinery still runs): each boundary fire enqueues ordinary work + stamped with the schedule's `ScheduleOpts` resolved over the target + queue's derived engine defaults; fire+advance is atomic under the + guarded state (the no-double-fire floor); **bounded catch-up with + the fixed 64-boundary cap and skip-forward past the cap** + (mem-owned tick implementation — third owner per + [ADR-012](../architecture/decisions/012-forked-substrate-design.md) + §2); leadership rides the reserved `__alkstore_scheduler` lock + through the lock mechanism (so two runners on one instance contend + through the same machinery the other engines use; loss → + `Err(LeadershipLost)`, clean stop → `Ok(())`). +- **Spec grammar v1**: `@every ` (`s|m|h|d`) only — mem owns + its parser (third copy, the grammar is small and pinned); cron + strings rejected (`InvalidSpec`). Boundary waits ride tokio time. +- **Outbox helper**: `Outbox { enqueue, run_once }` per + [ADR-019](../architecture/decisions/019-mechanism-handle-surfaces.md) + §5 — `run_once(worker_id, delivery) -> bool` is pull: claim one, + ack on `Ok`, `retry(err, None)` on `Err`; **no engine-issued + heartbeat inside delivery** (inherited posture); + `outbox_enqueue_tx` staged in the overlay by `mem-engine-seam-tx` + (the 60/5/5 stamp set); the derived backing queue + `__alkstore_outbox:{outbox}` unreachable by `enqueue_tx` + (validation already pins the reserved prefix), name format pinned + identical to the other engines (the suite's derived-queue-naming + row composes it). + +Unit tests pin: boundary fires (deterministic, tokio-time driven), +bounded catch-up + skip-forward, leadership lock contention/loss +through two `run_schedules` calls, outbox run_once ack/retry shapes, +derived-name format. + +## Acceptance Criteria + +- [ ] Scheduler surface + tick: boundary fire, catch-up (≤64), + skip-forward — unit-pinned with tokio-time determinism +- [ ] Leadership contention through the `__alkstore_scheduler` lock; + `LeadershipLost` on loss, `Ok(())` on clean stop — pinned +- [ ] Schedule name + queue argument validation; `@every`-only parser + (cron → `InvalidSpec`) — pinned +- [ ] Outbox: `enqueue`/`run_once` semantics (no engine heartbeat); + derived backing-queue name format identical to both engines — + pinned +- [ ] `cargo check --target wasm32-unknown-unknown -p alkstore-mem` + clean; workspace gates green + +## References + +- docs/architecture/queues.md; core-contract.md §scheduler, §outbox +- docs/architecture/decisions/009-scheduler-collapse.md §1/§3/§4/§6; + 014-outbox-tx-enqueue.md; 019 §4/§5; 020 §3 +- docs/architecture/engine-mem.md §Mapping the contract + (scheduler/outbox row) +- tasks/pg-engine-scheduler-outbox.md (the structural twin's task of + record — tick/catch-up/leadership shapes) + +## Notes + +> To be filled by implementation agent + +## Summary + +> To be filled on completion \ No newline at end of file diff --git a/tasks/mem-engine-seam-tx.md b/tasks/mem-engine-seam-tx.md new file mode 100644 index 0000000..06b63b2 --- /dev/null +++ b/tasks/mem-engine-seam-tx.md @@ -0,0 +1,95 @@ +--- +id: mem-engine-seam-tx +name: Mem engine — tx overlay, `Store` surface, `TxHandle` (staged `*_tx`, commit-merge + wake-at-commit, drop = discard) +status: pending +depends_on: [mem-engine-foundations] +scope: moderate +risk: high +impact: component +level: implementation +tags: [wave-6, mem-engine, seam] +--- + +## Description + +The seam task, and the wave's load-bearing artifact +(engine-mem.md §The tx overlay): the `Store` trait impl plus the tx +overlay's full discipline. + +- **`Store` surface**: the trait methods (`notify`, `stream`, + `queue`, `outbox`, `try_lock`, `listen`, `schedule`, `unschedule`, + `run_schedules`, `begin_tx`, `with_tx`) implemented over the + state core. `queue(name, opts)`/`outbox(name)` create/return + handles whose opts ride the handle (queues are names, not config + owners — the §3a stamp rules apply identically). Mechanism handles + are core-owned trait objects; engine types never appear in + signatures. +- **The tx overlay**: `begin_tx()` allocates a handle-private overlay. + All eleven `*_tx` methods stage effects in it — `enqueue_tx`, + `publish_tx`, `publish_with_key_tx`, `notify_tx`, + `save_offset_tx`, `get_job_tx`, `get_offset_tx`, `read_since_tx`, + `read_from_consumer_tx`, `outbox_enqueue_tx`, `commit`. Reads *inside* the tx merge the overlay over committed state (read- + your-own-writes, no round-trips: the tx-read rows' + `get_job_tx`/`read_since_tx` behaviors come free, but pin them). + Opt-stamping of staged effects runs at *enqueue* time on the + overlay (resolution identical to the no-handle paths, per + [ADR-020](../architecture/decisions/020-enqueue-opt-semantics-and-bridges.md) + §3: `enqueue_tx` stamps plain derived defaults 300/3/5/none; + `outbox_enqueue_tx` the 60/5/5 set). +- **Commit-merge + wake-at-commit**: commit merges the overlay + atomically (single guarded merge) and fires the overlay's pending + wakes *in the same operation* — a tx-staged notify delivers nothing + until the merge (the discipline pg got wrong and fixed via + `pg-fix-tx-wake`; here structural, and pinned from birth by the + suite's tx-commit-atomicity rows once integration wires them). +- **Drop = discard**: dropping the handle without commit/rollback + discards the overlay (rollback explicit form too) — no-ghosts + trivially, uncommitted state never touched committed state. + `with_tx` composition: `Ok` ⇒ commit, `Err`/drop/panic ⇒ rollback + (the N-5 panic posture applies; overlay discard must be panic-safe + through the unwind's drop). +- **Numeric domains** ([ADR-023](../architecture/decisions/023-fourth-review-round.md) + §2): validation at the trait-impl entry, before any storage-layer + effect — extents clamp empty, durations reject, boundaries total. + Same entry-point rule as the other engines, realized engine-side. + +Engine-internal unit tests pin the overlay discipline directly +(merge atomicity, wake-after-not-before, drop-discard, panic-through- +unwind) in the family's mechanism-test posture; the suite column's +cross-engine rows land in `mem-engine-integration`. + +## Acceptance Criteria + +- [ ] The full `Store`/`TxHandle` trait surfaces implemented over + guarded state; no engine types in consumer signatures +- [ ] Overlay staging → commit-merge → wake-at-commit pinned by unit + tests (a tx-staged notify delivers nothing pre-commit, one wake + post-commit; enqueue/publish commit-atomicity shapes) +- [ ] Drop-without-commit and explicit rollback leave zero residue; + `with_tx` panic rolls back through the unwind's drop +- [ ] Tx reads see staged effects; other views see none pre-commit; + post-rollback reads' subjects gone (no-ghosts for reads) +- [ ] Numeric-domain guards at entry (extents/durations/boundaries) + pinned per [ADR-023](../architecture/decisions/023-fourth-review-round.md) §2 +- [ ] `cargo check --target wasm32-unknown-unknown -p alkstore-mem` + clean; workspace gates green + +## References + +- docs/architecture/core-contract.md §The seam; §Cross-cutting contracts +- docs/architecture/decisions/007-transactional-seam.md; + 014-outbox-tx-enqueue.md; 020-enqueue-opt-semantics-and-bridges.md + §3; 021-tx-reads-and-value-shape-fixes.md; 023-fourth-review-round.md §2 +- docs/architecture/engine-mem.md §The tx overlay (the spec of record) +- tasks/pg-fix-tx-wake.md (the failure shape the overlay makes + structurally impossible) +- tasks/review-wave-5.md appendix (the suite rows whose mem wiring + consumes this task) + +## Notes + +> To be filled by implementation agent + +## Summary + +> To be filled on completion \ No newline at end of file diff --git a/tasks/mem-engine-streams.md b/tasks/mem-engine-streams.md new file mode 100644 index 0000000..0022023 --- /dev/null +++ b/tasks/mem-engine-streams.md @@ -0,0 +1,80 @@ +--- +id: mem-engine-streams +name: Mem engine — streams (in-memory log, per-consumer offsets, trim) +status: pending +depends_on: [mem-engine-seam-tx, mem-engine-notify-listen] +scope: moderate +risk: medium +impact: component +level: implementation +tags: [wave-6, mem-engine, streams] +--- + +## Description + +The streams mechanism over an in-memory event log + per-consumer +offset table (engine-mem.md §Mapping the contract, streams row): + +- **`StreamHandle` surface** ([ADR-019](../architecture/decisions/019-mechanism-handle-surfaces.md) + §2): `publish` / `publish_with_key` (present key must be non-empty + → `InvalidName`), `read_since` / `read_from_consumer` (cursor reads, + `offset ASC`, extents clamp empty per + [ADR-023](../architecture/decisions/023-fourth-review-round.md) §2), + `save_offset` / `get_offset` (monotone saves — below-checkpoint + saves are silent no-ops, `Err` arm carries `Database` only), + `trim_to` (delete `offset <= horizon`, exact-boundary, returns + deleted count, negative horizon deletes nothing idempotently, + **wakes nothing**, surviving rows keep offsets, never renumbered), + and `subscribe` (durable consumption: attach, read to tail, resume + from saved offset; wake-driven with re-read — the wake substrate + delivers at commit; explicit saves only, no auto-checkpoint). +- **Ordering**: engine-assigned monotonically increasing per-stream + offsets, global FIFO by offset — the ADR-015 ordering row, mem's own + counter (per-stream; absolute values need not equal the SQL engines' + — the equivalence rows pin relative order). +- **`StreamEvent` shape** exactly as pinned + ([ADR-015](../architecture/decisions/015-streams-depth.md) §3): + `{ offset, stream, key, payload, created_at }` — `created_at` is + unix-seconds at publish, informational, from the clock accessor. + Reads from a trimmed-away region resume at the horizon's first + remaining row. +- **Keyed publish rides the tx overlay** (staged `publish_with_key_tx` + landed in `mem-engine-seam-tx`); this task's non-tx forms, receiver, + and trim live here. +- **Receiver close arms** ([ADR-021](../architecture/decisions/021-tx-reads-and-value-shape-fixes.md) + §5): `recv()`'s `Err` carries `Database` only (payloads cross raw; + `Codec` at `payload_as`); close is terminal — the one close path + is engine drop (the notify-listen task's close arm, realized + identically); no reconnection concept (the pg stays-open-across- + reconnect row is absent from mem's column). + +## Acceptance Criteria + +- [ ] Full `StreamHandle` surface over the in-memory log + + per-consumer offsets +- [ ] The ADR-015 properties unit-pinned: global FIFO, immutable + never-renumbered offsets (gaps legal post-trim), exact-boundary + trim semantics (reads resume at horizon; saved sub-horizon + offsets stay valid; trim wakes nothing), monotone save + composition (direct + receiver forms interleave) +- [ ] Key round-trips exactly (`None` vs `Some`); empty-`Some`-key + rejected at every entry point +- [ ] Extent/offset numeric-domain guards at entry — pinned +- [ ] `cargo check --target wasm32-unknown-unknown -p alkstore-mem` + clean; workspace gates green + +## References + +- docs/architecture/core-contract.md §streams +- docs/architecture/decisions/015-streams-depth.md; + 019-mechanism-handle-surfaces.md §2/§6; 021 §5; 023 §2 +- docs/architecture/engine-mem.md §Mapping the contract (streams row) +- tasks/pg-engine-streams.md (the structural twin's task of record) + +## Notes + +> To be filled by implementation agent + +## Summary + +> To be filled on completion \ No newline at end of file diff --git a/tasks/pg-fix-open-path.md b/tasks/pg-fix-open-path.md index 272c8e0..0795c23 100644 --- a/tasks/pg-fix-open-path.md +++ b/tasks/pg-fix-open-path.md @@ -1,6 +1,6 @@ --- id: pg-fix-open-path -name: Fix `max_size: 0` open hang + DSN options override (review 002 Finding 3 + options note) +name: "Fix `max_size: 0` open hang + DSN options override (review 002 Finding 3 + options note)" status: completed depends_on: [] scope: single diff --git a/tasks/pg-fix-scheduler-tick-seam.md b/tasks/pg-fix-scheduler-tick-seam.md new file mode 100644 index 0000000..b35f9bf --- /dev/null +++ b/tasks/pg-fix-scheduler-tick-seam.md @@ -0,0 +1,68 @@ +--- +id: pg-fix-scheduler-tick-seam +name: "pg scheduler — transient-fault tick seam: retry/exhaustion arms tested (review 003 Finding 1)" +status: pending +depends_on: [] +scope: narrow +risk: low +impact: component +level: implementation +tags: [wave-6, postgres-engine, scheduler, test-hardening] +--- + +## Description + +Review 003 Finding 1 (MEDIUM — `docs/reviews/003-wave-5-general-review.md`): +the pg scheduler's transient-error retry loop +(`alkstore-postgres/src/scheduler.rs:680-704`, `tick_with_retries`) +has never executed under any test — `pg-fix-scheduler-resilience` +pinned its *constants* (`the_tick_retry_policy_is_pinned`) but the +loop that uses them (retry, backoff sleeps, exhaustion error, the +`eprintln` diagnostic — lcov 171-184, 237-260) is dead code under +test, with no fault seam anywhere in the pg scheduler for `tick_once`. +This is the last uncovered hardening arm from the wave-4 fix batch, +and the exact class that produced review 002's live bugs. + +Fix shape (the review's recommendation): a `cfg(test)` transient- +fault seam on the pg tick path, following the `sqlite-commit-error-arm +` precedent — a one-shot armed flag feeding a fabricated +transient error into the *real* retry loop (never a replica): + +- (a) one retried tick then success: the seam faults once, the next + `tick_once` succeeds, the job processes — the retry + backoff-sleep + arms execute for real; +- (b) exhaustion: the seam faults the max retry count, the loop + exhausts to the typed error shape and the `eprintln` diagnostic + fires, the runner survives (the resilience property the fix + promised). + +Cross-test safety is per-store scoping (the commit-fault seam's +pattern); no production-path changes — the seam is cfg-gated, born +disarmed, consumed-on-use. + +## Acceptance Criteria + +- [ ] The seam lands cfg(test)-gated; the real `tick_with_retries` + executes under test (no loop replication) +- [ ] Arms (a) and (b) pinned as above; the runner survives + exhaustion +- [ ] `cargo test -p alkstore-postgres` green against the harness + server; skips clean server-less +- [ ] Clippy `-D warnings`, fmt clean; workspace gates green + +## References + +- docs/reviews/003-wave-5-general-review.md §Finding 1 (sites, lcov + inventory, recommendation) +- docs/architecture/decisions/024-mem-engine.md (the wave-6 window + this rider rides — the plan's wave-6 section, "Riding the window") +- tasks/sqlite-commit-error-arm.md (the seam pattern of record) +- tasks/pg-fix-scheduler-resilience.md (the constants the loop uses) + +## 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..9e43e67 --- /dev/null +++ b/tasks/review-wave-6.md @@ -0,0 +1,81 @@ +--- +id: review-wave-6 +name: Review gate — wave 6 (mem engine) +status: pending +depends_on: [mem-engine-integration, pg-fix-scheduler-tick-seam] +scope: moderate +risk: low +impact: phase +level: review +tags: [wave-6, review] +--- + +## Description + +The wave-6 review gate (the plan's §Review gates, wave 6; the +born-pinned gate). Code-read the `alkstore-mem` engine against the +contract and the pinned ADR-024 text, and audit: + +1. **Conformance read**: engine-vs-contract line by line, mirroring + the wave-3/4 gate pattern — every trait surface against + core-contract.md and the pinning ADRs (007/008/009/010/015/019/ + 020/021/023), the tx overlay against ADR-024 + engine-mem.md §The + tx overlay (commit-merge atomicity, wake-at-commit, drop = discard, + panic-through-unwind). +2. **The third column as the compatibility instrument born pinned**: + the mem column's wiring read row-by-row against the discharge map + (which rows carry, which engine-shaped rows omit and why — the + asymmetry classification, ADR-024 §3); `PayloadTooLarge` + non-occurrence arm present; three-way arithmetic equivalence + actually asserted green (backoff curve, opts resolution — the + ADR-012 §2 one-owner rule's third owner), plus the derived-queue + naming row composing identically. +3. **wasm32 acceptance audit** (ADR-024 §5): the compile gate clean, + tokio feature subset verified on the manifest, no blocking calls + in mem internals, the current-thread flavor actually enforced by + the column's tests. +4. **engine-mem.md read against the implemented engine**; the + deployment matrix's mem rows (`deployment.md`, landed with ADR-024) + still true as implemented (honesty carriers: identity + docs + + matrix — nothing runtime invented). Gate may flip `engine-mem.md` + to `stable` if the read is clean. +5. **The rider closed**: `pg-fix-scheduler-tick-seam`'s arms verified + (review 003 Finding 1 discharged into this window; Findings 2–3 + confirmed still parked for wave 7). +6. **Contract-window discipline**: the class-1 amend-in-place window + is still open — any wobble the third implementation surfaced must + appear as resolved ADR text (or be recorded as a finding with its + disposition), not as a silent divergence between suite row and + engine behavior. + +## Acceptance Criteria + +- [ ] Full conformance code-read recorded (findings + dispositions + inline in this task's Notes, mirroring review-wave-3/4 style) +- [ ] Suite mem column green on host, 25-row set wired per the + classification; three-way equivalence rows green +- [ ] wasm32 compile gate + feature-subset + current-thread flavor + verified, recorded +- [ ] The rider's arms verified; Finding 1 disposition recorded +- [ ] Workspace gates re-run green this session (`cargo build`, + `cargo test`, clippy `-D warnings`, fmt) +- [ ] `engine-mem.md` disposition decided (stable flip or follow-up) + and `implementation.md`'s wave-6 row updated + +## References + +- docs/plans/implementation.md §Wave 6, §Review gates (wave 6 entry) +- docs/architecture/decisions/024-mem-engine.md (the charter this + gate audits code against) +- docs/architecture/engine-mem.md +- tasks/review-wave-3.md / tasks/review-wave-4.md (the gate pattern) +- docs/reviews/003-wave-5-general-review.md (Finding 1's disposition + trail; Findings 2–3's wave-7 park) + +## Notes + +> To be filled by implementation agent + +## Summary + +> To be filled on completion \ No newline at end of file diff --git a/tasks/suite-harness-current-thread.md b/tasks/suite-harness-current-thread.md new file mode 100644 index 0000000..33c606e --- /dev/null +++ b/tasks/suite-harness-current-thread.md @@ -0,0 +1,62 @@ +--- +id: suite-harness-current-thread +name: Contract-suite harness — async sleep in `past_stamp_sleep`, current-thread flavor readiness +status: pending +depends_on: [] +scope: narrow +risk: low +impact: component +level: implementation +tags: [wave-6, contract-suite] +--- + +## Description + +The one suite-side change wave 6 carries +([ADR-024](../architecture/decisions/024-mem-engine.md) §5 item 4; the +review-003 posture note at `properties.rs:1683`): the suite's +`past_stamp_sleep` helper uses a blocking `std::thread::sleep` inside +async rows — safe under the multi-thread test flavors both existing +engines' targets use, but it would stall a current-thread runtime. The +mem engine's suite column **must** run under a current-thread flavor +(`#[tokio::test(flavor = "current_thread")]`) to keep the mem +engine's internals honestly current-thread-clean — which is what +wasm32 needs. + +- Replace `past_stamp_sleep`'s blocking sleep with `tokio::time::sleep` + (async). The helper's semantics are unchanged (a bounded + pre-`run_at` settle before reads); the existing rows' determinism + posture is untouched — verify the two legs using it re-run green on + both engines with no timing drift (tolerances unchanged). +- Document (one line in the helper's doc comment or the factory + contract notes) the harness posture: **no blocking calls inside + suite rows** — the mem column enforces the flavor that catches + violations. + +This task is engine-independent and can run any time before +`mem-engine-integration` wires the third column. + +## Acceptance Criteria + +- [ ] `past_stamp_sleep` is async (`tokio::time::sleep`) +- [ ] Both engines' existing suites re-run green with identical + assertions and tolerances (determinism posture intact) +- [ ] The no-blocking-calls posture noted where the harness's + determinism posture is documented +- [ ] Clippy `-D warnings`, fmt clean; workspace gates green + +## References + +- docs/architecture/decisions/024-mem-engine.md §5 +- docs/reviews/003-wave-5-general-review.md §Finding 5 (the + `properties.rs:1683` posture note) +- docs/architecture/decisions/022-contract-suite-layout.md (the + factory contract the flavor rides) + +## Notes + +> To be filled by implementation agent + +## Summary + +> To be filled on completion \ No newline at end of file