fix(wake): #908 unify observed_seq on a single store-side allocator (dissolve detector-private-counter seam) (#915)
Co-authored-by: jason.woltje <jason@diversecanvas.com> Co-committed-by: jason.woltje <jason@diversecanvas.com>
This commit was merged in pull request #915.
This commit is contained in:
@@ -9,11 +9,14 @@
|
||||
# proven delta-gated poller (gitea-pr-watch: poll -> hash -> deliver only
|
||||
# on delta; 0-wasted) and adds (a) repo-section/anchor-scoped hashing so
|
||||
# human-decision FILE edits are caught, not just API-visible state.
|
||||
# §1.2 observe -> assign observed_seq -> enqueue into the W2 durable store.
|
||||
# §2.4 Cursor semantics: `observed_seq` is a detector-local monotonic int
|
||||
# assigned AT OBSERVATION and is AUTHORITATIVE; source SHAs are
|
||||
# DESCRIPTORS, not the cursor. A per-watch LAST-OBSERVED HASH is compared
|
||||
# each poll so a revert (A->B->A across polls) is caught as a delta.
|
||||
# §1.2 observe -> enqueue into the W2 durable store (the store ALLOCATES the
|
||||
# observed_seq — single store-side allocator, #908).
|
||||
# §2.4 Cursor semantics: `observed_seq` is a monotonic int, AUTHORITATIVE, now
|
||||
# allocated SOLELY by store.sh enqueue (#908 unified the two former
|
||||
# allocators). The detector no longer keeps a private counter: it enqueues
|
||||
# and captures the store-assigned seq. Source SHAs are DESCRIPTORS, not the
|
||||
# cursor. A per-watch LAST-OBSERVED HASH is compared each poll so a revert
|
||||
# (A->B->A across polls) is caught as a delta.
|
||||
# §4/G2a FAIL-LOUD source semantics: a source/target failure OR a 401 / 403 /
|
||||
# privacy-404 / partial / ambiguous-empty response MUST fail loud, MUST
|
||||
# NOT advance the authoritative cursor (`observed_seq`), and MUST NOT be
|
||||
@@ -100,8 +103,9 @@ Commands:
|
||||
validate Load + validate the watch-list and its
|
||||
schema_version against the manifest;
|
||||
exit 0 if usable, non-zero (loud) if not.
|
||||
cursors Print the detector-local observed_seq
|
||||
counter and the store cursors.
|
||||
cursors Print the store cursors (observed_seq is
|
||||
the store's single source of truth, #908;
|
||||
the detector keeps no private counter).
|
||||
|
||||
Environment:
|
||||
WAKE_WATCH_LIST Path to the operator watch-list JSON (required).
|
||||
@@ -171,19 +175,15 @@ _load_watchlist() {
|
||||
printf '%s' "$json"
|
||||
}
|
||||
|
||||
# --- detector-local observed_seq (§2.4, authoritative monotonic int) --------
|
||||
|
||||
# _next_observed_seq — atomically increment + return the detector-local counter.
|
||||
# Assigned AT OBSERVATION (only on a real delta). A single global monotonic
|
||||
# sequence across ALL watches (source SHAs are descriptors, NOT the cursor).
|
||||
_next_observed_seq() {
|
||||
mkdir -p "$DET_DIR"
|
||||
local cur nxt
|
||||
cur="$(_wake_read_int "$DET_DIR/observed_seq_counter" 0)"
|
||||
nxt=$((cur + 1))
|
||||
printf '%s' "$nxt" | _atomic_write "$DET_DIR/observed_seq_counter"
|
||||
printf '%s' "$nxt"
|
||||
}
|
||||
# --- observed_seq: the store is the SOLE allocator (#908) -------------------
|
||||
#
|
||||
# The detector NO LONGER keeps a private observed_seq counter. That private
|
||||
# counter was the shared root of three defects (burn-before-enqueue, W5
|
||||
# dual-allocator aliasing, and the migration-restart silent-swallow); see the PR
|
||||
# for #908. observed_seq is now allocated EXCLUSIVELY by store.sh enqueue (which
|
||||
# reads its own cursor and returns the assigned seq). The detector calls enqueue
|
||||
# WITHOUT --seq and captures the store-returned seq for logging/locators — it
|
||||
# never allocates a seq itself, so the seam is gone.
|
||||
|
||||
# --- per-watch last-observed hash (§2.4 revert/ABA-at-rest detection) --------
|
||||
|
||||
@@ -274,10 +274,11 @@ _poll_source() {
|
||||
return 0
|
||||
fi
|
||||
|
||||
# --- DELTA: assign the next observed_seq AT OBSERVATION and enqueue --------
|
||||
# (a revert A->B->A lands here because $h != the stored $last).
|
||||
local seq locators emit_ts
|
||||
seq="$(_next_observed_seq)"
|
||||
# --- DELTA: enqueue and let the STORE allocate observed_seq (#908) ---------
|
||||
# (a revert A->B->A lands here because $h != the stored $last). The store is
|
||||
# the sole allocator: we call enqueue WITHOUT --seq and capture the seq it
|
||||
# assigns (for logging/locators/emit). We do NOT allocate a seq ourselves.
|
||||
local locators emit_ts
|
||||
emit_ts="$(date +%s)"
|
||||
# NB: `def` is a reserved word in jq — the source-definition arg is `$sdef`.
|
||||
locators="$(jq -cn \
|
||||
@@ -288,16 +289,19 @@ _poll_source() {
|
||||
'{kind:$kind, id:$id, observed_hash:$hash}
|
||||
+ ( $sdef | {repo, path, anchor, remote, branches} | with_entries(select(.value != null)) )')"
|
||||
|
||||
local args=(enqueue --seq "$seq" --locators "$locators" --emit-ts "$emit_ts")
|
||||
local args=(enqueue --locators "$locators" --emit-ts "$emit_ts")
|
||||
# Pass class through only when the source declares one; absent => the store
|
||||
# defaults to `actionable` (fail-safe, §2.3). Never guess a coalescible class.
|
||||
[ -n "$class" ] && args+=(--class "$class")
|
||||
if ! "$STORE_SH" "${args[@]}" >/dev/null; then
|
||||
echo "detector.sh: store enqueue FAILED for '$kind/$id' seq $seq" >&2
|
||||
local seq
|
||||
if ! seq="$("$STORE_SH" "${args[@]}")"; then
|
||||
echo "detector.sh: store enqueue FAILED for '$kind/$id' (observed_seq NOT advanced; hash NOT advanced)" >&2
|
||||
return 1
|
||||
fi
|
||||
|
||||
# Only AFTER a durable enqueue do we advance the per-watch last-observed hash.
|
||||
# (store-allocated seq $seq is available for logging/emit if needed.)
|
||||
: "$seq"
|
||||
printf '%s' "$h" | _atomic_write "$hf"
|
||||
return 0
|
||||
}
|
||||
@@ -420,9 +424,9 @@ cmd_run() {
|
||||
}
|
||||
|
||||
cmd_cursors() {
|
||||
local counter
|
||||
counter="$(_wake_read_int "$DET_DIR/observed_seq_counter" 0)"
|
||||
printf 'detector_observed_seq=%s\n' "$counter"
|
||||
# observed_seq has a SINGLE source of truth now (#908): the STORE cursor. The
|
||||
# detector keeps no private counter, so cursors simply reports the store's
|
||||
# authoritative cursors (observed_seq / consumed_seq / pending_depth).
|
||||
"$STORE_SH" cursors 2>/dev/null || true
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user