Compare commits
1
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ac3b972273 |
@@ -39,7 +39,6 @@ overwritten on upgrade. (Layer model: `constitution/LAYER-MODEL.md`.)
|
|||||||
| TypeScript strict typing | `guides/TYPESCRIPT.md` |
|
| TypeScript strict typing | `guides/TYPESCRIPT.md` |
|
||||||
| QA / test strategy | `guides/QA-TESTING.md` |
|
| QA / test strategy | `guides/QA-TESTING.md` |
|
||||||
| Documentation (any code/API/auth/infra change) | `guides/DOCUMENTATION.md` |
|
| Documentation (any code/API/auth/infra change) | `guides/DOCUMENTATION.md` |
|
||||||
| Writing style (docs, comms, any prose) | `guides/WRITING-STYLE.md` |
|
|
||||||
| Secrets / vault usage | `guides/VAULT-SECRETS.md` |
|
| Secrets / vault usage | `guides/VAULT-SECRETS.md` |
|
||||||
| Tool/credential reference (service CLIs, wrappers) | `guides/TOOLS-REFERENCE.md` |
|
| Tool/credential reference (service CLIs, wrappers) | `guides/TOOLS-REFERENCE.md` |
|
||||||
| Memory protocol (OpenBrain capture/recall) | `guides/MEMORY.md` |
|
| Memory protocol (OpenBrain capture/recall) | `guides/MEMORY.md` |
|
||||||
|
|||||||
@@ -27,14 +27,6 @@ Master/slave model:
|
|||||||
- Do not perform destructive git/file actions without explicit instruction.
|
- Do not perform destructive git/file actions without explicit instruction.
|
||||||
- Browser automation (Playwright, Cypress, Puppeteer) MUST run in headless mode. Never launch a visible browser — it collides with the user's display and active session.
|
- Browser automation (Playwright, Cypress, Puppeteer) MUST run in headless mode. Never launch a visible browser — it collides with the user's display and active session.
|
||||||
|
|
||||||
### Output standards (writing + code)
|
|
||||||
|
|
||||||
- Technical documentation follows **MOS-STE** (Mosaic Simplified Technical English — an adapted ASD-STE100 profile): short sentences, one instruction per sentence, active voice, one word per meaning, one term per concept. Full rules: `~/.config/mosaic/guides/WRITING-STYLE.md`.
|
|
||||||
- Apply MOS-STE **hardest to verification artifacts** (acceptance criteria, witness predicates, gate/alarm conditions). There an ambiguous term produces a false green, not just a confused reader.
|
|
||||||
- Source code follows the **Google Style Guide** for the language.
|
|
||||||
- User-facing comms follow the user's declared `communicationStyle` in `USER.md` "Communication Preferences" (`direct` | `friendly` | `formal`, default `direct`); `guides/WRITING-STYLE.md` §5 maps each value to output. The documentation standard does not change with user preference.
|
|
||||||
- **Carve-out:** MOS-STE does NOT apply to content that must carry a specific human voice (letters, personal or marketing prose, voice-matched output). A declared voice profile wins.
|
|
||||||
|
|
||||||
### Secrets handling (HARD RULE)
|
### Secrets handling (HARD RULE)
|
||||||
|
|
||||||
- Vault is the canonical source-of-truth for every secret in every environment. No exceptions.
|
- Vault is the canonical source-of-truth for every secret in every environment. No exceptions.
|
||||||
|
|||||||
@@ -1,134 +0,0 @@
|
|||||||
# Writing Style Standard — MOS-STE (MANDATORY)
|
|
||||||
|
|
||||||
This guide defines how agents write. It sets one style standard per output type.
|
|
||||||
It is written in the standard it defines, as a worked example.
|
|
||||||
|
|
||||||
**Adapted, not compliant.** MOS-STE (Mosaic Simplified Technical English) is an
|
|
||||||
adapted profile of ASD-STE100. Mosaic does not license or certify against
|
|
||||||
ASD-STE100. Mosaic uses the load-bearing rules and fits them to agent work. This
|
|
||||||
is the same stance Mosaic takes toward DO-178B/C: use the rigor, do not claim the
|
|
||||||
certification.
|
|
||||||
|
|
||||||
## Scope — which standard governs which output
|
|
||||||
|
|
||||||
| Output type | Standard |
|
|
||||||
|---|---|
|
|
||||||
| Technical documentation (READMEs, runbooks, PRDs, procedures, ADRs, guides, acceptance criteria, design docs) | **MOS-STE** (this guide) |
|
|
||||||
| Source code and code comments | **Google Style Guide** for the language (§4) |
|
|
||||||
| Inter-agent comms | MOS-STE by default (concise, structured) |
|
|
||||||
| User-facing comms | **Per-user style choice** — read `USER.md` "Communication Preferences" (§5) |
|
|
||||||
| End-user prose the user owns (marketing, letters, personal writing, voice-matched content) | The user's declared voice. MOS-STE does NOT apply. |
|
|
||||||
|
|
||||||
**The user-voice carve-out is absolute.** Do not apply MOS-STE to content that
|
|
||||||
must carry a specific human voice (for example a cover letter, a personal
|
|
||||||
message, or marketing copy). That content needs the user's voice. MOS-STE would
|
|
||||||
damage it. When a project declares a voice profile, that profile wins.
|
|
||||||
|
|
||||||
## 1. Why one standard
|
|
||||||
|
|
||||||
Agent documentation drifts across projects. Different agents use different terms,
|
|
||||||
sentence styles, and structures for the same concept. Readers lose time.
|
|
||||||
Assumptions hide in ambiguous prose. One standard gives agents a clear target. It
|
|
||||||
gives reviewers a clear test.
|
|
||||||
|
|
||||||
## 2. Where MOS-STE matters most — verification artifacts
|
|
||||||
|
|
||||||
Apply MOS-STE hardest to acceptance criteria, witness predicates, gate
|
|
||||||
definitions, and alarm conditions. In prose, an ambiguous term produces a
|
|
||||||
confused reader. In a verification artifact, an ambiguous term produces a false
|
|
||||||
green — a check that passes without testing the claim.
|
|
||||||
|
|
||||||
The one-term-one-concept rule (rule 9) is the guard. When one word names two
|
|
||||||
concepts in one predicate, the check can test the wrong concept and still pass.
|
|
||||||
|
|
||||||
**Worked failure.** A rename used a witness predicate with three clauses: ref A
|
|
||||||
present, ref B absent, tip committed from this host. Every clause tested the git
|
|
||||||
*ref* (the channel). The claim under test was about a *field inside the payload*.
|
|
||||||
The word "beacon" named two concepts in one sentence. Deleting ref B was the next
|
|
||||||
scheduled step. That step flips the last clause green and certifies a state in
|
|
||||||
which the payload still names the wrong host. The predicate was one planned action
|
|
||||||
away from a false green on its normal path. The payload field was never tested.
|
|
||||||
|
|
||||||
Rule: when N failure modes share one observable, the observable is not a
|
|
||||||
diagnostic. In a verification artifact, that ambiguity does not confuse a reader —
|
|
||||||
it certifies the defect.
|
|
||||||
|
|
||||||
## 3. MOS-STE rules
|
|
||||||
|
|
||||||
### 3.1 Sentence rules
|
|
||||||
|
|
||||||
1. Keep sentences short. Use 20 words or fewer for a procedure. Use 25 words or
|
|
||||||
fewer for a description. (Reasoning and doctrine prose relaxes this limit —
|
|
||||||
see §3.4. A future lint enforces §3.1, not §3.4.)
|
|
||||||
2. Write one instruction per sentence. In a procedure, give one command per step.
|
|
||||||
3. Use the active voice. Write "Run the script." Do not write "The script should
|
|
||||||
be run."
|
|
||||||
4. Use the imperative for instructions. Start the sentence with the verb.
|
|
||||||
5. Use simple verb tenses. Prefer the present tense. Avoid the perfect and
|
|
||||||
progressive tenses when a simple tense works.
|
|
||||||
6. Do not use an `-ing` form when it makes the meaning unclear.
|
|
||||||
7. Write positive statements. State what to do, not only what to avoid.
|
|
||||||
|
|
||||||
### 3.2 Word rules
|
|
||||||
|
|
||||||
8. Use one word for one meaning. Do not use the same word in two senses.
|
|
||||||
9. Use one term for one concept. Do not use synonyms for variety. Example: choose
|
|
||||||
`secret`, `credential`, or `key` for each concept, and keep it.
|
|
||||||
10. Use articles (`a`, `the`). Do not drop words to save space.
|
|
||||||
11. Keep an approved-terms glossary per project. Add each domain noun and each
|
|
||||||
chosen verb. Technical names (for example `Vault`, `cgroup`, `systemd`) are
|
|
||||||
always allowed.
|
|
||||||
12. Define an abbreviation at its first use. Then use it consistently.
|
|
||||||
|
|
||||||
### 3.3 Structure rules
|
|
||||||
|
|
||||||
13. Use a list for parallel items or sequential steps. Do not put them in one long
|
|
||||||
sentence.
|
|
||||||
14. Use a table for data with more than two dimensions.
|
|
||||||
15. Use parallel structure in headings and steps.
|
|
||||||
16. Repeat the noun. Do not use a pronoun when the reference is unclear.
|
|
||||||
|
|
||||||
### 3.4 Adaptation notes (where MOS-STE deviates from ASD-STE100, and why)
|
|
||||||
|
|
||||||
- **No licensed dictionary.** ASD-STE100 ships a controlled dictionary under
|
|
||||||
copyright. MOS-STE uses per-project glossaries instead (rule 11).
|
|
||||||
- **Domain terms are allowed.** MOS-STE keeps every term the work needs.
|
|
||||||
- **Reasoning prose gets structure, not amputation.** Apply the sentence and word
|
|
||||||
rules to design and doctrine writing. Allow the length a subtle argument needs.
|
|
||||||
Readable-first beats rule-strict when the two conflict.
|
|
||||||
|
|
||||||
## 4. Code — Google Style Guide
|
|
||||||
|
|
||||||
Write source code to the Google Style Guide for the language (Python, TypeScript,
|
|
||||||
Shell, Go, and so on). Match the existing file when a local convention already
|
|
||||||
exists. Keep code comments to the MOS-STE sentence and word rules.
|
|
||||||
|
|
||||||
## 5. User-facing comms — a per-user choice
|
|
||||||
|
|
||||||
Mosaic is multi-user. Different users want different comms styles. The framework
|
|
||||||
already carries the selectable setting: `communicationStyle` (`direct` |
|
|
||||||
`friendly` | `formal`, default `direct`). `mosaic init` writes it, and the
|
|
||||||
builder renders it into the generated `USER.md` "Communication Preferences"
|
|
||||||
section. This guide adds the OUTPUT meaning of each value; do not invent new
|
|
||||||
values.
|
|
||||||
|
|
||||||
The builder renders the style as prose bullets, not the token name, so match on
|
|
||||||
the leading bullet the generated `USER.md` actually contains:
|
|
||||||
|
|
||||||
| `USER.md` leading bullet | Style | User-facing output |
|
|
||||||
|---|---|---|
|
|
||||||
| "Direct and concise" | `direct` (default) | MOS-STE structure — short, active, defined terms, tables for overview. |
|
|
||||||
| "Warm and conversational" | `friendly` | Warmer register. Full sentences, explain reasoning, fewer tables. |
|
|
||||||
| "Professional and structured" | `formal` | Professional and structured. Thorough, with explicit recommendations. |
|
|
||||||
|
|
||||||
This setting governs **user-facing comms only**. It does not change the
|
|
||||||
documentation standard (§3), which is always MOS-STE regardless of the value.
|
|
||||||
|
|
||||||
## 6. Enforcement
|
|
||||||
|
|
||||||
- **Now:** human review only. **No mechanical prose check exists today.** The
|
|
||||||
pre-push gate runs typecheck, lint, build, and tests; it inspects no prose.
|
|
||||||
Reviewers check output against the scope table and the MOS-STE rules by hand.
|
|
||||||
- **Future:** an MOS-STE lint check (built from the §3.1 sentence rules) and a
|
|
||||||
Google-style linter in the pre-push gate. A future linter enforces §3.1, not
|
|
||||||
§3.4 — see the note at rule 1.
|
|
||||||
@@ -54,16 +54,6 @@ Commands:
|
|||||||
Local-write only; a background sync
|
Local-write only; a background sync
|
||||||
ships it (never blocks on network).
|
ships it (never blocks on network).
|
||||||
--no-sync suppresses the background ship.
|
--no-sync suppresses the background ship.
|
||||||
--force-past-quarantine passes the #946
|
|
||||||
force flag through to store.sh consume:
|
|
||||||
the ONLY way to advance past a
|
|
||||||
QUARANTINED (dead-lettered, never
|
|
||||||
delivered) seq. The store's per-seq
|
|
||||||
step-over diagnostics are re-emitted on
|
|
||||||
stderr; no consumed-hash witness is
|
|
||||||
recorded for the quarantined entry.
|
|
||||||
Without the flag, a consume that would
|
|
||||||
cross a quarantined seq is REFUSED.
|
|
||||||
embed --upto N [--wake-id ID] [--agent A]
|
embed --upto N [--wake-id ID] [--agent A]
|
||||||
Print the copy-run ack line to EMBED in
|
Print the copy-run ack line to EMBED in
|
||||||
a digest (does not perform the ack).
|
a digest (does not perform the ack).
|
||||||
@@ -179,7 +169,7 @@ cmd_received() {
|
|||||||
|
|
||||||
cmd_consumed() {
|
cmd_consumed() {
|
||||||
_need_jq
|
_need_jq
|
||||||
local upto='' wake_id='' do_sync="1" force="0"
|
local upto='' wake_id='' do_sync="1"
|
||||||
while [ $# -gt 0 ]; do
|
while [ $# -gt 0 ]; do
|
||||||
case "$1" in
|
case "$1" in
|
||||||
--upto)
|
--upto)
|
||||||
@@ -194,10 +184,6 @@ cmd_consumed() {
|
|||||||
do_sync="0"
|
do_sync="0"
|
||||||
shift
|
shift
|
||||||
;;
|
;;
|
||||||
--force-past-quarantine)
|
|
||||||
force="1"
|
|
||||||
shift
|
|
||||||
;;
|
|
||||||
*)
|
*)
|
||||||
echo "ack.sh consumed: unknown option '$1'" >&2
|
echo "ack.sh consumed: unknown option '$1'" >&2
|
||||||
exit 2
|
exit 2
|
||||||
@@ -214,25 +200,11 @@ cmd_consumed() {
|
|||||||
# Advance consumed_seq via the store. The store enforces the CONTIGUOUS
|
# Advance consumed_seq via the store. The store enforces the CONTIGUOUS
|
||||||
# gapless-prefix rule and rejects a gap (cannot ack N while N-1 unconsumed).
|
# gapless-prefix rule and rejects a gap (cannot ack N while N-1 unconsumed).
|
||||||
# This is a LOCAL-WRITE cursor advance — no network.
|
# This is a LOCAL-WRITE cursor advance — no network.
|
||||||
local new_cursor store_args
|
local new_cursor
|
||||||
store_args=(consume --upto "$upto")
|
if ! new_cursor="$("$STORE_SH" consume --upto "$upto" 2>&1)"; then
|
||||||
if [ "$force" = "1" ]; then
|
|
||||||
store_args+=(--force-past-quarantine)
|
|
||||||
fi
|
|
||||||
if ! new_cursor="$("$STORE_SH" "${store_args[@]}" 2>&1)"; then
|
|
||||||
echo "ack.sh consumed: refused — $new_cursor" >&2
|
echo "ack.sh consumed: refused — $new_cursor" >&2
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
if [ "$force" = "1" ]; then
|
|
||||||
# #946: on the forced path the store's LOUD per-seq step-over diagnostics
|
|
||||||
# were captured together with the cursor line (2>&1 above). Re-emit them on
|
|
||||||
# OUR stderr — the loudness must survive the wrapper — and keep only the
|
|
||||||
# final line (the cursor) for the CONSUMED report below.
|
|
||||||
local cursor_line
|
|
||||||
cursor_line="$(printf '%s\n' "$new_cursor" | tail -n1)"
|
|
||||||
printf '%s\n' "$new_cursor" | sed '$d' | grep -v '^[[:space:]]*$' >&2 || true
|
|
||||||
new_cursor="$cursor_line"
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Record the CONSUMED ack in the local ledger (still no network).
|
# Record the CONSUMED ack in the local ledger (still no network).
|
||||||
local record
|
local record
|
||||||
|
|||||||
@@ -25,8 +25,8 @@
|
|||||||
# sufficiency NEVER exempts a consequential action from its live gate.
|
# sufficiency NEVER exempts a consequential action from its live gate.
|
||||||
#
|
#
|
||||||
# HARD LOCATORS (§2.1): every actionable claim MUST carry a precise locator
|
# HARD LOCATORS (§2.1): every actionable claim MUST carry a precise locator
|
||||||
# (repo + issue#, a 40-char SHA, file:anchor, or path — #944) so re-verification
|
# (repo + issue#, a 40-char SHA, or file:anchor) so re-verification is ONE
|
||||||
# is ONE targeted call. A missing locator = malformed ACTIONABLE entry = FAIL-LOUD.
|
# targeted call. A missing locator = malformed ACTIONABLE entry = FAIL-LOUD.
|
||||||
#
|
#
|
||||||
# #920 (per-entry quarantine — fail-loud WITHOUT head-of-line blocking): a
|
# #920 (per-entry quarantine — fail-loud WITHOUT head-of-line blocking): a
|
||||||
# render-refused entry (actionable-tier, no hard locator) is QUARANTINED — durably
|
# render-refused entry (actionable-tier, no hard locator) is QUARANTINED — durably
|
||||||
@@ -112,14 +112,6 @@ Exit codes:
|
|||||||
diagnostic (#924/G2a) — it never wedges the whole render either.
|
diagnostic (#924/G2a) — it never wedges the whole render either.
|
||||||
Reconciler enumerations (locators.reconciled==true) render ORIENTATION-tier,
|
Reconciler enumerations (locators.reconciled==true) render ORIENTATION-tier,
|
||||||
gate-exempt.
|
gate-exempt.
|
||||||
#946: quarantined entries are DISCLOSED in a QUARANTINED section (by
|
|
||||||
seq/class only — content stays excluded) and the embedded ack copy-run
|
|
||||||
line is CLAMPED below the lowest quarantined seq (the digest never
|
|
||||||
instructs the consumer to record a delivery that never happened; the
|
|
||||||
clamp is announced as an ACK CLAMPED note). A store-mode render also
|
|
||||||
REPLACES the store's quarantined.set (store.sh quarantine-sync) so the
|
|
||||||
consume path enforces the same clamp; --from-file/--stdin renders never
|
|
||||||
touch the set.
|
|
||||||
2 usage error.
|
2 usage error.
|
||||||
3 jq is required but missing.
|
3 jq is required but missing.
|
||||||
|
|
||||||
@@ -552,7 +544,7 @@ cmd_render() {
|
|||||||
# #920 head-of-line-blocking defect that exit-4'd the entire cumulative-state
|
# #920 head-of-line-blocking defect that exit-4'd the entire cumulative-state
|
||||||
# drain). Reconciler enumerations (locators.reconciled==true) are ORIENTATION-
|
# drain). Reconciler enumerations (locators.reconciled==true) are ORIENTATION-
|
||||||
# tier and gate-exempt, so they pass straight through to the deliverable set.
|
# tier and gate-exempt, so they pass straight through to the deliverable set.
|
||||||
local line loc seq pending_ok='' quarantined=0 q_seqs='' q_disclose=''
|
local line loc seq pending_ok='' quarantined=0
|
||||||
while IFS= read -r line; do
|
while IFS= read -r line; do
|
||||||
[ -n "$line" ] || continue
|
[ -n "$line" ] || continue
|
||||||
printf '%s' "$line" | jq -e . >/dev/null 2>&1 || continue
|
printf '%s' "$line" | jq -e . >/dev/null 2>&1 || continue
|
||||||
@@ -562,17 +554,6 @@ cmd_render() {
|
|||||||
seq="$(jq -r '.observed_seq // "?"' <<<"$line")"
|
seq="$(jq -r '.observed_seq // "?"' <<<"$line")"
|
||||||
_quarantine_entry "$line" "$seq"
|
_quarantine_entry "$line" "$seq"
|
||||||
quarantined=$((quarantined + 1))
|
quarantined=$((quarantined + 1))
|
||||||
# #946: collect the quarantined identity for DISCLOSURE + the ack
|
|
||||||
# CLAMP. Disclosure is by durable identity (observed_seq) + class ONLY:
|
|
||||||
# this entry failed the locator gate, so its content is exactly what
|
|
||||||
# this digest refuses to re-inject (the exclusion property Q1/Q4
|
|
||||||
# assert) — the consumer re-verifies via the dead-letter ledger, never
|
|
||||||
# via this line.
|
|
||||||
case "$seq" in
|
|
||||||
'' | *[!0-9]*) : ;; # an unnumbered entry cannot clamp the numeric cursor
|
|
||||||
*) q_seqs="$q_seqs$seq"$'\n' ;;
|
|
||||||
esac
|
|
||||||
q_disclose="$q_disclose * seq $seq [$(_scrub_inline "$(jq -r '.class // "actionable"' <<<"$line")")] HELD — dead-lettered (no §2.1 hard locator); content withheld, NOT delivered."$'\n'
|
|
||||||
continue
|
continue
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
@@ -582,33 +563,6 @@ cmd_render() {
|
|||||||
pending="$(printf '%s' "$pending_ok" | grep -v '^[[:space:]]*$' || true)"
|
pending="$(printf '%s' "$pending_ok" | grep -v '^[[:space:]]*$' || true)"
|
||||||
depth="$(printf '%s\n' "$pending" | grep -c . || true)"
|
depth="$(printf '%s\n' "$pending" | grep -c . || true)"
|
||||||
|
|
||||||
# --- #946: quarantine truth-sync + ack clamp ------------------------------
|
|
||||||
# (1) SYNC: an AUTHORITATIVE full-set render (src=store) REPLACES the store's
|
|
||||||
# quarantined.set with THIS render's quarantined seqs (possibly none — an
|
|
||||||
# empty replace IS the #944 recovery: once the gate is fixed and everything
|
|
||||||
# renders, the stale set clears and the store-side clamp self-heals). A
|
|
||||||
# foreign-data render (--from-file/--stdin) must NEVER rewrite lane truth.
|
|
||||||
if [ "$src" = "store" ]; then
|
|
||||||
if ! printf '%s' "$q_seqs" | "$STORE_SH" quarantine-sync; then
|
|
||||||
echo "digest.sh: WARN (#946) — store.sh quarantine-sync FAILED; the store-side consume clamp may be stale for this lane (the clamped ack line rendered below is still correct)." >&2
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
# (2) CLAMP: the embedded ack may advance AT MOST to just below the LOWEST
|
|
||||||
# quarantined seq — consume requires a contiguous prefix, so one held seq
|
|
||||||
# caps everything above it. With nothing quarantined this is the observed
|
|
||||||
# cursor unchanged. Render-local on purpose: it protects the copy-run line in
|
|
||||||
# EVERY mode, including hermetic --from-file renders.
|
|
||||||
local ack_upto="$observed" min_q='' qs q_list=''
|
|
||||||
while IFS= read -r qs; do
|
|
||||||
[ -n "$qs" ] || continue
|
|
||||||
if [ -z "$min_q" ] || [ "$qs" -lt "$min_q" ]; then min_q="$qs"; fi
|
|
||||||
done <<<"$q_seqs"
|
|
||||||
if [ -n "$min_q" ] && [ "$((min_q - 1))" -lt "$ack_upto" ]; then
|
|
||||||
ack_upto=$((min_q - 1))
|
|
||||||
fi
|
|
||||||
[ "$ack_upto" -ge 0 ] || ack_upto=0
|
|
||||||
q_list="$(printf '%s' "$q_seqs" | tr '\n' ' ' | sed -e 's/[[:space:]]*$//')"
|
|
||||||
|
|
||||||
# --- render (all validated) ----------------------------------------------
|
# --- render (all validated) ----------------------------------------------
|
||||||
local n_actionable=0
|
local n_actionable=0
|
||||||
{
|
{
|
||||||
@@ -702,16 +656,6 @@ cmd_render() {
|
|||||||
printf '%s\n' "$hbody"
|
printf '%s\n' "$hbody"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# #946: QUARANTINED disclosure — a held entry must be VISIBLE in the digest
|
|
||||||
# it was held from (five successive live digests each silently stepped the
|
|
||||||
# consumer past buried seq 68). Disclosure is by seq/class ONLY; the
|
|
||||||
# entry's content already failed the locator gate and stays EXCLUDED.
|
|
||||||
if [ -n "$q_disclose" ]; then
|
|
||||||
printf '\n-- QUARANTINED (dead-lettered; HELD — NOT delivered; the ack below does NOT cover these) --\n'
|
|
||||||
printf '%s' "$q_disclose"
|
|
||||||
printf ' disposition: see %s/dead-letter.jsonl — fix the source locator (re-delivery is automatic once the entry passes the gate), or step past EXPLICITLY with ack.sh consumed --force-past-quarantine.\n' "$STATE_DIR"
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Embedded ack copy-run line (W2). CONSUMED is a consumer act; this is the
|
# Embedded ack copy-run line (W2). CONSUMED is a consumer act; this is the
|
||||||
# exact local-write line the consumer runs after durable capture.
|
# exact local-write line the consumer runs after durable capture.
|
||||||
#
|
#
|
||||||
@@ -726,19 +670,12 @@ cmd_render() {
|
|||||||
# itself was env-less (agent=="default"), baking "default" is no worse than
|
# itself was env-less (agent=="default"), baking "default" is no worse than
|
||||||
# today — the fix wins the common case where WAKE_AGENT was set at render.
|
# today — the fix wins the common case where WAKE_AGENT was set at render.
|
||||||
printf '\n-- ACK (copy-run; local-write only, never blocks on network) --\n'
|
printf '\n-- ACK (copy-run; local-write only, never blocks on network) --\n'
|
||||||
# #946: the embedded --upto is the CLAMPED cursor (ack_upto), never the raw
|
|
||||||
# observed cursor while a quarantined seq sits inside (consumed, observed] —
|
|
||||||
# the copy-run line itself must not instruct the consumer to record
|
|
||||||
# deliveries that never happened. The clamp is disclosed loudly.
|
|
||||||
if [ "$ack_upto" -ne "$observed" ]; then
|
|
||||||
printf '# ACK CLAMPED (#946): embedding --upto %s, not observed_seq %s — quarantined seq(s) %s were dead-lettered and NEVER delivered; an ordinary ack cannot step past them. Only ack.sh consumed ... --force-past-quarantine (loud) can.\n' "$ack_upto" "$observed" "$q_list"
|
|
||||||
fi
|
|
||||||
local ack_line agent_scrubbed
|
local ack_line agent_scrubbed
|
||||||
agent_scrubbed="$(_scrub_inline "$agent")"
|
agent_scrubbed="$(_scrub_inline "$agent")"
|
||||||
if [ -n "$wake_id" ]; then
|
if [ -n "$wake_id" ]; then
|
||||||
ack_line="$("$ACK_SH" embed --upto "$ack_upto" --agent "$agent_scrubbed" --wake-id "$wake_id" 2>/dev/null || true)"
|
ack_line="$("$ACK_SH" embed --upto "$observed" --agent "$agent_scrubbed" --wake-id "$wake_id" 2>/dev/null || true)"
|
||||||
else
|
else
|
||||||
ack_line="$("$ACK_SH" embed --upto "$ack_upto" --agent "$agent_scrubbed" 2>/dev/null || true)"
|
ack_line="$("$ACK_SH" embed --upto "$observed" --agent "$agent_scrubbed" 2>/dev/null || true)"
|
||||||
fi
|
fi
|
||||||
printf '%s\n' "${ack_line:-# ack unavailable}"
|
printf '%s\n' "${ack_line:-# ack unavailable}"
|
||||||
} | _redact_secrets
|
} | _redact_secrets
|
||||||
|
|||||||
@@ -341,41 +341,9 @@
|
|||||||
# as CLAIM@seq — and Q1/Q6-Q9 fixtures moved off the now-valid
|
# as CLAIM@seq — and Q1/Q6-Q9 fixtures moved off the now-valid
|
||||||
# path-bearing shape onto genuinely address-free shapes,
|
# path-bearing shape onto genuinely address-free shapes,
|
||||||
# amending the #920-era ruling that had pinned the live pilot's
|
# amending the #920-era ruling that had pinned the live pilot's
|
||||||
# own locator shape as the malformed example). Doc follow-up:
|
# own locator shape as the malformed example).
|
||||||
# #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).
|
|
||||||
component=wake
|
component=wake
|
||||||
version=0.6.15
|
version=0.6.14
|
||||||
|
|
||||||
# Watch-list schema this component consumes, and the INCLUSIVE range of
|
# Watch-list schema this component consumes, and the INCLUSIVE range of
|
||||||
# schema_version values it supports. A wake-watch-list.json whose schema_version
|
# schema_version values it supports. A wake-watch-list.json whose schema_version
|
||||||
|
|||||||
@@ -77,42 +77,11 @@ Commands:
|
|||||||
the actual paste is out of scope.
|
the actual paste is out of scope.
|
||||||
If --require-idle-cmd is given and it
|
If --require-idle-cmd is given and it
|
||||||
exits non-zero, emit nothing (not idle).
|
exits non-zero, emit nothing (not idle).
|
||||||
consume --upto N [--force-past-quarantine]
|
consume --upto N Advance consumed_seq over the contiguous
|
||||||
Advance consumed_seq over the contiguous
|
|
||||||
gapless prefix <=N; drop consumed
|
gapless prefix <=N; drop consumed
|
||||||
entries. Rejects a gap (cannot ack N
|
entries. Rejects a gap (cannot ack N
|
||||||
while N-1 is unconsumed). Cumulative &
|
while N-1 is unconsumed). Cumulative &
|
||||||
idempotent. REFUSES to advance past a
|
idempotent.
|
||||||
QUARANTINED seq (#946): a quarantined
|
|
||||||
entry was dead-lettered at render and
|
|
||||||
never delivered in any digest, so an
|
|
||||||
ordinary ack may not record it consumed.
|
|
||||||
--force-past-quarantine is the ONLY way
|
|
||||||
past — loud per stepped-over seq, and
|
|
||||||
even then NO consumed-hash witness is
|
|
||||||
recorded for the quarantined entry.
|
|
||||||
quarantine-sync REPLACE the store-owned quarantined.set
|
|
||||||
with the observed_seqs read from stdin
|
|
||||||
(one per line; empty input CLEARS).
|
|
||||||
Called by digest.sh after each
|
|
||||||
authoritative store render — the set is
|
|
||||||
re-DERIVED per render, never accumulated,
|
|
||||||
so a fixed locator gate self-heals the
|
|
||||||
consume clamp (#944 recovery).
|
|
||||||
quarantine-audit [--repair] Report consumed-hashes rows that are
|
|
||||||
PROVABLY FALSE: the row matches a
|
|
||||||
dead-letter ledger entry on
|
|
||||||
(kind,id,observed_seq,observed_hash) at
|
|
||||||
or below consumed_seq — i.e. the recorded
|
|
||||||
consumption was of a quarantined,
|
|
||||||
never-delivered entry (#946 Finding A).
|
|
||||||
Report-only by default (exit 1 when any
|
|
||||||
found); --repair removes exactly those
|
|
||||||
rows (atomic, loud). The dead-letter
|
|
||||||
ledger itself is NEVER modified (it is
|
|
||||||
history). Rows whose dead-letter evidence
|
|
||||||
was pruned are NOT provable and are
|
|
||||||
never touched.
|
|
||||||
cursors Print observed_seq / consumed_seq / depth.
|
cursors Print observed_seq / consumed_seq / depth.
|
||||||
|
|
||||||
Environment:
|
Environment:
|
||||||
@@ -385,20 +354,10 @@ cmd_drain() {
|
|||||||
# (the reconciler re-enumerates the consumed state once — no lost obligation),
|
# (the reconciler re-enumerates the consumed state once — no lost obligation),
|
||||||
# never a failed consume.
|
# never a failed consume.
|
||||||
_record_last_consumed() {
|
_record_last_consumed() {
|
||||||
local upto="$1" existing new_records merged qjson
|
local upto="$1" existing new_records merged
|
||||||
[ -f "$STATE_DIR/pending.jsonl" ] || return 0
|
[ -f "$STATE_DIR/pending.jsonl" ] || return 0
|
||||||
# #946: seqs in quarantined.set are EXCLUDED from the record — the trust
|
new_records="$(jq -c --argjson upto "$upto" '
|
||||||
# boundary above says "existence implies durably enqueued+CONSUMED", but a
|
select((.observed_seq // -1) <= $upto)
|
||||||
# quarantined entry was dead-lettered at render and NEVER delivered, so a row
|
|
||||||
# for it would witness a delivery that never happened. This holds even on the
|
|
||||||
# FORCED step-over path: the reconciler re-enumerating the state once is
|
|
||||||
# safe-but-noisy; a false witness silences it forever.
|
|
||||||
qjson="$(jq -nR -c '[inputs | select(length > 0) | tonumber? // empty]' "$STATE_DIR/quarantined.set" 2>/dev/null || true)"
|
|
||||||
[ -n "$qjson" ] || qjson='[]'
|
|
||||||
new_records="$(jq -c --argjson upto "$upto" --argjson quarantined "$qjson" '
|
|
||||||
(.observed_seq // -1) as $seq
|
|
||||||
| select($seq <= $upto)
|
|
||||||
| select(($quarantined | index($seq)) == null)
|
|
||||||
| select((.locators.kind // "") != "" and (.locators.id // "") != "" and (.locators.observed_hash // "") != "")
|
| select((.locators.kind // "") != "" and (.locators.id // "") != "" and (.locators.observed_hash // "") != "")
|
||||||
| {kind:.locators.kind, id:.locators.id, observed_hash:.locators.observed_hash, observed_seq:.observed_seq}
|
| {kind:.locators.kind, id:.locators.id, observed_hash:.locators.observed_hash, observed_seq:.observed_seq}
|
||||||
' "$STATE_DIR/pending.jsonl" 2>/dev/null || true)"
|
' "$STATE_DIR/pending.jsonl" 2>/dev/null || true)"
|
||||||
@@ -413,17 +372,13 @@ _record_last_consumed() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
cmd_consume() {
|
cmd_consume() {
|
||||||
local upto='' force=0
|
local upto=''
|
||||||
while [ $# -gt 0 ]; do
|
while [ $# -gt 0 ]; do
|
||||||
case "$1" in
|
case "$1" in
|
||||||
--upto)
|
--upto)
|
||||||
upto="${2:-}"
|
upto="${2:-}"
|
||||||
shift 2
|
shift 2
|
||||||
;;
|
;;
|
||||||
--force-past-quarantine)
|
|
||||||
force=1
|
|
||||||
shift
|
|
||||||
;;
|
|
||||||
*)
|
*)
|
||||||
echo "store.sh consume: unknown option '$1'" >&2
|
echo "store.sh consume: unknown option '$1'" >&2
|
||||||
exit 2
|
exit 2
|
||||||
@@ -471,33 +426,6 @@ cmd_consume() {
|
|||||||
k=$((k + 1))
|
k=$((k + 1))
|
||||||
done
|
done
|
||||||
|
|
||||||
# --- #946 quarantine CLAMP -------------------------------------------------
|
|
||||||
# A quarantined seq was DEAD-LETTERED at render (no §2.1 hard locator): it was
|
|
||||||
# never delivered in any digest, so advancing consumed_seq past it would record
|
|
||||||
# consumption of an entry the consumer has never seen. The ordinary path
|
|
||||||
# REFUSES; --force-past-quarantine is the ONLY way past, and it is loud per
|
|
||||||
# stepped-over seq. digest.sh re-derives the set at each authoritative store
|
|
||||||
# render (quarantine-sync REPLACE), so a fixed locator gate self-heals this
|
|
||||||
# clamp without operator action.
|
|
||||||
local qfile="$STATE_DIR/quarantined.set" blocked='' q
|
|
||||||
if [ -s "$qfile" ]; then
|
|
||||||
while IFS= read -r q; do
|
|
||||||
case "$q" in '' | *[!0-9]*) continue ;; esac
|
|
||||||
if [ "$q" -gt "$consumed" ] && [ "$q" -le "$upto" ]; then
|
|
||||||
blocked="$blocked $q"
|
|
||||||
fi
|
|
||||||
done <"$qfile"
|
|
||||||
fi
|
|
||||||
if [ -n "$blocked" ]; then
|
|
||||||
if [ "$force" -eq 0 ]; then
|
|
||||||
echo "store.sh consume: REFUSED (#946 quarantine clamp) — quarantined seq(s):$blocked inside (consumed_seq=$consumed, upto=$upto] were dead-lettered at render and NEVER delivered in any digest. Advancing past them would record consumption of entries the consumer has never seen. Disposition them (see $STATE_DIR/dead-letter.jsonl) or step over EXPLICITLY with --force-past-quarantine." >&2
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
for q in $blocked; do
|
|
||||||
echo "store.sh consume: FORCED PAST QUARANTINE (#946) — stepping consumed_seq over quarantined seq $q (dead-lettered, NEVER delivered). No consumed-hash witness is recorded for it; the obligation stays visible ONLY in $STATE_DIR/dead-letter.jsonl." >&2
|
|
||||||
done
|
|
||||||
fi
|
|
||||||
|
|
||||||
# #932: record the last-consumed observed_hash per (kind,id) BEFORE the pending
|
# #932: record the last-consumed observed_hash per (kind,id) BEFORE the pending
|
||||||
# prefix is dropped (this reads the entries about to be truncated), so the
|
# prefix is dropped (this reads the entries about to be truncated), so the
|
||||||
# reconciler can recognise an already-consumed state as ACCOUNTED instead of
|
# reconciler can recognise an already-consumed state as ACCOUNTED instead of
|
||||||
@@ -511,137 +439,9 @@ cmd_consume() {
|
|||||||
awk -v c="$upto" 'NF && $1+0 > c' "$STATE_DIR/observed.set" 2>/dev/null |
|
awk -v c="$upto" 'NF && $1+0 > c' "$STATE_DIR/observed.set" 2>/dev/null |
|
||||||
_atomic_write "$STATE_DIR/observed.set" || true
|
_atomic_write "$STATE_DIR/observed.set" || true
|
||||||
printf '%s' "$upto" | _atomic_write "$STATE_DIR/consumed_seq"
|
printf '%s' "$upto" | _atomic_write "$STATE_DIR/consumed_seq"
|
||||||
# #946: on a forced step-over, PRUNE the stepped-over seqs from quarantined.set
|
|
||||||
# (they are inside the consumed prefix now; a stale entry would re-refuse the
|
|
||||||
# next consume forever). Mirrors the observed.set prune idiom above.
|
|
||||||
if [ -n "$blocked" ]; then
|
|
||||||
awk -v c="$upto" 'NF && $1+0 > c' "$qfile" 2>/dev/null |
|
|
||||||
_atomic_write "$qfile" || true
|
|
||||||
fi
|
|
||||||
echo "$upto"
|
echo "$upto"
|
||||||
}
|
}
|
||||||
|
|
||||||
# cmd_quarantine_sync — #946: REPLACE the store-owned quarantined.set with the
|
|
||||||
# observed_seqs read from stdin (one per line). Called by digest.sh after each
|
|
||||||
# AUTHORITATIVE full-set render (src=store): the set is re-DERIVED per render,
|
|
||||||
# never accumulated, so a fixed locator gate self-heals the consume clamp (the
|
|
||||||
# #944 recovery case — a cumulative-forever set would keep refusing acks on
|
|
||||||
# entries that now render). Empty input CLEARS the set. Foreign-data renders
|
|
||||||
# (--from-file/--stdin) never call this. Atomic write; invalid input is refused
|
|
||||||
# loudly with the set left untouched.
|
|
||||||
cmd_quarantine_sync() {
|
|
||||||
[ $# -eq 0 ] || {
|
|
||||||
echo "store.sh quarantine-sync: takes no options (observed_seqs on stdin, one per line)" >&2
|
|
||||||
exit 2
|
|
||||||
}
|
|
||||||
local seq list=''
|
|
||||||
while IFS= read -r seq; do
|
|
||||||
[ -n "$seq" ] || continue
|
|
||||||
case "$seq" in
|
|
||||||
*[!0-9]*)
|
|
||||||
echo "store.sh quarantine-sync: invalid observed_seq '$seq' — one non-negative integer per line; set left untouched" >&2
|
|
||||||
exit 2
|
|
||||||
;;
|
|
||||||
esac
|
|
||||||
list="$list$seq"$'\n'
|
|
||||||
done
|
|
||||||
_wake_init_dir "$STATE_DIR"
|
|
||||||
if ! { printf '%s' "$list" | grep -v '^[[:space:]]*$' || true; } | sort -n | uniq | _atomic_write "$STATE_DIR/quarantined.set"; then
|
|
||||||
echo "store.sh quarantine-sync: quarantined.set write FAILED — the consume clamp may be stale for this lane" >&2
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
# cmd_quarantine_audit — #946 Finding A: the pre-#946 consume recorded
|
|
||||||
# consumed-hash rows for QUARANTINED (never-delivered) entries — false
|
|
||||||
# witnesses that silence the reconciler for keys whose only "consumption" was a
|
|
||||||
# dead-lettered entry (live: safe by population only where the key re-emitted
|
|
||||||
# and a later seq won the per-key max_by merge; keys that never recurred keep
|
|
||||||
# the false row forever).
|
|
||||||
#
|
|
||||||
# A row is PROVABLY FALSE iff the dead-letter ledger contains an entry matching
|
|
||||||
# it on (kind, id, observed_seq, observed_hash) AND row.observed_seq <=
|
|
||||||
# consumed_seq: the per-key max_by merge means the surviving row's provenance IS
|
|
||||||
# that quarantined entry (a healed row differs in seq/hash and never matches).
|
|
||||||
# PROVABILITY BOUND: a row whose dead-letter evidence was pruned/rotated away is
|
|
||||||
# NOT provable and is never touched — this audit only ever removes what the
|
|
||||||
# ledger can convict. The dead-letter ledger itself is history and is NEVER
|
|
||||||
# modified here.
|
|
||||||
cmd_quarantine_audit() {
|
|
||||||
local repair=0
|
|
||||||
while [ $# -gt 0 ]; do
|
|
||||||
case "$1" in
|
|
||||||
--repair)
|
|
||||||
repair=1
|
|
||||||
shift
|
|
||||||
;;
|
|
||||||
*)
|
|
||||||
echo "store.sh quarantine-audit: unknown option '$1'" >&2
|
|
||||||
exit 2
|
|
||||||
;;
|
|
||||||
esac
|
|
||||||
done
|
|
||||||
_need_jq
|
|
||||||
_wake_init_dir "$STATE_DIR"
|
|
||||||
local rec="$STATE_DIR/consumed-hashes.jsonl" dlf="$STATE_DIR/dead-letter.jsonl"
|
|
||||||
if [ ! -s "$rec" ]; then
|
|
||||||
echo "store.sh quarantine-audit: OK — no consumed-hashes record to audit"
|
|
||||||
return 0
|
|
||||||
fi
|
|
||||||
local dl
|
|
||||||
dl="$(jq -s -c '[.[] | {kind: (.locators.kind // ""), id: ((.locators.id // "") | tostring), observed_seq: (.observed_seq // -1), observed_hash: (.locators.observed_hash // "")}]' "$dlf" 2>/dev/null || true)"
|
|
||||||
[ -n "$dl" ] || dl='[]'
|
|
||||||
local consumed
|
|
||||||
consumed="$(_wake_read_int "$STATE_DIR/consumed_seq" 0)"
|
|
||||||
local false_rows
|
|
||||||
false_rows="$(jq -c --argjson dl "$dl" --argjson consumed "$consumed" '
|
|
||||||
. as $row
|
|
||||||
| select(($row.observed_seq // -1) <= $consumed)
|
|
||||||
| select(($dl | map(select(
|
|
||||||
.kind == ($row.kind // "")
|
|
||||||
and .id == (($row.id // "") | tostring)
|
|
||||||
and .observed_seq == ($row.observed_seq // -1)
|
|
||||||
and .observed_hash == ($row.observed_hash // "")
|
|
||||||
)) | length) > 0)
|
|
||||||
' "$rec" 2>/dev/null || true)"
|
|
||||||
if [ -z "$false_rows" ]; then
|
|
||||||
echo "store.sh quarantine-audit: OK — no provably-false consumed-hash rows (rows without surviving dead-letter evidence are not provable and were not judged)"
|
|
||||||
return 0
|
|
||||||
fi
|
|
||||||
local n row
|
|
||||||
n="$(printf '%s\n' "$false_rows" | grep -c . || true)"
|
|
||||||
while IFS= read -r row; do
|
|
||||||
[ -n "$row" ] || continue
|
|
||||||
if [ "$repair" -eq 1 ]; then
|
|
||||||
echo "store.sh quarantine-audit: REPAIR — removing FALSE WITNESS row $row (matches a dead-lettered, never-delivered entry at/below consumed_seq=$consumed)" >&2
|
|
||||||
else
|
|
||||||
echo "FALSE WITNESS — consumed-hashes row $row matches a dead-lettered, never-delivered entry at/below consumed_seq=$consumed (the recorded consumption never happened)"
|
|
||||||
fi
|
|
||||||
done <<<"$false_rows"
|
|
||||||
if [ "$repair" -eq 0 ]; then
|
|
||||||
echo "store.sh quarantine-audit: $n provably-false row(s) found — run with --repair to remove exactly these rows"
|
|
||||||
return 1
|
|
||||||
fi
|
|
||||||
local kept
|
|
||||||
kept="$(jq -c --argjson dl "$dl" --argjson consumed "$consumed" '
|
|
||||||
. as $row
|
|
||||||
| select(
|
|
||||||
(($row.observed_seq // -1) > $consumed)
|
|
||||||
or (($dl | map(select(
|
|
||||||
.kind == ($row.kind // "")
|
|
||||||
and .id == (($row.id // "") | tostring)
|
|
||||||
and .observed_seq == ($row.observed_seq // -1)
|
|
||||||
and .observed_hash == ($row.observed_hash // "")
|
|
||||||
)) | length) == 0)
|
|
||||||
)
|
|
||||||
' "$rec" 2>/dev/null || true)"
|
|
||||||
if ! { printf '%s\n' "$kept" | grep -v '^[[:space:]]*$' || true; } | _atomic_write "$rec"; then
|
|
||||||
echo "store.sh quarantine-audit: consumed-hashes rewrite FAILED — record left untouched" >&2
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
echo "store.sh quarantine-audit: repaired — removed $n provably-false row(s); dead-letter ledger untouched (history)"
|
|
||||||
}
|
|
||||||
|
|
||||||
cmd_cursors() {
|
cmd_cursors() {
|
||||||
_wake_init_dir "$STATE_DIR"
|
_wake_init_dir "$STATE_DIR"
|
||||||
local observed consumed depth
|
local observed consumed depth
|
||||||
@@ -663,8 +463,6 @@ main() {
|
|||||||
enqueue) cmd_enqueue "$@" ;;
|
enqueue) cmd_enqueue "$@" ;;
|
||||||
drain) cmd_drain "$@" ;;
|
drain) cmd_drain "$@" ;;
|
||||||
consume) cmd_consume "$@" ;;
|
consume) cmd_consume "$@" ;;
|
||||||
quarantine-sync) cmd_quarantine_sync "$@" ;;
|
|
||||||
quarantine-audit) cmd_quarantine_audit "$@" ;;
|
|
||||||
cursors) cmd_cursors "$@" ;;
|
cursors) cmd_cursors "$@" ;;
|
||||||
-h | --help | help) usage ;;
|
-h | --help | help) usage ;;
|
||||||
*)
|
*)
|
||||||
|
|||||||
@@ -14,10 +14,9 @@
|
|||||||
# {kind,id,path,observed_hash} as the malformed case — ratifying a gate
|
# {kind,id,path,observed_hash} as the malformed case — ratifying a gate
|
||||||
# the detector's own locators could never satisfy (every actionable
|
# the detector's own locators could never satisfy (every actionable
|
||||||
# heartbeat-planning delta dead-lettered; live seqs 63/68). #944 amends
|
# heartbeat-planning delta dead-lettered; live seqs 63/68). #944 amends
|
||||||
# that ruling: `path` is now a hard-locator arm — and ONLY path;
|
# that ruling: `path` and 40-hex `snapshot_sha` are now hard-locator
|
||||||
# bare snapshot_sha is deliberately NOT an arm, Q11(d) asserts it
|
# arms (see Q11), so the quarantine fixtures here and in Q6-Q9 use
|
||||||
# still quarantines — so the quarantine fixtures here and in Q6-Q9
|
# genuinely ADDRESS-FREE shapes instead.]
|
||||||
# use genuinely ADDRESS-FREE shapes instead.]
|
|
||||||
# Q2 (b) ENUM-AS-ORIENTATION: a reconciler enumeration (locators.reconciled==
|
# Q2 (b) ENUM-AS-ORIENTATION: a reconciler enumeration (locators.reconciled==
|
||||||
# true) renders as an ORIENTATION-tier pointer and does NOT exit-4 / is
|
# true) renders as an ORIENTATION-tier pointer and does NOT exit-4 / is
|
||||||
# NOT quarantined. RED baseline: reconciled `actionable` + soft locators
|
# NOT quarantined. RED baseline: reconciled `actionable` + soft locators
|
||||||
@@ -74,42 +73,15 @@
|
|||||||
# attestation — a pre-#940 adapter or a dropped attestation) must
|
# attestation — a pre-#940 adapter or a dropped attestation) must
|
||||||
# ALSO render, with the "re-read <path>" hint; (c) an address-free
|
# ALSO render, with the "re-read <path>" hint; (c) an address-free
|
||||||
# sibling ({kind,id,observed_hash}) must STILL quarantine + route its
|
# sibling ({kind,id,observed_hash}) must STILL quarantine + route its
|
||||||
# own alarm — observed_hash is a content hash, not an address; (d)
|
# own alarm — observed_hash is a content hash, not an address; (d) a
|
||||||
# bare path-less snapshot_sha siblings must ALSO still quarantine —
|
# bare path-less 40-hex snapshot_sha sibling must ALSO still
|
||||||
# the widened gate must not widen PAST the board_file vocabulary
|
# quarantine — the widened gate must not widen PAST the board_file
|
||||||
# (review-adopted criterion) — asserted at THREE lengths (7-char
|
# vocabulary (review-adopted criterion). RED baseline: pre-#944
|
||||||
# abbreviation, 40-hex, 64-char sha-256) spanning the detector's
|
|
||||||
# actual attestation validation ^[0-9a-f]{7,64}$ (detector.sh), so
|
|
||||||
# the assertion distinguishes "no snapshot_sha arm" from "an arm
|
|
||||||
# present but length-gated". RED baseline: pre-#944
|
|
||||||
# digest.sh dead-letters (a) and (b) — an assertion nobody has seen
|
# digest.sh dead-letters (a) and (b) — an assertion nobody has seen
|
||||||
# succeed is as unproven as one nobody has seen fail.
|
# succeed is as unproven as one nobody has seen fail.
|
||||||
#
|
#
|
||||||
# #946 (ack watermark passes quarantined entries): the rendered digest embedded
|
|
||||||
# `ack.sh consumed --upto <observed>` even when entries in (consumed, observed]
|
|
||||||
# were quarantined — the copy-run line itself instructed the consumer to record
|
|
||||||
# deliveries that never happened (live: five successive digests each stepping
|
|
||||||
# the consumer past buried seq 68). Fix = DISCLOSE + CLAMP + force-only-past:
|
|
||||||
# Q12 disclosure (by seq — content stays EXCLUDED per Q1/Q4) + the
|
|
||||||
# embedded ack CLAMPED below the lowest quarantined seq, hermetic
|
|
||||||
# --from-file; a foreign-data render must NOT write the store's
|
|
||||||
# quarantined.set.
|
|
||||||
# Q13 nothing quarantined -> unclamped ack at the observed cursor; no
|
|
||||||
# disclosure section, no clamp note.
|
|
||||||
# Q14 store-mode render SYNCS quarantined.set (REPLACE) -> the store's
|
|
||||||
# ordinary consume path refuses past the held seq END-TO-END.
|
|
||||||
# Q15 gate-fix RECOVERY: a stale quarantined.set is REPLACED (cleared) by
|
|
||||||
# a clean store-mode render — the clamp self-heals (#944 recovery
|
|
||||||
# invariant; a cumulative-forever set would keep blocking acks on
|
|
||||||
# entries a fixed gate now renders).
|
|
||||||
# Q16 (guard, green-by-design) Q2's ENUM-B fixture must STAY address-free
|
|
||||||
# so the reconciled exemption remains load-bearing at the gate
|
|
||||||
# (#944 F1); goes RED only if the fixture regresses.
|
|
||||||
#
|
|
||||||
# Hermetic: feeds controlled JSONL via `digest.sh render --from-file` — NO store,
|
# Hermetic: feeds controlled JSONL via `digest.sh render --from-file` — NO store,
|
||||||
# NO network, NO openssl (so it runs identically under the CI openssl-mask).
|
# NO network, NO openssl (so it runs identically under the CI openssl-mask).
|
||||||
# (Q14/Q15 are the intentional exception: the #946 store sync is store-mode-only
|
|
||||||
# behavior, so they drive store.sh enqueue/consume against a temp state home.)
|
|
||||||
#
|
#
|
||||||
# Each test runs in its own (..) subshell for env isolation; the per-subshell
|
# Each test runs in its own (..) subshell for env isolation; the per-subshell
|
||||||
# WAKE_STATE_HOME export is intentional (mirrors test-wake-reconcile.sh).
|
# WAKE_STATE_HOME export is intentional (mirrors test-wake-reconcile.sh).
|
||||||
@@ -191,11 +163,7 @@ echo "== Q2 (b): reconciler enumeration (reconciled:true) renders ORIENTATION-ti
|
|||||||
unset WAKE_AGENT
|
unset WAKE_AGENT
|
||||||
f="$TMP_ROOT/q2.jsonl"
|
f="$TMP_ROOT/q2.jsonl"
|
||||||
# A reconciler enumeration: store class actionable (unchanged) + reconciled marker.
|
# A reconciler enumeration: store class actionable (unchanged) + reconciled marker.
|
||||||
# ADDRESS-FREE on purpose (#944 F1): no path/file/sha/repo+issue — the reconciled
|
printf '%s\n' '{"observed_seq":5,"class":"actionable","locators":{"kind":"repo","id":"ENUM-B","path":"BOARD.md","observed_hash":"cafe1234","reconciled":true},"emit_ts":1}' >"$f"
|
||||||
# exemption must be the ONLY thing keeping this entry out of quarantine, so the
|
|
||||||
# exemption is proven load-bearing AT THE GATE (mutation-killable), not merely at
|
|
||||||
# the tier label. (Q3's ENUM-C* stay path-bearing: reconciled + valid-locator mix.)
|
|
||||||
printf '%s\n' '{"observed_seq":5,"class":"actionable","locators":{"kind":"repo","id":"ENUM-B","observed_hash":"cafe1234","reconciled":true},"emit_ts":1}' >"$f"
|
|
||||||
err="$TMP_ROOT/q2.err"
|
err="$TMP_ROOT/q2.err"
|
||||||
out="$("$DIGEST" render --from-file "$f" --agent default 2>"$err")"
|
out="$("$DIGEST" render --from-file "$f" --agent default 2>"$err")"
|
||||||
rc=$?
|
rc=$?
|
||||||
@@ -414,15 +382,9 @@ echo "== Q11 (#944): REAL detector-shape actionable RENDERS as CLAIM@seq; addres
|
|||||||
printf '%s\n' '{"observed_seq":69,"class":"actionable","locators":{"kind":"board_file","id":"PILOT-LOCAL","observed_hash":"abcd1234","path":"BOARD.md"},"emit_ts":2}'
|
printf '%s\n' '{"observed_seq":69,"class":"actionable","locators":{"kind":"board_file","id":"PILOT-LOCAL","observed_hash":"abcd1234","path":"BOARD.md"},"emit_ts":2}'
|
||||||
# (c) ADDRESS-FREE — the retained negative: a content hash is not an address.
|
# (c) ADDRESS-FREE — the retained negative: a content hash is not an address.
|
||||||
printf '%s\n' '{"observed_seq":70,"class":"actionable","locators":{"kind":"board_file","id":"ADDR-FREE","observed_hash":"ffff0000"},"emit_ts":2}'
|
printf '%s\n' '{"observed_seq":70,"class":"actionable","locators":{"kind":"board_file","id":"ADDR-FREE","observed_hash":"ffff0000"},"emit_ts":2}'
|
||||||
# (d) bare path-less snapshot_sha — must NOT pass: the widened gate must
|
# (d) bare path-less 40-hex snapshot_sha — must NOT pass: the widened gate
|
||||||
# not widen past the board_file vocabulary. Asserted at all three
|
# must not widen past the board_file vocabulary.
|
||||||
# lengths the detector's attestation validation ^[0-9a-f]{7,64}$
|
printf '%s\n' '{"observed_seq":71,"class":"actionable","locators":{"kind":"board_file","id":"SNAP-ONLY","observed_hash":"eeee1111","snapshot_sha":"55d4909569d2b5fbccfec49ec9ca83db5049f3ce"},"emit_ts":2}'
|
||||||
# admits: a 40-hex sha-1, a 7-char abbreviation, a 64-char sha-256.
|
|
||||||
# One length alone cannot distinguish "no arm" from "arm present but
|
|
||||||
# length-gated" (enumeration finding E1).
|
|
||||||
printf '%s\n' '{"observed_seq":71,"class":"actionable","locators":{"kind":"board_file","id":"SNAP-ONLY-40","observed_hash":"eeee1111","snapshot_sha":"55d4909569d2b5fbccfec49ec9ca83db5049f3ce"},"emit_ts":2}'
|
|
||||||
printf '%s\n' '{"observed_seq":72,"class":"actionable","locators":{"kind":"board_file","id":"SNAP-ONLY-7","observed_hash":"eeee2222","snapshot_sha":"55d4909"},"emit_ts":2}'
|
|
||||||
printf '%s\n' '{"observed_seq":73,"class":"actionable","locators":{"kind":"board_file","id":"SNAP-ONLY-64","observed_hash":"eeee3333","snapshot_sha":"55d4909569d2b5fbccfec49ec9ca83db5049f3ce55d4909569d2b5fbccfec49e"},"emit_ts":2}'
|
|
||||||
} >"$f"
|
} >"$f"
|
||||||
ALARM_OUT="$TMP_ROOT/q11.alarm.jsonl"
|
ALARM_OUT="$TMP_ROOT/q11.alarm.jsonl"
|
||||||
export ALARM_OUT
|
export ALARM_OUT
|
||||||
@@ -444,135 +406,15 @@ echo "== Q11 (#944): REAL detector-shape actionable RENDERS as CLAIM@seq; addres
|
|||||||
[ "$n_claims" = "2" ] || fail_msg "Q11: EXACTLY the two valid entries must render as CLAIM@seq (got $n_claims)"
|
[ "$n_claims" = "2" ] || fail_msg "Q11: EXACTLY the two valid entries must render as CLAIM@seq (got $n_claims)"
|
||||||
# (c)+(d) NEGATIVES retained: both quarantine, each with its OWN alarm.
|
# (c)+(d) NEGATIVES retained: both quarantine, each with its OWN alarm.
|
||||||
printf '%s' "$out" | grep -q 'ADDR-FREE' && fail_msg "Q11c: the address-free entry must be EXCLUDED from the digest"
|
printf '%s' "$out" | grep -q 'ADDR-FREE' && fail_msg "Q11c: the address-free entry must be EXCLUDED from the digest"
|
||||||
printf '%s' "$out" | grep -q 'SNAP-ONLY' && fail_msg "Q11d: no bare path-less snapshot_sha entry may appear in the digest (gate must not widen past board_file)"
|
printf '%s' "$out" | grep -q 'SNAP-ONLY' && fail_msg "Q11d: the bare path-less snapshot_sha entry must be EXCLUDED (gate must not widen past board_file)"
|
||||||
grep -q 'ADDR-FREE' "$(dlq "$home")" 2>/dev/null || fail_msg "Q11c: the address-free entry must be DEAD-LETTERED"
|
grep -q 'ADDR-FREE' "$(dlq "$home")" 2>/dev/null || fail_msg "Q11c: the address-free entry must be DEAD-LETTERED"
|
||||||
for snap_id in SNAP-ONLY-40 SNAP-ONLY-7 SNAP-ONLY-64; do
|
grep -q 'SNAP-ONLY' "$(dlq "$home")" 2>/dev/null || fail_msg "Q11d: the bare snapshot_sha entry must be DEAD-LETTERED"
|
||||||
grep -q "$snap_id" "$(dlq "$home")" 2>/dev/null || fail_msg "Q11d: the bare snapshot_sha entry ($snap_id) must be DEAD-LETTERED"
|
|
||||||
done
|
|
||||||
grep -q 'heartbeat-planning' "$(dlq "$home")" 2>/dev/null && fail_msg "Q11a: the valid live entry must NOT be dead-lettered"
|
grep -q 'heartbeat-planning' "$(dlq "$home")" 2>/dev/null && fail_msg "Q11a: the valid live entry must NOT be dead-lettered"
|
||||||
grep -q 'PILOT-LOCAL' "$(dlq "$home")" 2>/dev/null && fail_msg "Q11b: the valid path-only entry must NOT be dead-lettered"
|
grep -q 'PILOT-LOCAL' "$(dlq "$home")" 2>/dev/null && fail_msg "Q11b: the valid path-only entry must NOT be dead-lettered"
|
||||||
n_alarms="$(grep -c . "$ALARM_OUT" 2>/dev/null || true)"
|
n_alarms="$(grep -c . "$ALARM_OUT" 2>/dev/null || true)"
|
||||||
[ "$n_alarms" = "4" ] || fail_msg "Q11: exactly the four invalid entries must alarm (got $n_alarms) [$(cat "$ALARM_OUT" 2>/dev/null)]"
|
[ "$n_alarms" = "2" ] || fail_msg "Q11: exactly the two invalid entries must alarm (got $n_alarms) [$(cat "$ALARM_OUT" 2>/dev/null)]"
|
||||||
grep -q '"observed_seq":70' "$ALARM_OUT" 2>/dev/null || fail_msg "Q11c: seq 70's own alarm must be present"
|
grep -q '"observed_seq":70' "$ALARM_OUT" 2>/dev/null || fail_msg "Q11c: seq 70's own alarm must be present"
|
||||||
for snap_seq in 71 72 73; do
|
grep -q '"observed_seq":71' "$ALARM_OUT" 2>/dev/null || fail_msg "Q11d: seq 71's own alarm must be present"
|
||||||
grep -q "\"observed_seq\":$snap_seq" "$ALARM_OUT" 2>/dev/null || fail_msg "Q11d: seq $snap_seq's own alarm must be present"
|
|
||||||
done
|
|
||||||
true
|
|
||||||
) && ok
|
|
||||||
|
|
||||||
echo "== Q12 (#946): quarantined entries are DISCLOSED (by seq, content withheld) and the embedded ack is CLAMPED below them =="
|
|
||||||
(
|
|
||||||
home="$(fresh_home q12)"
|
|
||||||
export WAKE_STATE_HOME="$home"
|
|
||||||
unset WAKE_AGENT
|
|
||||||
mkdir -p "$home/default"
|
|
||||||
printf '2' >"$home/default/observed_seq"
|
|
||||||
f="$TMP_ROOT/q12.jsonl"
|
|
||||||
{
|
|
||||||
printf '{"observed_seq":1,"class":"actionable","locators":{"sha":"%s","file":"src/a.ts"},"emit_ts":1}\n' "$SHA40"
|
|
||||||
printf '%s\n' '{"observed_seq":2,"class":"actionable","locators":{"kind":"board_file","id":"ADDR-Q12","observed_hash":"qq12"},"emit_ts":1}'
|
|
||||||
} >"$f"
|
|
||||||
out="$("$DIGEST" render --from-file "$f" --agent default 2>/dev/null)"
|
|
||||||
rc=$?
|
|
||||||
[ "$rc" -eq 0 ] || fail_msg "Q12: render must exit 0, got rc=$rc"
|
|
||||||
# DISCLOSURE: a held entry must be VISIBLE in the digest it was held from —
|
|
||||||
# a silent hold is how five successive digests each stepped past seq 68...
|
|
||||||
printf '%s' "$out" | grep -q 'QUARANTINED' || fail_msg "Q12: the digest must carry a QUARANTINED disclosure section (no silent hold)"
|
|
||||||
printf '%s' "$out" | grep -q 'seq 2 .*HELD' || fail_msg "Q12: the disclosure must name the held seq (2) as HELD"
|
|
||||||
# ...but WITHOUT re-injecting the refused content: disclosure is by seq only;
|
|
||||||
# the Q1/Q4/Q5/Q6/Q9/Q11 exclusion property stands.
|
|
||||||
printf '%s' "$out" | grep -q 'ADDR-Q12' && fail_msg "Q12: the quarantined entry's content/locators must stay EXCLUDED from the digest"
|
|
||||||
# CLAMP: the embedded ack stops BELOW the quarantined seq, and says so loudly.
|
|
||||||
printf '%s' "$out" | grep -Eq 'consumed --upto 1$' || fail_msg "Q12: the embedded ack must be CLAMPED to --upto 1 (below quarantined seq 2)"
|
|
||||||
printf '%s' "$out" | grep -Eq 'consumed --upto 2( |$)' && fail_msg "Q12: the raw observed cursor (2) must NOT be embedded while seq 2 is quarantined"
|
|
||||||
printf '%s' "$out" | grep -q 'ACK CLAMPED' || fail_msg "Q12: the clamp must be LOUDLY disclosed in the ACK section"
|
|
||||||
# A foreign-data render must NOT rewrite the lane's quarantine truth.
|
|
||||||
[ -e "$home/default/quarantined.set" ] && fail_msg "Q12: a --from-file render must NOT write the store's quarantined.set (lane truth is store-mode only)"
|
|
||||||
true
|
|
||||||
) && ok
|
|
||||||
|
|
||||||
echo "== Q13 (#946): nothing quarantined -> ack UNCLAMPED at the observed cursor; no disclosure section, no clamp note =="
|
|
||||||
(
|
|
||||||
home="$(fresh_home q13)"
|
|
||||||
export WAKE_STATE_HOME="$home"
|
|
||||||
unset WAKE_AGENT
|
|
||||||
mkdir -p "$home/default"
|
|
||||||
printf '1' >"$home/default/observed_seq"
|
|
||||||
f="$TMP_ROOT/q13.jsonl"
|
|
||||||
printf '{"observed_seq":1,"class":"actionable","locators":{"sha":"%s","file":"src/a.ts"},"emit_ts":1}\n' "$SHA40" >"$f"
|
|
||||||
out="$("$DIGEST" render --from-file "$f" --agent default 2>/dev/null)"
|
|
||||||
rc=$?
|
|
||||||
[ "$rc" -eq 0 ] || fail_msg "Q13: render must exit 0, got rc=$rc"
|
|
||||||
printf '%s' "$out" | grep -Eq 'consumed --upto 1$' || fail_msg "Q13: with nothing quarantined the ack must embed the observed cursor (1) unchanged"
|
|
||||||
printf '%s' "$out" | grep -q 'QUARANTINED' && fail_msg "Q13: no disclosure section when nothing is quarantined"
|
|
||||||
printf '%s' "$out" | grep -q 'ACK CLAMPED' && fail_msg "Q13: no clamp note when nothing is quarantined"
|
|
||||||
true
|
|
||||||
) && ok
|
|
||||||
|
|
||||||
echo "== Q14 (#946): store-mode render SYNCS quarantine truth into the store — the clamp is enforced END-TO-END at consume =="
|
|
||||||
(
|
|
||||||
home="$(fresh_home q14)"
|
|
||||||
export WAKE_STATE_HOME="$home"
|
|
||||||
unset WAKE_AGENT
|
|
||||||
STORE="$SCRIPT_DIR/store.sh"
|
|
||||||
"$STORE" enqueue --class actionable --locators "{\"sha\":\"$SHA40\",\"file\":\"src/a.ts\"}" >/dev/null
|
|
||||||
"$STORE" enqueue --class actionable --locators '{"kind":"board_file","id":"ADDR-Q14","observed_hash":"qq14"}' >/dev/null
|
|
||||||
out="$("$DIGEST" render --from-store --agent default 2>/dev/null)"
|
|
||||||
rc=$?
|
|
||||||
[ "$rc" -eq 0 ] || fail_msg "Q14: render must exit 0, got rc=$rc"
|
|
||||||
printf '%s' "$out" | grep -q 'QUARANTINED' || fail_msg "Q14: the store-mode digest must disclose the held entry"
|
|
||||||
printf '%s' "$out" | grep -Eq 'consumed --upto 1$' || fail_msg "Q14: the embedded ack must clamp to 1 (below quarantined seq 2)"
|
|
||||||
qf="$home/default/quarantined.set"
|
|
||||||
[ "$(cat "$qf" 2>/dev/null)" = "2" ] || fail_msg "Q14: a store-mode render must sync quarantined.set to exactly {2}, got [$(cat "$qf" 2>/dev/null)]"
|
|
||||||
# END-TO-END: even a hand-typed upto past the held seq is refused at the
|
|
||||||
# store — the copy-run defect (#946) cannot re-land via a different path.
|
|
||||||
if "$STORE" consume --upto 2 >/dev/null 2>&1; then
|
|
||||||
fail_msg "Q14: store consume --upto 2 must be REFUSED after the render synced the quarantine"
|
|
||||||
fi
|
|
||||||
"$STORE" consume --upto 1 >/dev/null 2>&1 || fail_msg "Q14: consume --upto 1 (the clamped value) must succeed"
|
|
||||||
) && ok
|
|
||||||
|
|
||||||
echo "== Q15 (#946): gate-fix RECOVERY — a stale quarantined.set is REPLACED by a clean store-mode render; the clamp self-heals =="
|
|
||||||
(
|
|
||||||
home="$(fresh_home q15)"
|
|
||||||
export WAKE_STATE_HOME="$home"
|
|
||||||
unset WAKE_AGENT
|
|
||||||
STORE="$SCRIPT_DIR/store.sh"
|
|
||||||
"$STORE" enqueue --class actionable --locators "{\"sha\":\"$SHA40\",\"file\":\"src/a.ts\"}" >/dev/null
|
|
||||||
"$STORE" enqueue --class actionable --locators '{"kind":"board_file","id":"OK-NOW","observed_hash":"h","path":"BOARD.md"}' >/dev/null
|
|
||||||
# A stale set from a broken-gate era: both seqs wrongly quarantined.
|
|
||||||
printf '1\n2\n' >"$home/default/quarantined.set"
|
|
||||||
out="$("$DIGEST" render --from-store --agent default 2>/dev/null)"
|
|
||||||
rc=$?
|
|
||||||
[ "$rc" -eq 0 ] || fail_msg "Q15: render must exit 0, got rc=$rc"
|
|
||||||
printf '%s' "$out" | grep -q 'QUARANTINED' && fail_msg "Q15: nothing quarantines under the fixed gate — no disclosure section"
|
|
||||||
printf '%s' "$out" | grep -Eq 'consumed --upto 2$' || fail_msg "Q15: the ack must embed the full observed cursor (2) once the gate is fixed"
|
|
||||||
[ -s "$home/default/quarantined.set" ] && fail_msg "Q15: the clean render must REPLACE (clear) the stale quarantined.set — a cumulative-forever set would block acks on entries that now render, got [$(cat "$home/default/quarantined.set")]"
|
|
||||||
"$STORE" consume --upto 2 >/dev/null 2>&1 || fail_msg "Q15: the ordinary consume must succeed after the clamp self-heals"
|
|
||||||
) && ok
|
|
||||||
|
|
||||||
echo "== Q16 (guard): Q2's ENUM-B fixture must STAY address-free — the reconciled exemption must remain load-bearing at the gate (#944 F1) =="
|
|
||||||
(
|
|
||||||
self="$SCRIPT_DIR/test-wake-digest-quarantine.sh"
|
|
||||||
# Token concatenated so THIS guard's own source lines never contain the
|
|
||||||
# literal fixture id and cannot self-match.
|
|
||||||
enum_id='ENUM''-B'
|
|
||||||
fixture_line="$(grep -F "\"id\":\"$enum_id\"" "$self" | grep -F '"observed_seq":5' | head -n1)"
|
|
||||||
[ -n "$fixture_line" ] || fail_msg "Q16: could not locate Q2's $enum_id fixture line (renamed/renumbered? update this guard)"
|
|
||||||
fixture_json="$(printf '%s' "$fixture_line" | sed "s/.*'\({.*}\)'.*/\1/")"
|
|
||||||
# Positive controls FIRST (blind-instrument rule): the extraction must yield
|
|
||||||
# the real fixture, and the predicate must be able to detect a hard locator.
|
|
||||||
printf '%s' "$fixture_json" | jq -e . >/dev/null 2>&1 || fail_msg "Q16: extracted fixture is not valid JSON [$fixture_json]"
|
|
||||||
printf '%s' "$fixture_json" | jq -e '.locators.reconciled == true' >/dev/null 2>&1 || fail_msg "Q16: fixture must carry reconciled:true (wrong line extracted?) [$fixture_json]"
|
|
||||||
hard_arms='.locators | ((.repo // "") != "" and (((.issue // "") | tostring) != "")) or (((.sha // "") | tostring) | test("^[0-9a-f]{40}$")) or ((.file // "") != "") or ((.path // "") != "")'
|
|
||||||
printf '%s' '{"locators":{"path":"BOARD.md"}}' | jq -e "$hard_arms" >/dev/null 2>&1 || fail_msg "Q16: positive control failed — the inline hard-locator predicate did not detect a path arm (instrument broken; re-sync it with digest.sh _has_hard_locator)"
|
|
||||||
# THE GUARD: the fixture must remain ADDRESS-FREE. If it ever gains a hard
|
|
||||||
# locator, Q2 passes the gate for the wrong reason and the reconciled
|
|
||||||
# exemption stops being exercised (#944 F1: an exemption nobody exercises is
|
|
||||||
# as unproven as a gate nobody has seen refuse).
|
|
||||||
if printf '%s' "$fixture_json" | jq -e "$hard_arms" >/dev/null 2>&1; then
|
|
||||||
fail_msg "Q16: Q2's $enum_id fixture has grown a hard-locator arm — restore an address-free fixture so the reconciled exemption stays load-bearing [$fixture_json]"
|
|
||||||
fi
|
|
||||||
true
|
true
|
||||||
) && ok
|
) && ok
|
||||||
|
|
||||||
|
|||||||
@@ -19,15 +19,6 @@
|
|||||||
# T10 concurrency: two concurrent enqueues get DISTINCT seqs (lock) (#908)
|
# T10 concurrency: two concurrent enqueues get DISTINCT seqs (lock) (#908)
|
||||||
# T12 consume records the last-consumed observed_hash per (kind,id) into the
|
# T12 consume records the last-consumed observed_hash per (kind,id) into the
|
||||||
# store-owned record (additive; monotonic last-seq wins; lazily created) (#932)
|
# store-owned record (additive; monotonic last-seq wins; lazily created) (#932)
|
||||||
# T13 #946 quarantine CLAMP: ordinary consume (store + ack wrapper) REFUSES to
|
|
||||||
# advance past a quarantined seq; the refusal names the seq + the force flag
|
|
||||||
# T14 #946 forced step-over: --force-past-quarantine advances LOUDLY, prunes the
|
|
||||||
# set, and NEVER fabricates a consumed-hash row for the quarantined entry
|
|
||||||
# T15 #946 quarantine-sync: full REPLACE semantics (sorted/deduped; empty input
|
|
||||||
# CLEARS — the clamp self-heals once the gate is fixed; invalid input refused)
|
|
||||||
# T16 #946 quarantine-audit: consumed-hashes rows provably false against the
|
|
||||||
# dead-letter ledger are reported (exit 1) and removed only under --repair;
|
|
||||||
# healed rows and the ledger itself are untouched
|
|
||||||
#
|
#
|
||||||
# Isolated: every test runs against a fresh WAKE_STATE_HOME temp dir.
|
# Isolated: every test runs against a fresh WAKE_STATE_HOME temp dir.
|
||||||
set -uo pipefail
|
set -uo pipefail
|
||||||
@@ -530,156 +521,6 @@ echo "== T12: #932 — consume records the last-consumed observed_hash per (kind
|
|||||||
"$STORE" cursors | grep -q 'consumed_seq=3' || fail_msg "T12: consumed_seq must be 3 after CONSUMED 3"
|
"$STORE" cursors | grep -q 'consumed_seq=3' || fail_msg "T12: consumed_seq must be 3 after CONSUMED 3"
|
||||||
) && ok
|
) && ok
|
||||||
|
|
||||||
echo "== T13: #946 — ordinary consume REFUSES to advance past a quarantined seq (store + ack paths) =="
|
|
||||||
(
|
|
||||||
WAKE_STATE_HOME="$(fresh_state t13)"
|
|
||||||
export WAKE_STATE_HOME
|
|
||||||
unset WAKE_AGENT
|
|
||||||
"$STORE" enqueue --class actionable --locators '{"kind":"repo","id":"a","observed_hash":"HA"}' >/dev/null
|
|
||||||
"$STORE" enqueue --class actionable --locators '{"kind":"repo","id":"b","observed_hash":"HB"}' >/dev/null
|
|
||||||
"$STORE" enqueue --class actionable --locators '{"kind":"repo","id":"c","observed_hash":"HC"}' >/dev/null
|
|
||||||
printf '2\n' | "$STORE" quarantine-sync || fail_msg "T13: quarantine-sync must accept a valid seq list"
|
|
||||||
# A quarantined seq was dead-lettered at render and NEVER delivered in any
|
|
||||||
# digest; the ordinary path must REFUSE to record it consumed (#946: a force
|
|
||||||
# flag the ordinary path can bypass is decoration).
|
|
||||||
if "$STORE" consume --upto 3 >/dev/null 2>&1; then
|
|
||||||
fail_msg "T13: ordinary consume --upto 3 must be REFUSED while seq 2 is quarantined"
|
|
||||||
fi
|
|
||||||
err="$("$STORE" consume --upto 3 2>&1 >/dev/null || true)"
|
|
||||||
echo "$err" | grep -q 'quarantined seq(s): 2' || fail_msg "T13: the refusal must NAME the quarantined seq [$err]"
|
|
||||||
echo "$err" | grep -q -- '--force-past-quarantine' || fail_msg "T13: the refusal must NAME the force flag [$err]"
|
|
||||||
cur="$("$STORE" cursors)"
|
|
||||||
echo "$cur" | grep -q 'consumed_seq=0' || fail_msg "T13: a refused consume must NOT advance the cursor [$cur]"
|
|
||||||
# BELOW the quarantined seq the ordinary path is unaffected.
|
|
||||||
"$STORE" consume --upto 1 >/dev/null 2>&1 || fail_msg "T13: consume --upto 1 (below the quarantined seq) must succeed"
|
|
||||||
# The ack wrapper propagates the refusal — no ordinary-path bypass exists.
|
|
||||||
if "$ACK" consumed --upto 3 --no-sync >/dev/null 2>&1; then
|
|
||||||
fail_msg "T13: ack.sh consumed --upto 3 must be REFUSED while seq 2 is quarantined (ordinary-path bypass)"
|
|
||||||
fi
|
|
||||||
cur="$("$STORE" cursors)"
|
|
||||||
echo "$cur" | grep -q 'consumed_seq=1' || fail_msg "T13: cursor must still be 1 after the refused ack [$cur]"
|
|
||||||
) && ok
|
|
||||||
|
|
||||||
echo "== T14: #946 — FORCED step-over is LOUD, prunes the set, and NEVER fabricates a consumed-hash row for the quarantined entry =="
|
|
||||||
(
|
|
||||||
WAKE_STATE_HOME="$(fresh_state t14)"
|
|
||||||
export WAKE_STATE_HOME
|
|
||||||
unset WAKE_AGENT
|
|
||||||
rec="$WAKE_STATE_HOME/default/consumed-hashes.jsonl"
|
|
||||||
qf="$WAKE_STATE_HOME/default/quarantined.set"
|
|
||||||
"$STORE" enqueue --class actionable --locators '{"kind":"repo","id":"a","observed_hash":"HA"}' >/dev/null
|
|
||||||
"$STORE" enqueue --class actionable --locators '{"kind":"repo","id":"b","observed_hash":"HB"}' >/dev/null
|
|
||||||
"$STORE" enqueue --class actionable --locators '{"kind":"repo","id":"c","observed_hash":"HC"}' >/dev/null
|
|
||||||
printf '2\n' | "$STORE" quarantine-sync || fail_msg "T14: quarantine-sync failed"
|
|
||||||
errf="$TMP_ROOT/t14.err"
|
|
||||||
out="$("$STORE" consume --upto 3 --force-past-quarantine 2>"$errf")"
|
|
||||||
rc=$?
|
|
||||||
[ "$rc" -eq 0 ] || fail_msg "T14: forced consume must succeed (rc=$rc) [$(cat "$errf")]"
|
|
||||||
[ "$out" = "3" ] || fail_msg "T14: forced consume must print the new cursor 3, got '$out'"
|
|
||||||
grep -q 'FORCED PAST QUARANTINE' "$errf" || fail_msg "T14: the forced path must be LOUD on stderr [$(cat "$errf")]"
|
|
||||||
grep -q 'seq 2' "$errf" || fail_msg "T14: the forced-path diagnostic must name the stepped-over seq 2 [$(cat "$errf")]"
|
|
||||||
"$STORE" cursors | grep -q 'consumed_seq=3' || fail_msg "T14: forced consume must advance the cursor to 3"
|
|
||||||
# NO FALSE WITNESS: the quarantined entry (repo/b) was NEVER delivered, so no
|
|
||||||
# consumed-hash row may exist for it — even on the forced path (the reconciler
|
|
||||||
# re-enumerating it once is safe-but-noisy; a false witness silences it
|
|
||||||
# forever). Its delivered siblings' rows must exist.
|
|
||||||
jq_any "$rec" '.kind=="repo" and .id=="a" and .observed_hash=="HA"' || fail_msg "T14: the delivered sibling repo/a must have its consumed-hash row"
|
|
||||||
jq_any "$rec" '.kind=="repo" and .id=="c" and .observed_hash=="HC"' || fail_msg "T14: the delivered sibling repo/c must have its consumed-hash row"
|
|
||||||
jq_any "$rec" '.kind=="repo" and .id=="b"' && fail_msg "T14: the quarantined entry repo/b must have NO consumed-hash row (a row would witness a delivery that never happened)"
|
|
||||||
# The stepped-over seq is PRUNED from the set (it is consumed now; a stale
|
|
||||||
# entry would re-refuse forever).
|
|
||||||
grep -qxF '2' "$qf" 2>/dev/null && fail_msg "T14: seq 2 must be PRUNED from quarantined.set after the forced step-over"
|
|
||||||
# The ack wrapper's force flag passes through, stays LOUD on stderr, and
|
|
||||||
# still reports a CLEAN cursor line on stdout.
|
|
||||||
"$STORE" enqueue --class actionable --locators '{"kind":"repo","id":"d","observed_hash":"HD"}' >/dev/null
|
|
||||||
"$STORE" enqueue --class actionable --locators '{"kind":"repo","id":"e","observed_hash":"HE"}' >/dev/null
|
|
||||||
printf '5\n' | "$STORE" quarantine-sync || fail_msg "T14: quarantine-sync (2nd) failed"
|
|
||||||
errf2="$TMP_ROOT/t14b.err"
|
|
||||||
out2="$("$ACK" consumed --upto 5 --no-sync --force-past-quarantine 2>"$errf2")"
|
|
||||||
rc2=$?
|
|
||||||
[ "$rc2" -eq 0 ] || fail_msg "T14: forced ack must succeed (rc=$rc2) [$(cat "$errf2")]"
|
|
||||||
echo "$out2" | grep -q '^CONSUMED 5$' || fail_msg "T14: forced ack must report a CLEAN cursor line 'CONSUMED 5', got '$out2'"
|
|
||||||
grep -q 'FORCED PAST QUARANTINE' "$errf2" || fail_msg "T14: the forced-path loudness must survive the ack wrapper (stderr) [$(cat "$errf2")]"
|
|
||||||
jq_any "$rec" '.kind=="repo" and .id=="e"' && fail_msg "T14: the quarantined repo/e must have NO consumed-hash row via the forced ack path either"
|
|
||||||
true
|
|
||||||
) && ok
|
|
||||||
|
|
||||||
echo "== T15: #946 — quarantine-sync is a full REPLACE (sorted, deduped; empty input CLEARS; invalid input REFUSED) =="
|
|
||||||
(
|
|
||||||
WAKE_STATE_HOME="$(fresh_state t15)"
|
|
||||||
export WAKE_STATE_HOME
|
|
||||||
unset WAKE_AGENT
|
|
||||||
qf="$WAKE_STATE_HOME/default/quarantined.set"
|
|
||||||
printf '3\n1\n3\n' | "$STORE" quarantine-sync || fail_msg "T15: sync of a valid list must succeed"
|
|
||||||
[ "$(cat "$qf" 2>/dev/null)" = "$(printf '1\n3')" ] || fail_msg "T15: set must be sorted+deduped {1,3}, got [$(cat "$qf" 2>/dev/null)]"
|
|
||||||
printf '2\n' | "$STORE" quarantine-sync || fail_msg "T15: re-sync must succeed"
|
|
||||||
[ "$(cat "$qf" 2>/dev/null)" = "2" ] || fail_msg "T15: sync must REPLACE, not merge — expected {2}, got [$(cat "$qf" 2>/dev/null)]"
|
|
||||||
# Empty input CLEARS the set: the set is re-DERIVED per authoritative render,
|
|
||||||
# never accumulated, so a fixed locator gate self-heals the clamp.
|
|
||||||
: | "$STORE" quarantine-sync || fail_msg "T15: empty sync (clear) must succeed"
|
|
||||||
[ ! -s "$qf" ] || fail_msg "T15: empty sync must CLEAR the set, got [$(cat "$qf")]"
|
|
||||||
# Invalid input is refused loudly and must not corrupt the set.
|
|
||||||
printf '1\n' | "$STORE" quarantine-sync || fail_msg "T15: re-seed failed"
|
|
||||||
if printf 'abc\n' | "$STORE" quarantine-sync >/dev/null 2>&1; then
|
|
||||||
fail_msg "T15: a non-integer line must be REFUSED"
|
|
||||||
fi
|
|
||||||
[ "$(cat "$qf" 2>/dev/null)" = "1" ] || fail_msg "T15: a refused sync must leave the set untouched, got [$(cat "$qf" 2>/dev/null)]"
|
|
||||||
# End-to-end: a cleared set stops clamping (the #944 recovery case).
|
|
||||||
"$STORE" enqueue --class actionable --locators '{"kind":"repo","id":"x","observed_hash":"H1"}' >/dev/null
|
|
||||||
if "$STORE" consume --upto 1 >/dev/null 2>&1; then
|
|
||||||
fail_msg "T15: consume --upto 1 must be refused while seq 1 is quarantined"
|
|
||||||
fi
|
|
||||||
: | "$STORE" quarantine-sync || fail_msg "T15: clear failed"
|
|
||||||
"$STORE" consume --upto 1 >/dev/null 2>&1 || fail_msg "T15: after the set is cleared (gate fixed), the ordinary consume must succeed — the clamp must self-heal"
|
|
||||||
) && ok
|
|
||||||
|
|
||||||
echo "== T16: #946 — quarantine-audit: a consumed-hash row matching a dead-letter entry on (kind,id,seq,hash) at/below consumed_seq is PROVABLY FALSE; --repair removes ONLY those rows =="
|
|
||||||
(
|
|
||||||
WAKE_STATE_HOME="$(fresh_state t16)"
|
|
||||||
export WAKE_STATE_HOME
|
|
||||||
unset WAKE_AGENT
|
|
||||||
STATE_DIR="$WAKE_STATE_HOME/default"
|
|
||||||
rec="$STATE_DIR/consumed-hashes.jsonl"
|
|
||||||
dl="$STATE_DIR/dead-letter.jsonl"
|
|
||||||
# Rebuild the historical false-witness state via the REAL flow the defect
|
|
||||||
# used: X@1 was quarantined (dead-lettered) yet consumed under pre-#946 code;
|
|
||||||
# Y@2 is clean; Z re-emitted (dead-lettered at seq 3, healed by seq 4 winning
|
|
||||||
# the per-key max_by merge — Finding A's live-canary shape).
|
|
||||||
"$STORE" enqueue --class actionable --locators '{"kind":"repo","id":"X","observed_hash":"HX"}' >/dev/null
|
|
||||||
"$STORE" enqueue --class actionable --locators '{"kind":"repo","id":"Y","observed_hash":"HY"}' >/dev/null
|
|
||||||
"$STORE" enqueue --class actionable --locators '{"kind":"repo","id":"Z","observed_hash":"HZ-OLD"}' >/dev/null
|
|
||||||
"$STORE" enqueue --class actionable --locators '{"kind":"repo","id":"Z","observed_hash":"HZ-NEW"}' >/dev/null
|
|
||||||
{
|
|
||||||
printf '%s\n' '{"observed_seq":1,"locators":{"kind":"repo","id":"X","observed_hash":"HX"},"class":"actionable","emit_ts":1,"hmac":""}'
|
|
||||||
printf '%s\n' '{"observed_seq":3,"locators":{"kind":"repo","id":"Z","observed_hash":"HZ-OLD"},"class":"actionable","emit_ts":1,"hmac":""}'
|
|
||||||
} >"$dl"
|
|
||||||
# Pre-#946-shaped consume: NO quarantined.set exists, so this consume writes
|
|
||||||
# the false witness for X@1 exactly as the live defect did.
|
|
||||||
"$STORE" consume --upto 4 >/dev/null 2>&1 || fail_msg "T16: baseline consume failed"
|
|
||||||
jq_any "$rec" '.id=="X" and .observed_seq==1' || fail_msg "T16: fixture broken — the false X@1 row was not written"
|
|
||||||
# REPORT: exactly the X row is provably false; non-zero exit signals findings.
|
|
||||||
rep="$TMP_ROOT/t16.rep"
|
|
||||||
if "$STORE" quarantine-audit >"$rep" 2>&1; then
|
|
||||||
fail_msg "T16: report-mode audit must exit NON-ZERO when false rows exist"
|
|
||||||
fi
|
|
||||||
grep -q 'FALSE WITNESS' "$rep" || fail_msg "T16: the audit must name the false row loudly [$(cat "$rep")]"
|
|
||||||
grep -q '"id":"X"' "$rep" || fail_msg "T16: the audit must identify the false row (repo/X@1) [$(cat "$rep")]"
|
|
||||||
grep -q '"id":"Y"' "$rep" && fail_msg "T16: the clean row repo/Y must NOT be flagged"
|
|
||||||
grep -q '"id":"Z"' "$rep" && fail_msg "T16: the HEALED row repo/Z@4 must NOT be flagged (its dead-letter evidence is seq 3 with a different hash)"
|
|
||||||
# Report mode modifies nothing.
|
|
||||||
jq_any "$rec" '.id=="X"' || fail_msg "T16: report mode must not modify the record"
|
|
||||||
# REPAIR: exactly the false row is removed; the dead-letter LEDGER is history
|
|
||||||
# and must never be modified.
|
|
||||||
"$STORE" quarantine-audit --repair >"$TMP_ROOT/t16.fix" 2>&1 || fail_msg "T16: --repair must succeed [$(cat "$TMP_ROOT/t16.fix")]"
|
|
||||||
jq_any "$rec" '.id=="X"' && fail_msg "T16: --repair must REMOVE the provably-false X row"
|
|
||||||
jq_any "$rec" '.id=="Y" and .observed_hash=="HY"' || fail_msg "T16: --repair must keep the clean Y row"
|
|
||||||
jq_any "$rec" '.id=="Z" and .observed_hash=="HZ-NEW" and .observed_seq==4' || fail_msg "T16: --repair must keep the healed Z@4 row"
|
|
||||||
[ "$(grep -c . "$dl")" = "2" ] || fail_msg "T16: the dead-letter LEDGER must be untouched by --repair"
|
|
||||||
# Clean re-audit: OK, exit 0.
|
|
||||||
"$STORE" quarantine-audit >"$TMP_ROOT/t16.ok" 2>&1 || fail_msg "T16: a clean audit must exit 0 [$(cat "$TMP_ROOT/t16.ok")]"
|
|
||||||
grep -qi 'OK' "$TMP_ROOT/t16.ok" || fail_msg "T16: a clean audit must say OK [$(cat "$TMP_ROOT/t16.ok")]"
|
|
||||||
) && ok
|
|
||||||
|
|
||||||
echo
|
echo
|
||||||
if [ -s "$FAILFILE" ]; then
|
if [ -s "$FAILFILE" ]; then
|
||||||
echo "wake store/ack harness: FAILED ($(grep -c . "$FAILFILE") assertion(s))" >&2
|
echo "wake store/ack harness: FAILED ($(grep -c . "$FAILFILE") assertion(s))" >&2
|
||||||
|
|||||||
Reference in New Issue
Block a user