docs: Phase 1 review round — wake wording honesty, job-handle validity predicate, outbox/streams depth OQs
- C2: 'at-least-once wake delivery' collapsed to 'best-effort hints' (ADR-006 §1 + core-contract) — coalescing and the pg no-replay hole contradict per-commit wake promises; the guarantee table's row was already correct. - W2: uniform job-handle validity predicate pinned (ADR-010 §2, core-contract, queues.md; verification-backlog row): processing state + unexpired deadline; D-12's missing-check not inherited. - W1: outbox run_once worker semantics pinned in core-contract (pull op, ack/retry-on-curve, no heartbeat in delivery — honker parity, dual-execution window documented). - W3: named-locks tx-seam posture stated (no lock_tx; acquisition is auto-commit; TTL discipline governs). - C1 -> OQ-12: streams depth (key semantics w/ honker ground, event shape, ordering row, retention); method names pinned, depth open. - New find -> OQ-13: the v1 TxHandle surface cannot express the transactional outbox enqueue (derived backing-queue name is reserved-prefix-rejected; honker's raw-Transaction seam unavailable); option set + decision rule sketched. - README resolution order refreshed (OQ-06 resolved); ScheduleOpts + stream consumption-trigger lines added to core-contract.
This commit is contained in:
1 parent
8e68b44194
commit
d401908f13
9 files changed
+250
-35
No files matched your search
@@ -26,8 +26,13 @@ ADR-011 substrate fork), OQ-10 (versioning discipline for contract
|
||||
extensions; note ADR-011 changes its substrate-side facts for SQLite —
|
||||
the forked machinery lives in-tree inside the engine crate per
|
||||
[ADR-013](decisions/013-fold-substrate-into-sqlite.md), so the
|
||||
engine/core contract pairing is what the discipline must track), and
|
||||
OQ-11 (fork follow-through items — substrate-side, non-consumer-facing).
|
||||
engine/core contract pairing is what the discipline must track), OQ-11
|
||||
(fork follow-through items — substrate-side, non-consumer-facing),
|
||||
**OQ-12** (streams depth — a Phase 1 review find: key semantics,
|
||||
`StreamEvent` shape, ordering row, retention; evidence gathered), and
|
||||
**OQ-13** (the transactional outbox enqueue shape — a Phase 1 review
|
||||
find: the v1 `TxHandle` surface cannot express the outbox's one
|
||||
load-bearing operation).
|
||||
|
||||
Resolved questions stay listed with their resolution; they are not
|
||||
deleted.
|
||||
@@ -297,9 +302,114 @@ narrowed to the pinning work its own record already scoped.)*
|
||||
carries the fold's provenance duties in-tree), ADR-011, ADR-012,
|
||||
ADR-013.
|
||||
|
||||
### OQ-12: Streams depth — key semantics, `StreamEvent` shape, ordering row, retention
|
||||
|
||||
- **Origin**: [core-contract.md](core-contract.md) streams section,
|
||||
[ADR-008](decisions/008-contract-v1-pinning.md) §1 (Phase 1
|
||||
architecture review, 2026-10-05)
|
||||
- **Status**: open
|
||||
- **Priority**: high (streams is a first-class
|
||||
[ADR-002](decisions/002-feature-scope.md) mechanism; ADR-008's
|
||||
"no placeholder semantics remain" positive consequence is false for
|
||||
`publish_with_key` until this resolves — the contract suite also
|
||||
cannot pin cross-engine stream equivalence without an ordering row)
|
||||
- **Resolution**: open. The method *names* are pinned (ADR-008 §1);
|
||||
the depth is not. Sub-questions, with the evidence already gathered:
|
||||
- **Key semantics** — honker's realization is a stored `key` column
|
||||
(`_honker_stream(topic, key, payload)`, NULLable) with no
|
||||
per-key read path or ordering enforcement anywhere; the honker-rs
|
||||
doc comment says "used for per-key ordering downstream"
|
||||
(`packages/honker-rs/src/lib.rs:894`), i.e. per-key global-FIFO
|
||||
is an *emergent* property (a consumer reading `offset ASC`
|
||||
filtered by key), not server-enforced. Options: (a) pin exactly
|
||||
honker's shape — key is carried metadata, ordering stays global
|
||||
FIFO (document the emergent per-key pattern), cheapest and
|
||||
honest; (b) pin *server-enforced* per-key ordering (a read or
|
||||
delivery guarantee keyed on `key`) — real machinery on both
|
||||
engines, needs a consumer row naming the need; (c) cut
|
||||
`publish_with_key` from v1 (no consumer row names keys — the
|
||||
honker-rs doc comment is upstream's intent, not ours). Decision
|
||||
rule: consumer-inventory row first if (b).
|
||||
- **`StreamEvent` shape** — honker's: `{ offset: i64, topic: String,
|
||||
key: Option<String>, payload, created_at: i64 }` with
|
||||
`payload_as<T>` (upstream lib.rs:1008). The contract needs its own
|
||||
pinned field list (stream name vs topic token, key in or out per
|
||||
the key decision, timestamp semantics) — probably inherited
|
||||
near-verbatim.
|
||||
- **Ordering guarantee row** — ADR-006's table has no streams
|
||||
*ordering* cell content beyond "readable"; pin FIFO-by-offset as
|
||||
the read ordering (`ORDER BY offset ASC` is already both
|
||||
engines' claim path) and whether any per-key strengthening rides
|
||||
the key decision.
|
||||
- **Retention / bounded growth** — the reference read flags
|
||||
`_honker_stream` as unbounded-growth unless the user sweeps
|
||||
(honker-machinery §8.5); no consumer row names stream retention
|
||||
(replay-forever is the *purpose* for the named need —
|
||||
subscription surfaces). Options: consumer-side trim API
|
||||
(`trim_to(offset)` — a new contract method), consumer-recipe
|
||||
only (new stream + advance consumers + unschedule/nothing —
|
||||
needs a deletion path that doesn't exist yet, so this option is
|
||||
really "document the gap"), or document nothing (honest but
|
||||
repeats the notifications-table mistake ADR-010 §6 just fixed).
|
||||
Rides the no-ambient-sweeper posture either way.
|
||||
- **Cross-references**: OQ-04 (the pinning that left this depth
|
||||
open), OQ-01 (scope row), ADR-002, ADR-006 (the guarantee table
|
||||
the ordering row extends), ADR-008 §1.
|
||||
|
||||
### OQ-13: Transactional outbox enqueue shape — the `TxHandle` surface cannot express it
|
||||
|
||||
- **Origin**: [core-contract.md](core-contract.md) (outbox section),
|
||||
[ADR-008](decisions/008-contract-v1-pinning.md) §1 (Phase 1
|
||||
architecture review, 2026-10-05)
|
||||
- **Status**: open
|
||||
- **Priority**: high (the outbox's *only reason to exist* is the
|
||||
commit-atomic enqueue; as pinned, the v1 surface cannot perform it
|
||||
— a decomposition off the current text would produce an
|
||||
unimplementable task)
|
||||
- **Resolution**: open. The hole, stated precisely: the outbox is
|
||||
"enqueue inside the business transaction + delivery workers"
|
||||
([ADR-002](decisions/002-feature-scope.md)); the delivery side is
|
||||
pinned (`run_once`, plus plain queue consumption of the backing
|
||||
queue), but the enqueue side has no method. The derived backing
|
||||
queue name `__alkstore_outbox:{name}`
|
||||
([ADR-008](decisions/008-contract-v1-pinning.md) §4) is
|
||||
reserved-prefix — every entry point **rejects** it, so
|
||||
`handle.enqueue_tx("__alkstore_outbox:x", …)` is contract-illegal
|
||||
by construction; and `Outbox` (the handle `store.outbox(name)`
|
||||
returns) is not in the `TxHandle` method set
|
||||
([ADR-008](decisions/008-contract-v1-pinning.md) §1, §2), so there
|
||||
is no `outbox_enqueue_tx` either. Honker solves it with a
|
||||
different seam: `outbox.enqueue_tx(&tx, …)` routes through the
|
||||
caller's raw `Transaction`
|
||||
(`packages/honker-rs/src/lib.rs:506-513`) — a seam our handle
|
||||
design ([ADR-007](decisions/007-transactional-seam.md),
|
||||
[ADR-008](decisions/008-contract-v1-pinning.md) §2 — the handle
|
||||
*is* the tx, no downcast) deliberately does not have. Options
|
||||
(starter set, not exhaustive):
|
||||
(a) `outbox_enqueue_tx(name, payload)` on the `TxHandle` trait —
|
||||
symmetric with the other `*_tx` methods, one method, engine resolves
|
||||
the derived name on the engine side (bypasses the entry-point
|
||||
rejection legitimately, since the *method* takes the outbox name,
|
||||
not the derived queue name);
|
||||
(b) an `OutboxHandle` obtained from the `TxHandle`
|
||||
(`handle.outbox(name) -> OutboxHandle` with enqueue) — more
|
||||
symmetrical with the store-side handle, two hops;
|
||||
(c) non-transactional only — `outbox.enqueue` auto-commit, dropping
|
||||
the commit-atomic property (defeats the purpose; listed to be
|
||||
rejected explicitly).
|
||||
Decision rule: smallest surface that restores the
|
||||
transactional-local-adjacency property (guiding principle 2) for
|
||||
the outbox without opening the reserved namespace; (a) reads
|
||||
strongest against ADR-008 §2's rationale and needs one contract
|
||||
suite row.
|
||||
- **Cross-references**: OQ-04 (the pinning that missed this),
|
||||
OQ-12 (the sibling depth gap found by the same review), ADR-002
|
||||
(outbox scope row), ADR-007/ADR-008 (the seam design), ADR-010 §3
|
||||
(the outbox's derived QueueOpts — unchanged by this).
|
||||
|
||||
## Deferred / Blocked
|
||||
|
||||
None currently. Every open OQ above is actionable Phase 1 work
|
||||
(capability-surface shape, versioning discipline, fork-scaffold
|
||||
follow-through) with its evidence base complete — no external arrivals
|
||||
are being waited on.
|
||||
(streams depth, outbox enqueue shape, capability-surface shape,
|
||||
versioning discipline, fork-scaffold follow-through) with its evidence
|
||||
base complete — no external arrivals are being waited on.
|
||||
Reference in new issue
Block a user