core: verify and pin the receiver's pre-yield offset() value (review 004 item 12, core-pin-offset-initial-value) — the three engines verified equal live, so the doc-pin branch applies: offset() reads 0 before any yield on every receiver — fresh consumer, stored positive checkpoint, stored negative seed (ADR-023 §2's totality arm, the one a checkpoint-seeded realization would surface on) — the no-yield sentinel, never the stored checkpoint: the checkpoint gates the feed's attach-read start while the receiver's offset tracks the last-yielded event only, composing with the pinned pre-yield save_offset() no-op; the attach read's prefetches sit unobserved and the first recv/try_recv observation is the first move (then the post-yield value, receiver_close_and_save_arms' pin). Verified by code read first (all three offset() bodies are self.position, all three constructors seed position: 0 unconditionally — mem stream.rs:353, sqlite :399, pg :620) then live: new suite row receiver_offset_pre_yield_is_zero (alkstore-contract-suite, stamped ADR-008 §8 + the core-contract.md pin, four arms incl. the first-observation composition leg) green on all three columns, pg against the harness server (parked pglo-poc :15432); pinned in three places: core-contract.md streams/subscribe row (dated 2026-10-11 annotation) + a verification-backlog bullet recording the verified pin, the core trait doc on offset() (core's doc comments are contract text), and the suite row itself — ADR-008 §8's shape comment deliberately left untouched (doc-only verification takes no ADR amendment, noted in the task); no engine code changed, no divergence to raise; gates green: workspace build/tests server-less, full pg column vs harness (135+26+9), clippy -D warnings, fmt --check, doc gate zero warnings, fuzz corpus replay 6/6, wasm32 mem check, taskgraph validate (96); task -> completed
This commit is contained in:
1 parent
321f579ac2
commit
9301bb926b
8 files changed
+196
-21
No files matched your search
@@ -45,7 +45,8 @@ pub use properties::{
|
||||
payload_round_trip_stores_exact_encoding, payload_too_large_never_produced_on_mem,
|
||||
payload_too_large_never_produced_on_sqlite, payload_too_large_produced_on_pg,
|
||||
publish_with_key_tx_commit_atomicity, queue_depth_reclaim_and_dead_letter,
|
||||
receiver_close_and_save_arms, scheduler_boundary_fires, scheduler_bounded_catchup,
|
||||
scheduler_leadership_discipline, stream_ordering_equivalence, sweep_no_stranded_rows,
|
||||
trim_to_semantics, wake_receiver_shapes, with_tx_panicking_closure_rolls_back,
|
||||
receiver_close_and_save_arms, receiver_offset_pre_yield_is_zero, scheduler_boundary_fires,
|
||||
scheduler_bounded_catchup, scheduler_leadership_discipline, stream_ordering_equivalence,
|
||||
sweep_no_stranded_rows, trim_to_semantics, wake_receiver_shapes,
|
||||
with_tx_panicking_closure_rolls_back,
|
||||
};
|
||||
@@ -1508,6 +1508,88 @@ pub async fn receiver_close_and_save_arms(factory: &dyn StoreFactory) {
|
||||
);
|
||||
}
|
||||
|
||||
/// **Receiver pre-yield `offset()` initial value** — `offset()` reads
|
||||
/// 0 before any event has been yielded, on every receiver: a fresh
|
||||
/// consumer's (no checkpoint), a consumer with a stored checkpoint
|
||||
/// (the attach read prefetches past it unobserved), and a consumer
|
||||
/// with a negative checkpoint (the ADR-023 §2 totality seed — a
|
||||
/// checkpoint-seeded realization would surface it here). The
|
||||
/// pre-yield value is the no-yield sentinel, never the stored
|
||||
/// checkpoint: the checkpoint gates the feed's attach-read start,
|
||||
/// while the receiver's offset tracks the last-yielded event only —
|
||||
/// the pre-yield `save_offset()` no-op (stored checkpoint untouched)
|
||||
/// and the 0 sentinel compose. The first `recv`/`try_recv`
|
||||
/// observation is the first move; the post-yield value is the
|
||||
/// last-yielded event's offset (`receiver_close_and_save_arms`' pin).
|
||||
///
|
||||
/// Contract stamp: ADR-008 §8 (`EventReceiver`'s `offset() -> i64`
|
||||
/// shape, "the receiver's current offset") — the pre-yield return
|
||||
/// pinned in the core-contract.md streams row (annotated 2026-10-11,
|
||||
/// review-004 item 12's named-gap verification).
|
||||
pub async fn receiver_offset_pre_yield_is_zero(factory: &dyn StoreFactory) {
|
||||
let store = factory.open().await.expect("factory opens a store");
|
||||
|
||||
let stream = store.stream("events").await.unwrap();
|
||||
let published = stream.publish(json!({"n": 1})).await.unwrap();
|
||||
|
||||
// Arm 1: fresh consumer — no checkpoint exists at all.
|
||||
let mut fresh = stream.subscribe("fresh-preyield").await.unwrap();
|
||||
assert_eq!(
|
||||
fresh.offset(),
|
||||
0,
|
||||
"a fresh consumer's pre-yield offset() reads the 0 sentinel"
|
||||
);
|
||||
|
||||
// Arm 2: consumer with a stored positive checkpoint — the attach
|
||||
// read starts past it, yet the receiver's offset stays at the
|
||||
// sentinel until an event is observed.
|
||||
stream
|
||||
.save_offset("seeded-preyield", published)
|
||||
.await
|
||||
.unwrap();
|
||||
let seeded = stream.subscribe("seeded-preyield").await.unwrap();
|
||||
assert_eq!(
|
||||
seeded.offset(),
|
||||
0,
|
||||
"a checkpointed consumer's pre-yield offset() reads the 0 sentinel, not the stored checkpoint"
|
||||
);
|
||||
|
||||
// Arm 3: consumer with a stored negative checkpoint (ADR-023 §2's
|
||||
// totality row: the seed stores raw) — the arm where a
|
||||
// checkpoint-seeded realization would diverge.
|
||||
stream
|
||||
.save_offset("seeded-negative-preyield", -5)
|
||||
.await
|
||||
.unwrap();
|
||||
let negative = stream.subscribe("seeded-negative-preyield").await.unwrap();
|
||||
assert_eq!(
|
||||
negative.offset(),
|
||||
0,
|
||||
"a negative-checkpoint consumer's pre-yield offset() reads the 0 sentinel, not the seed"
|
||||
);
|
||||
|
||||
// The first observation is the first move: the fresh receiver's
|
||||
// attach-read-prefetched event yields and the post-yield value
|
||||
// lands the last-yielded offset exactly (the sentinel composes
|
||||
// with the yield discipline).
|
||||
let first = fresh
|
||||
.recv()
|
||||
.await
|
||||
.expect("the fresh receiver is open and holds the prefetched event")
|
||||
.unwrap();
|
||||
assert_eq!(
|
||||
first.offset, published,
|
||||
"the fresh receiver's first yield carries the prefetched event"
|
||||
);
|
||||
assert_eq!(
|
||||
fresh.offset(),
|
||||
published,
|
||||
"the first yield moves offset() off the sentinel to the last-yielded offset"
|
||||
);
|
||||
|
||||
factory.teardown().await.expect("factory teardown");
|
||||
}
|
||||
|
||||
/// **Wake semantics under the pinned `WakeReceiver` shapes** — the
|
||||
/// wake contract's pinnable core, tolerance-bounded (wakes are
|
||||
/// best-effort hints — the row asserts state outcomes, never delivery
|
||||
|
||||
Reference in new issue
Block a user