Files
stack/packages/mosaic/framework/tools/wake/manifest.txt
T
Jason WoltjeandClaude Fable 5 f1b63bccfb docs(wake): #952 the 0.7.1 changelog entry names all THREE fix surfaces
The entry said the fix landed in 'the clean-sweep message + the PROVABILITY
BOUND comment' — two surfaces. It landed in three: usage() came in the
second commit (author-side self-catch) and the entry was never updated.
This PR's whole thesis is naming a set completely; its own permanent record
naming 2 of 3 of its own fix surfaces would reproduce the defect it fixes.
Taken as the pre-merge clause (mos-dt flagged, MOS ruled) — this is the only
window the entry can be made accurate in-place under the changelog-is-history
ruling.

Also rides along, comment-placement-only: the 0.7.1 entry moved above
component= so component=/version= sit adjacent again (the split was verified
inert — _manifest_val's anchored sed cannot match a comment — but placement
is free to fix in the same comment-only commit). Keys re-verified parsing:
component=wake, version=0.7.1, schema 1/1.

Manifest-comment-only: zero code, zero manifest key changes.

Written-by: pepper (sb-it-1-dt)
Co-Authored-By: Claude Fable 5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01NsKce8iZuSuRnu3gVMCBKB
2026-07-30 17:01:23 -05:00

