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:
glm-5.3-flash committed 2026-10-05 12:50:33 +00:00
1 parent 8e68b44194
commit d401908f13
9 files changed
+250 -35

No files matched your search

+115 -5
View File
@@ -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.