564 lines
41 KiB
Plaintext
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Mosaic wake component — VERSION metadata manifest (Gate B).
#
# EPIC #892, W2 + W3 of the wake/heartbeat canon.
#
# SCOPE — THIS FILE IS VERSION METADATA ONLY. It declares the wake component's
# semantic version and the RANGE of watch-list schema versions it supports. It
# does NOT authorize file/path ownership: path-ownership remains the sole domain
# of packages/mosaic/framework/framework-manifest.txt (Gate A). Do not read any
# ownership meaning into this file.
#
# Format: KEY=VALUE, one per line. '#' and blank lines ignored.
# Component identity + semantic version.
# 0.1.0 W2 — store+drain lib + ack-wrapper.
# 0.2.0 W3 — cumulative-state digest renderer + non-circular HMAC signer.
# 0.3.0 W4 — per-host single-instance delta-gated detector daemon.
# 0.4.0 W5 — synthetic-canary FN-oracle + source-parity reconciler.
# 0.5.0 W6 — off-host dead-man beacon emitter + pluggable alarm-sink adapter
# + beacon-absence alarm (fail-loud on unconfigured/unreachable).
# 0.6.0 W7 — A10 idempotent, fail-closed component installer (Gate-A
# intersect+validate against the framework-manifest SSOT), the
# mosaic-wake.service detector daemon, the blank-reset retire idiom
# for the legacy heartbeat timer + snapshot-guard, and fail-closed
# alarm-target/HMAC-key install-validation. Also folds in the two W6
# monitor-integration observations (monitor-side ingested_ts
# staleness + beacon HMAC-verify at record).
# 0.6.1 #908 — UNIFY observed_seq on a SINGLE store-side allocator. store.sh
# enqueue is now the sole allocator (reads its own cursor, next=+1
# under an exclusive lock, prints the seq; commits IFF the durable
# write succeeds). The detector-private observed_seq_counter and its
# --seq hand-off are DELETED; the reconciler enumerates via the same
# store allocator (its dual-allocator fail-closed guard retired). This
# dissolves the three defects rooted in the private-counter seam:
# burn-before-enqueue (arrow 1), W5 co-feed aliasing (arrow 2), and
# the migration-restart silent-swallow (arrow 3, now structurally
# impossible — allocation is always > consumed or fails loud).
# 0.6.2 #914 digest.sh renderer fixes (live wake-pilot findings): (a) the
# embedded ack copy-run line now bakes an explicit
# WAKE_AGENT=<render-time-agent> prefix (shell-quoted) so an
# env-less copy-run resolves to the correct per-agent namespace
# instead of silently falling back to `default`; (b) the
# ORIENTATION locator renderer now also recognizes the locator
# vocabulary detector.sh (A1) actually emits for a digest-class
# entry (kind/id/observed_hash/remote/path), so a digest-class
# pointer carries a usable (soft) locator instead of rendering
# empty. Display-only: the ACTIONABLE-tier hard-locator FAIL-LOUD
# gate (_has_hard_locator, exit 4) is unchanged.
# 0.6.3 #912 digest.sh scrub PORTABILITY (no contract change): _scrub_ctrl's
# control/bidi/zero-width byte patterns are now LITERAL bytes (via
# printf %b) instead of GNU-sed `\xNN` hex escapes. BusyBox sed (the
# Alpine/musl CI runner, running as root) rejects a `\xNN` character
# range, which aborted the whole scrub sed and silently VOIDED the
# scrub in CI — collapsing every scrubbed value to empty and failing
# the digest suite's D1/D4/D5/D6 only in the Woodpecker runner. The
# scrub now renders byte-identically under GNU sed (glibc dev) and
# BusyBox sed (Alpine CI). The two-tier trust, exit-4 hard-locator
# FAIL-LOUD, and the secret-scrub/SHA-preservation contract are all
# unchanged — this makes the existing scrub deterministic across
# runners, it does not weaken it.
# 0.6.4 #920 digest.sh drain-quarantine + reconciler-enumeration render tier
# (live wake-pilot finding #6, BLOCKING). (a) PER-ENTRY
# QUARANTINE: a render-refused ACTIONABLE entry (no §2.1 hard
# locator) is now DEAD-LETTERED to $STATE_DIR/dead-letter.jsonl +
# a loud per-entry alarm and EXCLUDED, while the REST of the
# cumulative set still renders (exit 0). Replaces the whole-digest
# exit-4 that let ONE malformed entry wedge the entire drain (head-
# of-line blocking — 4 consecutive live timer failures, nothing
# delivered). Fail-loud is preserved, now per-entry; the bad entry
# is never silently dropped. (b) Reconciler ENUMERATIONS render
# ORIENTATION-tier: an entry whose locators carry reconciled==true
# (set only by reconcile.sh) is EXEMPT from the actionable hard-
# locator gate and renders as an orientation pointer via
# _locator_line's digest-class vocabulary — a RENDER-layer change
# only. reconcile.sh's STORE class is UNCHANGED (non-coalescing), so
# distinct enumerations never collapse (§2.3/T2/G3-R6 intact); the
# rejected class=digest alternative would have silently coalesced
# them. store.sh and reconcile.sh are UNCHANGED by 0.6.4.
# 0.6.5 #927 enqueue TOCTOU fix — move stale-tmp cleanup OFF the hot enqueue
# path (no concurrent in-flight-write clobber). cmd_enqueue called
# _wake_init_dir() (which reaped EVERY .wake.tmp.* unconditionally)
# BEFORE taking the enqueue lock, so a 2nd enqueue's PRE-LOCK cleanup
# deleted the LIVE in-flight tmp of a 1st enqueue holding the lock
# through its atomic write -> spurious "durable pending write FAILED"
# abort of a valid enqueue (reachable under live co-feed: detector +
# reconciler concurrently enqueue). FIX (_wake-common.sh): (a)
# _wake_init_dir no longer reaps tmps — it only ensures the layout,
# so nothing on the enqueue/consume/cursors/ack hot paths can clobber
# a concurrent live write; (b) _wake_clean_stale_tmp is AGE-SCOPED
# (mmin +${WAKE_TMP_STALE_MIN:-5}) so it can only remove demonstrably-
# orphaned crash-left tmps, never a live (ms-old) in-flight write.
# Reaping now runs as an explicit MAINTENANCE action at store.sh init
# (daemon-start) and the detector poll tick (detector.sh), keeping
# accumulation bounded once-per-pass instead of raced per-enqueue.
# #908 seq-integrity is UNCHANGED (single store-side allocator,
# atomic allocate+enqueue under flock, arrow-1 no-burn, anti-swallow
# fail-loud). reconcile.sh is UNCHANGED (its enumeration retry is the
# structural recovery net: an aborted enqueue advances neither the
# seen-ledger nor observed_seq, so the source is re-enumerated next
# cycle — no obligation loss). Files changed: _wake-common.sh,
# store.sh, detector.sh (+ tests).
# 0.6.6 #924 digest.sh dead-letter QUARANTINE alarm — G2a fix (wake-pilot
# cure-verification follow-up on #920/PR #922). The #920 per-entry
# quarantine alarm was stderr/journal-LOCAL only; a dead-lettered
# entry is STORE-ACCOUNTED (§2.3) so the reconciler never re-flags
# it, so journal-local-only visibility meant an unattended operator
# could PERMANENTLY MISS a real obligation (G2a silent-degradation).
# FIX: the SAME per-entry quarantine alarm now ALSO routes through
# WAKE_ALARM_SINK_CMD — REUSING beacon.sh's (W6/#910) exact
# pluggable off-host alarm-sink adapter contract (operator target
# resolved by-name inside the adapter, fail-closed) — IN ADDITION
# to (never instead of) the existing stderr diagnostic. Per-
# observed_seq DEDUP (entries carry no per-entry wake_id; the
# entry's durable identity is its store-allocated observed_seq,
# #908) via a durable alarmed-set file under STATE_DIR
# (dead-letter-alarmed.set, atomic-written) ensures a still-dead-
# lettered entry is alarmed off-host EXACTLY ONCE per drain/restart,
# never once per re-render; a NEW distinct dead-lettered entry
# still routes its own one alarm. An unconfigured/unreachable
# WAKE_ALARM_SINK_CMD is a LOUD per-entry stderr diagnostic
# (mirrors beacon.sh's fail-closed wording) but does NOT itself
# fail the whole render (per-entry fail-loud, never a whole-drain
# wedge — #920's core property is preserved). digest.sh is the
# ONLY file changed; store.sh/beacon.sh/reconcile.sh are
# UNCHANGED (beacon.sh's adapter contract is reused, not modified).
# 0.6.7 #913 wake-install.sh installer ADOPTION-GAP fixes (wake-pilot,
# non-blocking, ADDITIVE per #869). (a) DEP-CHECK: the installer
# sourced _lib/manifest.sh (the shared framework-manifest reader it
# needs for Gate A) unconditionally, so an older host seed that
# predates that helper aborted with a bare, obscure
# `source: No such file or directory`. It now checks the library
# FIRST and FAILS LOUD naming the missing file + the remedy (re-seed
# the framework, then retry --component wake) — the dependency is
# genuinely required (Gate A cannot be skipped on an enforcement
# path), so it fails loud rather than degrading. (b) SYSTEMD SEARCH
# PATH: wi_install copies mosaic-wake.service under mosaic home
# (systemd/user/, framework-owned) but `systemctl --user` searches
# ~/.config/systemd/user/, so the unit was invisible and could not be
# enabled/started. install now LINKS the unit into the user systemd
# search path (symlink -> the mosaic-home SSOT copy, so upgrades
# propagate) and VALIDATES it resolves (search-path entry exists,
# dereferences to a readable, well-formed unit; an opportunistic
# `systemctl --user cat` probe runs only behind a guard, since the
# installer may run where no user manager is live). Both steps are
# idempotent (a re-install neither duplicates nor breaks the link).
# #869 ADDITIVE: the link target lives OUTSIDE mosaic home, so it is
# not a framework-manifest path; ownership of the SSOT unit stays
# systemd/** in the single framework-manifest.txt authority — NO new
# owned path, NO second ownership authority. framework-manifest.txt,
# the install-ordering-guard, and the manifest parity contract are all
# UNCHANGED. Only wake-install.sh (+ test-wake-install.sh) changed.
# 0.6.8 #925 — framework-ship the canon-side FALLBACK WAKE (F7 replacement-
# before-retirement) so hosts get it OUT OF THE BOX rather than hand-
# wiring it per host. ADDITIVE, #869 / Gate-A/B discipline:
# (1) new framework units systemd/user/mosaic-wake-fallback.{timer,
# service}: a LOW-FREQUENCY SAFETY drain (oneshot service running the
# canon drain `digest.sh render --from-store`) fired by a per-class
# cadence timer, INDEPENDENT of the event-driven detector, so a stalled
# detector/daemon can never SILENTLY STARVE delivery. Both units are
# framework-owned via the EXISTING `systemd/**` glob in framework-
# manifest.txt (Gate A) — NO new owned path, NO second ownership
# authority. (2) an OPTIONAL, additive per-class `fallback_cadence`
# bound in wake-watch-list.schema.json (config, not code). It is
# backward-compatible within schema_version 1, so [schema_min,
# schema_max] stays [1,1] and the detector's Gate B range check is
# UNCHANGED (an out-of-range schema_version still fails loud). (3) A10
# install/wire: wi_install enumerates + LINKS + validates the two units
# into the user systemd search path (idempotent, fail-closed, same
# link-to-SSOT pattern as the detector unit); write-fallback-cadence
# writes the per-class cadence as a BLANK-RESET drop-in (exactly one
# effective OnUnitActiveUSec). (4) F7 install-validate: the §5 legacy
# reap now REFUSES unless the canon fallback wake is proven live
# (installed + schedulable floor always; enabled + proven-firing when a
# live user manager is probeable, mirroring #913's opportunistic
# WAKE_VERIFY_USE_SYSTEMCTL pattern) — F7 is encoded in the installer,
# not operator memory. framework-manifest.txt, the install-ordering-
# guard, and the manifest parity contract are all UNCHANGED.
# 0.6.9 #917 store.sh cmd_enqueue — HARDEN the final observed_seq cursor write
# (defense-in-depth, surfaced by the #915 review obs#2; non-blocking).
# The final cursor _atomic_write was the ONE durable write not wrapped
# in a failure check and was cross-file non-atomic with the observed.set
# write just before it. Now (a) the cursor write is GATED like the
# pending/observed.set writes (#908) — a cursor-write failure is
# FAIL-LOUD (non-zero + diagnostic), never silently swallowed into a
# spurious success while the allocation stayed uncommitted; and (b) on
# cursor-write failure observed.set is ROLLED BACK to its pre-write
# snapshot, so observed.set and the cursor can never be left cross-file
# inconsistent (observed.set ahead of a cursor that never committed) —
# they BOTH advance or NEITHER does. #908 is UNCHANGED: single store-side
# allocator, atomic allocate+enqueue under flock, arrow-1 no-burn
# (pending write still FIRST and its failure still aborts before any
# cursor advance), anti-swallow ≤consumed fail-loud, and the W2
# contiguous-prefix CONSUMED contract all intact. The cursor remains the
# sole COMMIT point (an uncommitted pending entry is re-derived/reconciled,
# never consumed), so a pending-ahead state is exactly the one #908 already
# tolerates on its observed.set-failure path. ON-DISK FORMAT UNCHANGED
# (read-compatible; a store written by older code reads identically). Only
# store.sh (+ test-wake-store-ack.sh T11) changed.
# 0.6.10 #932 reconciler RE-ENUMERATION of already-CONSUMED detector-observed
# state (wake-pilot finding #7 — safe-but-noisy G2a alarm-hygiene).
# After consume-truncation a consumed state matched NO accounting
# record (inbox truncated; the reconciler's seen-ledger only covers
# its OWN enumerations; the detector hash-file is correctly
# DISTRUSTED) -> the reconciler treated it as UNACCOUNTED and
# re-enumerated it: one DUPLICATE orientation wake + one SPURIOUS
# rc=1 CRITICAL per detector-active window per cycle (functionally
# safe — no lost obligation — but cry-wolf erosion of real alarms at
# fleet scale). FIX: (1) store.sh records the last-consumed
# observed_hash per (kind,id) at consume-truncation into a NEW
# store-owned durable record consumed-hashes.jsonl (atomic write;
# ADDITIVE — existing on-disk format unchanged/read-compatible; #908
# allocator untouched); (2) reconcile.sh adds a THIRD accounting
# source alongside the inbox and its seen-ledger: a detector-observed
# state whose observed_hash MATCHES the store's recorded last-consumed
# hash is ACCOUNTED (not re-enumerated — no dup wake, no spurious
# CRITICAL). TRUST BOUNDARY: the 3rd check consults ONLY the
# store-written record (its existence implies the state was durably
# enqueued+consumed, so it structurally cannot exhibit the §5
# hash-advance-without-enqueue swallow signature); trusting DETECTOR
# hash-files STAYS REJECTED. G3 is NOT weakened: only states the store
# RECORDED as consumed are suppressed — a genuinely-unaccounted state
# (enqueued-but-unconsumed, still in the inbox, OR a real gap) still
# re-enumerates + alarms. Changed: store.sh, reconcile.sh,
# _wake-common.sh (doc), test-wake-reconcile.sh (R10/R11),
# test-wake-store-ack.sh (T12).
# 0.6.11 #934 seq-integrity fault injection made MOUNT-FREE + privilege-invariant
# so the allocator's most safety-critical failure paths ACTUALLY RUN in
# the real NON-privileged CI runner (which denies mount-in-userns) instead
# of skipping. T9 (#908 arrow-1 no-burn) and T11 (#917 final-cursor gate +
# observed.set rollback) previously forced a write to fail via
# `unshare --mount --user --map-root-user` + a bind-mount EBUSY-on-mountpoint,
# which the non-priv runner DENIES -> both SKIPPED (skipped-trust-layer, the
# class #912 cured for digest). FIX: a single test-only, PROD-INERT fault
# seam in _wake-common.sh _atomic_write honored ONLY when the env var
# WAKE_TEST_FAULT explicitly names a write point (pending->pending.jsonl,
# cursor->observed_seq); it forces the ALREADY-EXISTING fail-loud/rollback
# PATH (#908/#917) to be taken for that one target and RUNS UNPRIVILEGED. No
# production input can set a process env var, so with it unset the seam is a
# no-op: on-disk format + allocator semantics are byte-for-byte unchanged in
# production. The unshare+bind-mount injection AND its skip-when-unavailable
# guard/witness-marker are REMOVED — T9/T11 now RUN and ASSERT their failure
# paths in every environment including non-priv CI. Changed: _wake-common.sh
# (seam), test-wake-store-ack.sh (T9/T11 conversion).
# 0.6.12 #940 snapshot-datable digests — the adapter-contract fd-3 snapshot-
# metadata channel (wake-pilot finding fw-wake-digest-snapshot-lag:
# a digest's locator carried observed_hash + emit_ts but nothing
# DATING the snapshot, so a consumer could not tell a fresh
# snapshot from one already superseded at delivery without a tool
# call). ADDITIVE + backward-compatible: (a) detector.sh invokes
# the W4 source adapter with fd 3 redirected to a temp file; the
# adapter MAY write one JSON object {"snapshot_sha": "<git commit
# sha>", "snapshot_ts": <epoch>} there. OUT-OF-BAND is load-
# bearing: everything on stdout is hashed by the delta gate, so an
# in-band tip-commit sha would advance observed_hash on every
# unrelated push (spurious delta wake per watched file). Metadata
# is ADVISORY and validated (sha ^[0-9a-f]{7,64}$, ts number):
# malformed metadata is dropped with a LOUD stderr diagnostic but
# NEVER fails the poll or suppresses the wake — the obligation
# never depends on optional dating. Valid fields join the enqueue
# locators; an adapter that never writes fd 3 is byte-identical
# legacy behavior. (b) digest.sh _locator_line renders
# snapshot_sha=/snapshot_ts= (scrubbed) beside observed_hash=, and
# snapshot_sha+path upgrades the one-call re-verify hint to
# `git show <snapshot_sha>:<path>` (snapshot_sha IS a commit sha,
# unlike observed_hash, so it may feed the git hint). With emit_ts
# already in the header, snapshot age becomes local arithmetic for
# the consumer — zero round trips. Watch-list schema UNTOUCHED
# ([1,1] unchanged — adapter contract + locator vocabulary, not
# watch-list config). store.sh/reconcile.sh/beacon.sh UNCHANGED.
# Review hardening (#941 §2): snapshot_ts additionally requires a
# VALID snapshot_sha (a bare number with no revision to re-verify
# against is the weakest attestation — dropped loudly), must be a
# sane positive epoch (^[0-9]{1,12}$ — validated BEFORE the shell
# integer comparison so an absurd value cannot error past it), and
# must not sit beyond a future-skew allowance
# (WAKE_SNAPSHOT_TS_FUTURE_SLACK, default 300 s): a future ts
# yields a NEGATIVE age — stale-reads-fresher-than-fresh, the
# exact failure class #940 fixes. The SLACK knob itself is
# operator input interpolated into arithmetic under set -u, so it
# gets the same discipline (#941 §2 round 2): shape-validated as
# a plain non-negative integer of at most 9 digits, else LOUD
# fallback to 300 — a malformed knob
# ('300s', '5m', 'abc') must never kill the poll, and a negative
# one must never invert the guard into deny-all. Shape validation
# is NOT radix validation (#942 review): bash reads leading zeros
# as OCTAL, so '08'/'09' pass the shape check yet are fatal in
# $((...)) and '0300' silently means 192 — the knob is therefore
# forced base-10 (10#) after validation, so it means what the
# operator wrote. The validator and the consumer must also agree
# on STRING EXTENT (#942 follow-up): grep's ^...$ anchors bind
# per LINE, so a multi-line value ($'300\n8') passed the regex
# whole yet was fatal in $((...)) — validation is a whole-string
# case pattern, not grep, so an embedded newline rejects.
# SKEW GUARANTEE
# (stated, not implied): the future-skew check runs against the
# DETECTOR's clock; consumer-side age arithmetic runs on the
# consumer's. A surviving snapshot_ts is therefore attested only
# to within SLACK seconds of the detector's clock, plus whatever
# skew the consumer's own clock adds — a small NEGATIVE age at
# render is bounded, not impossible; treat age <= 0 as
# "effectively current," never as proof of freshness.
# NOTE for consumers: these fields
# are ADVISORY and their ABSENCE IS DELIBERATELY NOT DIAGNOSTIC —
# a pre-#940 adapter and a dropped-as-malformed attestation render
# identically (no snapshot_* fields); the drop is loud only in the
# detector's own stderr. Do not build load-bearing logic on the
# absence of these fields.
# Changed: detector.sh, digest.sh (+ test-wake-detector.sh
# D10/D11/D12/D13, test-wake-digest-quarantine.sh Q10).
# 0.6.13 #942/#943 SLACK-knob validation hardening, split from 0.6.12 because
# version= is the component's SOLE self-identity claim (no per-file
# hashes here) and two detector-changing merges after the 0.6.12
# stamp had left three materially different detectors under one
# version string (#943 review §2). #942: the knob is resolved once,
# shape-validated, LOUD fallback 300, and forced base-10 (10#) so
# zero-padded values mean what the operator wrote instead of octal.
# #943: validation is a whole-string case pattern, not grep, so an
# embedded newline ($'300\n8' — accepted per-line by grep's ^...$
# anchors, fatal in $((...))) rejects. Full rationale in the knob
# paragraph of the 0.6.12 entry above.
# Changed: detector.sh (+ test-wake-detector.sh D13).
# 0.6.14 #944 the §2.1 hard-locator gate was UNSATISFIABLE for detector-built
# actionable board_file entries: _has_hard_locator tested only
# repo+issue / 40-hex sha / file, while detector.sh (A1) builds
# kind/id/observed_hash + path (+ snapshot_sha/_ts when attested,
# #940) — no key in common, so every class=actionable board_file
# delta was structurally guaranteed to dead-letter (live: mos-dt
# seqs 63/68, 2026-07-30; the only tier with a 30m SLO delivered
# nothing on its only actionable source). Fix: `path` becomes a
# hard-locator arm — and ONLY path: it mirrors `file`'s one-call
# "re-read X" precision, upgrading to one-call
# `git show <snapshot_sha>:<path>` when a snapshot is attested.
# observed_hash (content hash, not an address) and bare path-less
# snapshot_sha (would widen the gate past the board_file
# vocabulary — review-adopted criterion) remain NON-arms.
# Quarantine is a RENDER-TIME filter (entries never leave
# pending), so existing UNCONSUMED dead-letters re-deliver
# automatically on the first post-upgrade drain; consumed-past
# dead-letters are not requeued.
# Changed: digest.sh (+ test-wake-digest-quarantine.sh: Q11
# positive control — the live seq-68 entry verbatim must RENDER
# as CLAIM@seq — and Q1/Q6-Q9 fixtures moved off the now-valid
# path-bearing shape onto genuinely address-free shapes,
# amending the #920-era ruling that had pinned the live pilot's
# own locator shape as the malformed example). Doc follow-up:
# #948 amends CONVERGED-DESIGN.md §2.1 to add `path` to the
# hard-locator enumeration and to state the operative test as
# "one targeted call, never a search" (NOT "pins the observed
# state") — sequenced AFTER the reseed so the edit itself is a
# live delivery test of the fixed gate.
# 0.6.15 #946 the digest's embedded ack watermark covered quarantined
# entries: quarantine is a render-time filter (0.6.14), so the
# suggested `ack.sh consumed --upto <observed_seq>` stepped the
# cursor PAST dead-lettered seqs and _record_last_consumed then
# wrote consumed-hash witness rows for deliveries that never
# happened (live: mos-dt seq 68 buried under five digests;
# Finding A: a false 9d0f639f…@63 witness row). Fix — disclose
# AND clamp: (1) the rendered digest gains a QUARANTINED section
# (seq + class + HELD only; ids/locators stay withheld,
# preserving the exclusion property) and the embedded ack is
# clamped to min(observed_seq, min quarantined seq 1);
# (2) store.sh consume REFUSES to cross an unconsumed
# quarantined seq — `--force-past-quarantine` (plumbed through
# ack.sh consumed) is the ONLY way past, loud per-seq on stderr,
# and even the forced path never writes a consumed-hash witness
# for a quarantined seq; (3) render --from-store syncs the
# store-owned quarantined.set via new `store.sh quarantine-sync`
# (full-replace, so a gate fix self-heals stale quarantine;
# --from-file/--stdin never touch the set); (4) new
# `store.sh quarantine-audit [--repair]` sweeps consumed-hashes
# for rows provably contradicted by the dead-letter ledger
# (report exits 1; --repair removes only provably-false rows;
# the ledger itself is history and is never modified; rows whose
# dead-letter evidence was pruned are unprovable and untouched).
# Changed: store.sh, digest.sh, ack.sh
# (+ test-wake-store-ack.sh T13-T16,
# test-wake-digest-quarantine.sh Q12-Q16).
# 0.7.0 #958 A11 preimage.sh — durable provenance for the OPERATOR-SIDE
# preimage definition (wake-pilot finding: source-adapter.sh was
# unversioned, so the operator-owned bytes every observed_hash is
# computed FROM had thinner provenance than any hash they feed;
# attribution required an agent transcript). NEW tool + two
# pre-step integrations, ADDITIVE: (1) preimage.sh derives the
# preimage set from the RUNTIME env (the resolved
# WAKE_DETECTOR_SOURCE_CMD file, WAKE_WATCH_LIST, optional
# WAKE_PREIMAGE_EXTRA paths) and, per file, records
# {ts,path,sha256,size,mtime,prev} to an append-only ledger +
# captures the bytes CONTENT-ADDRESSED under
# $STATE_DIR/preimage/objects/<sha256> — prior bytes + change
# time are answerable from durable state alone, no transcript
# (acceptance a). A git-repo-in-operator-dir design was REJECTED:
# its who/why claim is false under shared-author fleets, and
# .gitignore is pattern-based where acceptance (b) demands
# fail-closed. (2) CREDENTIAL HARD GATE (acceptance b), FOUR
# LAYERS (#964 re-verdict: B2 cases C+D): (i) POLARITY — extras
# (WAKE_PREIMAGE_EXTRA) are RECORD-ONLY by default (ledger row +
# cause line, NO bytes); byte capture for an extra requires the
# path listed in WAKE_PREIMAGE_CAPTURE (colon-separated opt-in);
# only the core set (adapter, watch-list) is capture-eligible by
# default — a shape list can only refuse the secrets someone
# already enumerated, so safety may not rest on one (#964-D).
# (ii) PATH DENY evaluated on BOTH the raw and realpath-resolved
# candidate forms against BOTH unresolved and resolved
# mosaic-home anchors (credentials.json,
# tools/_lib/credentials.json, credentials/**, any basename
# credentials.json) — a deny list written in unresolved paths
# cannot match a path resolved before it arrived (#964-C), and a
# symlinked opt-in entry cannot smuggle a denied target (deny
# runs FIRST, independent of what the opt-in matched). (iii)
# CONTENT DENY on the opt-in path only, defense-in-depth NOT the
# safety mechanism: digest.sh's six scrub shapes + a
# named-assignment probe (key/token/secret/passw/hmac/credential/
# bearer = unbroken value >=16 chars); probe ERROR fails TOWARD
# refusal (an error exit is not a negative result); false
# positives are acceptable — a false match only withholds byte
# capture, never tracking. (iv) size cap WAKE_PREIMAGE_MAX_BYTES
# (default 1 MiB). Deny ALWAYS wins over the opt-in. A refused
# file still gets its hash/size/mtime row (captured:false +
# refused reason) so change TIME survives even when content must
# not. (3) FIRST-CLASS CAUSE LINE
# (acceptance c): on a change (or deletion — ABSENT is a state,
# not an error), one class=actionable entry is enqueued via the
# store allocator with locators {kind:preimage, path (§2.1 hard
# locator — never quarantined), observed_hash, prev_hash,
# preimage:true, reason:preimage-definition-changed}. Both
# detector.sh cmd_poll_once and reconcile.sh cmd_reconcile run
# the check as a PRE-step, so the cause line lands at a LOWER
# observed_seq than the N per-source deltas/enumerations it
# explains — "preimage definition changed" reads first, not N
# UNACCOUNTED lines. (4) FAIL-LOUD discipline (D2/#955 class):
# an unresolvable adapter, unreadable watch-list, corrupt ledger
# (REFUSES to compare or re-baseline over corrupt history), failed
# object/ledger write, or failed enqueue is a loud non-zero —
# never read as "no change"; in both integrations the pass exits
# non-zero but source observation still proceeds (no starvation).
# An ABSENT/EMPTY ledger with objects/ NON-empty is a loud
# non-zero REFUSAL to re-baseline (#964-B11: objects with no
# ledger cannot be a first install — history was deleted; the
# first-install path must not silently absorb it). First-seen on
# a genuinely clean state dir is a SILENT baseline (detector
# first-poll idiom).
# The installer is UNCHANGED — Gate A auto-enumerates the new
# file from the filesystem; the recording site is the runtime
# tick, which is where the env-derived preimage set exists.
# store.sh/digest.sh/beacon.sh UNCHANGED; watch-list schema
# UNTOUCHED ([1,1]). Changed: preimage.sh (new), detector.sh,
# reconcile.sh (+ test-wake-preimage.sh P1-P17; P13-P17 are the
# #964 re-verdict regression needles: symlinked store, live
# secret value, polarity, ledger deletion, allowlist-symlink).
# 0.7.1 #952 quarantine-audit clean-sweep message named only ONE of the
# two unprovable residual classes ("rows without surviving
# dead-letter evidence"), so an operator reading the OK concluded
# NO evidence exists when evidence can exist and be UNUSABLE: a
# surviving dead-letter row whose locator extracts an empty
# observed_hash (live specimen: mos-dt seq 13,
# bench/malformed-locator-test, "deliberately non-conformant")
# can never satisfy the four-field conviction match, because
# _record_last_consumed only writes rows with a NON-empty hash.
# WORDING-ONLY fix (measurement defect, #951 review finding 1):
# THREE surfaces — the clean-sweep message, the PROVABILITY
# BOUND comment, and (second commit, author-side self-catch)
# the usage() help text — now name both classes; the
# conviction predicate is UNCHANGED.
# Test T17 drives the real writer flow and plants the verbatim
# live specimen (nested .locators.*, NO observed_hash key) —
# never a hand-built flat dead-letter row, which would make the
# audit's correct non-conviction look exactly like the defect
# under hunt (the #951 review's false-defect near-miss).
component=wake
version=0.7.1
# Watch-list schema this component consumes, and the INCLUSIVE range of
# schema_version values it supports. A wake-watch-list.json whose schema_version
# falls outside [schema_min, schema_max] is rejected by the component (fail-loud),
# never silently coerced.
#
# #925: the OPTIONAL per-class `fallback_cadence` bound is ADDITIVE and backward-
# compatible — an existing schema_version-1 watch-list stays valid (the field is
# omittable), so the supported range is UNCHANGED at [1, 1] and Gate B is intact.
schema=wake-watch-list
schema_min=1
schema_max=1
# Pieces shipped by this component version (informational):
# store.sh A2 — three-cursor durable store + drain lib. Stale-tmp reaping is
# OFF the hot enqueue path; `init` performs the age-scoped
# maintenance reap (#927). enqueue's final observed_seq cursor
# write is GATED fail-loud + rolls observed.set back on failure so
# the two never diverge (#917). (W2, #927, #917)
# ack.sh A4 — RECEIVED/CONSUMED ack-wrapper (local-write + ship). (W2)
# digest.sh A3 — cumulative-state digest renderer (hard locators,
# two-tier trust, injection/secret scrub). PER-ENTRY
# quarantine: a render-refused entry is dead-lettered +
# alarmed (stderr AND off-host via WAKE_ALARM_SINK_CMD,
# deduped by observed_seq, #924) + excluded, the rest still
# renders (no head-of-line block); reconciler enumerations
# (reconciled==true) render ORIENTATION-tier, gate-exempt.
# (W3, #920, #924)
# sign.sh A5 — non-circular HMAC signer (independent wake_id,
# load_credentials by-name; fills the hmac placeholder). (W3)
# detector.sh A1 — per-host single-instance delta-gated detector daemon
# (flock, anchor-scoped hashing, fail-loud source semantics;
# enqueues deltas to store.sh and captures the store-allocated
# observed_seq — no private counter, #908). Its poll tick also
# runs the age-scoped maintenance stale-tmp reap (#927). (W4)
# fn-oracle.sh A6 — synthetic-canary FN-oracle: injects a KNOWN delta at the
# source boundary, drives the pipeline through the detector's
# public poll-once, asserts CONSUMED within the per-class SLO
# (off-domain verdict from the terminal store cursor). §4
# requires FN-rate=0; a dropping/disabled detector FAILS. (W5)
# reconcile.sh A7 — source-parity reconciler: (i) source-coverage parity
# inventory (an omitted source cannot pass the vector
# vacuously) + (ii) periodic full reconcile to 0-unaccounted,
# enumerating pre-existing/startup state into the store via the
# SINGLE store-side allocator (co-feed is safe; the former
# dual-allocator fail-closed guard retired, #908). (W5)
# beacon.sh A8 — off-host DEAD-MAN liveness beacon: a monotonic beacon
# EMITTER (emit — the primitive the detector run-loop calls
# each cycle), the off-host monitor's RECEIVER + beacon-ABSENCE
# alarm (record, check), and a pluggable alarm-sink/beacon-sink
# ADAPTER INTERFACE. Liveness is SPLIT from work-triggering;
# the alarm fires on ABSENCE, routing to a human/other-host
# within its SLO (§4/G1). FAIL-CLOSED: an unconfigured OR
# unreachable target FAILS LOUD (no silent no-alarm host).
# A same-host sibling is REJECTED as non-independent; an
# isolated host degrades to a FLAGGED different-supervision-root
# beacon; capture-pane is a liveness HINT only. (W6)
# wake-install.sh A10 — idempotent, fail-closed COMPONENT installer. Selects the
# component file set and INTERSECTS-AND-VALIDATES it against the
# single SSOT framework-manifest.txt (Gate A) — this VERSION
# manifest authorizes no path. Ships the blank-reset retire
# idiom (exactly-one OnUnitActiveUSec) for the legacy heartbeat
# timer, the snapshot-guard (no reap without a snapshot), and
# fail-closed alarm-target + HMAC-key install-validation (the
# installer wires + install-validates the beacon target that
# beacon.sh's fail-loud primitive is designed for). Fails loud
# if the required _lib/manifest.sh helper is absent (older host
# seed) instead of a bare source error, and LINKS the unit into
# the user systemd search path + post-install-validates it
# resolves (#913). (W7, #913)
# preimage.sh A11 — operator-side PREIMAGE-DEFINITION provenance: derives the
# preimage set from the runtime env (resolved adapter file,
# watch-list, WAKE_PREIMAGE_EXTRA), appends
# {ts,path,sha256,size,mtime,prev} rows to an append-only
# ledger, captures bytes content-addressed under
# preimage/objects/<sha256>, and enqueues a FIRST-CLASS
# "preimage-definition-changed" actionable (path = §2.1 hard
# locator) BEFORE the deltas it explains (detector +
# reconcile pre-step). Credential HARD GATE: capture is
# REFUSED (hash/mtime still recorded) for credential-store
# paths, secret-shaped content, and oversized files —
# refusal, never redaction. Fail-loud on any infra failure;
# a corrupt ledger refuses re-baseline. (#958)
# Companion (framework subtree, not under tools/wake/): systemd/user/mosaic-wake.service
# — the long-lived detector daemon unit (per-class SLO lives in
# the daemon, NOT a systemd interval). (W7)
# Companion (framework subtree, not under tools/wake/):
# systemd/user/mosaic-wake-fallback.timer + mosaic-wake-fallback.service
# — the canon FALLBACK WAKE (F7): a per-class cadence timer firing
# a oneshot SAFETY drain (digest.sh render --from-store),
# INDEPENDENT of the detector, so a stalled detector cannot starve
# delivery. Owned via the existing systemd/** glob; the per-class
# cadence is a blank-reset drop-in; the §5 reap is F7-gated on this
# being proven live. (W7, #925)