Compare commits

..
Author SHA1 Message Date
Jason Woltjeandmos-dt-0 f63340802c merge: origin/main into fix/1019-queue-guard-stdin (union on test:framework-shell)
Both sides edited the single `test:framework-shell` line additively and did not
overlap: main prepended the test-enumeration guard pair
(check-test-enumeration.sh, test-check-test-enumeration.sh); this branch inserted
test-ci-queue-wait-parse.sh after test-ci-queue-wait-branch-absent.sh.

Resolved as the union — main's line with this branch's one insertion restored.
Main's new guard makes that insertion mandatory rather than cosmetic: the test
file ships in this branch, so leaving it unenumerated would fail the guard.

Co-authored-by: mos-dt-0 <[email protected]>
2026-07-31 11:43:24 -05:00
mos-dt-0andMos 06e0d40352 feat(quality): CI test-membership guard — enumeration can no longer silently under-run the disk (#1017) (#1018)
Co-authored-by: mos-dt-0 <[email protected]>
2026-07-31 14:08:41 +00:00
Mos 166ee8c90f fix(wake): close fd 9 in the detector's sleep child so a dead detector's lock dies with it (#993) 2026-07-31 13:48:33 +00:00
Jason Woltje 505b6f799c fix(git): ci-queue-wait.sh — parse the payload it was handed, not stdin (#1019)
Both python sites piped a status payload into `python3 - <<'PY'`. The heredoc binds
stdin to the program text, so `json.load(sys.stdin)` saw EOF, the bare `except` fired,
and the parser returned "unknown" for every input — success, pending and failure alike.
"unknown" then reaches a silent `exit 0` arm.

Consequence: the gate-6 queue guard has never made a determination. It exits 0 on every
invocation, on both the gitea and github paths (one shared parser). The pending wait
loop, --require-status and the 124 timeout were all unreachable code. What it still
validated was connectivity — an unresolved token, head sha, or platform could exit 1.

Fix is the one already shipped in the sibling: capture the payload with `payload=$(cat)`
before invoking python, pass it by environment. pr-ci-wait.sh:38 has carried a comment
describing this exact bug — "yielding EOF and returning unknown every time" — along with
the remedy. It was never backported to the sibling the constitution makes mandatory
before every push and merge.

Adds test-ci-queue-wait-parse.sh, enumerated in test:framework-shell. Every assertion is
on the RETURNED STATE STRING; a suite asserting only rc=0 passes against the broken build,
which is how this survived. Verified by mutation: against the pre-fix wrapper the suite
fails 10 of 14, and the four that pass are the four for which passing is correct (the
undecodable-input control, the unknown-vocabulary case, and both needle halves).

The needle scans with heredoc semantics rather than matching text. `json.load(sys.stdin)`
is correct under `python3 -c`, where the program comes from argv and stdin really is the
payload — ci-queue-wait.sh uses that form legitimately in gitea_get_branch_head_sha. A
flat grep flags that innocent site; the scanner names only the two defective ones.

Out of scope, deliberately, and recorded on #1019: token-in-argv (ps-visible) and the
hardcoded BRANCH="main". Bundling them would make a safety-critical parse fix harder to
review.

Refs #1019
2026-07-31 08:42:48 -05:00
mos-dt-0andMos 826a8b3b26 fix(git): pr-review.sh — surface the provider's stated reason, drop the hardcoded #865 attribution (#1006)
Co-authored-by: mos-dt-0 <[email protected]>
2026-07-31 10:52:58 +00:00
mos-dt-0andMos a4280b9c98 fix(wake): #984 fatal source guard + #985 absorb re-scan — #973 follow-up batch (#1001)
Co-authored-by: mos-dt-0 <[email protected]>
2026-07-31 10:16:22 +00:00
Mos 4fb44f6345 fix(wake): three-valued grep verdicts — has_match/count_lines across all ten suites (closes #973) (#983) 2026-07-31 08:39:04 +00:00
23 changed files with 895 additions and 81 deletions
+5
View File
@@ -41,6 +41,11 @@ steps:
# (Constitution + dispatcher + each RUNTIME.md slice). See DESIGN §7 / R9. # (Constitution + dispatcher + each RUNTIME.md slice). See DESIGN §7 / R9.
- bash packages/mosaic/framework/tools/quality/scripts/check-resident-budget.sh --self-test - bash packages/mosaic/framework/tools/quality/scripts/check-resident-budget.sh --self-test
- bash packages/mosaic/framework/tools/quality/scripts/check-resident-budget.sh - bash packages/mosaic/framework/tools/quality/scripts/check-resident-budget.sh
# Test-membership guard (#1017): also first link of test:framework-shell.
# Invoked from BOTH surfaces it audits (F2, PR #1018) — the guard is link
# [0] of the pnpm chain, so severing that chain would silence it together
# with everything it guards; this direct line keeps one instrument running.
- bash packages/mosaic/framework/tools/quality/scripts/check-test-enumeration.sh
# Blocking gate (#791): a framework upgrade must never write or delete an # Blocking gate (#791): a framework upgrade must never write or delete an
# operator-owned path. The HARD GATE proves an unanticipated operator sentinel # operator-owned path. The HARD GATE proves an unanticipated operator sentinel
@@ -34,12 +34,18 @@ EOF
# get_remote_host and get_gitea_token are provided by detect-platform.sh # get_remote_host and get_gitea_token are provided by detect-platform.sh
get_state_from_status_json() { get_state_from_status_json() {
python3 - <<'PY' # Capture piped JSON BEFORE invoking `python3 - <<PY`. The heredoc binds
# stdin to the Python program text — so json.load(sys.stdin) inside would
# try to re-read stdin after `-` already consumed it for the program,
# yielding EOF and returning "unknown" every time. Pass payload via env.
local payload
payload=$(cat)
CI_QUEUE_STATUS_JSON="$payload" python3 - <<'PY'
import json import json
import sys import os
try: try:
payload = json.load(sys.stdin) payload = json.loads(os.environ.get("CI_QUEUE_STATUS_JSON", ""))
except Exception: except Exception:
print("unknown") print("unknown")
raise SystemExit(0) raise SystemExit(0)
@@ -83,12 +89,15 @@ PY
} }
print_pending_contexts() { print_pending_contexts() {
python3 - <<'PY' # Same stdin hazard as get_state_from_status_json above — pass payload via env.
local payload
payload=$(cat)
CI_QUEUE_STATUS_JSON="$payload" python3 - <<'PY'
import json import json
import sys import os
try: try:
payload = json.load(sys.stdin) payload = json.loads(os.environ.get("CI_QUEUE_STATUS_JSON", ""))
except Exception: except Exception:
print("[ci-queue-wait] unable to decode status payload") print("[ci-queue-wait] unable to decode status payload")
raise SystemExit(0) raise SystemExit(0)
@@ -109,6 +109,55 @@ else
detect_platform >/dev/null detect_platform >/dev/null
fi fi
# Render the provider's own explanation for a failed request, for appending to
# an error message (#1004). Every HTTP arm in this file already has the response
# body on disk; without this it was discarded unread at exactly the moment the
# caller needed it, which pushes an operator toward re-issuing the request by
# hand to find out what the server said. Gitea returns {"message": "..."} on a
# refusal; anything unparseable falls back to a truncated raw first line so a
# proxy's HTML error page still says something. Prints "" when there is nothing
# to add, so callers can interpolate unconditionally.
#
# Args: $1 = path to the response body file.
gitea_error_detail() {
local body_file="$1"
[[ -s "$body_file" ]] || return 0
python3 - "$body_file" <<'PY' 2>/dev/null || true
import json
import sys
LIMIT = 300
try:
with open(sys.argv[1], encoding="utf-8", errors="replace") as response:
raw = response.read().strip()
except OSError:
raise SystemExit(0)
if not raw:
raise SystemExit(0)
detail = ""
try:
parsed = json.loads(raw)
if isinstance(parsed, dict):
for key in ("message", "error", "errors"):
value = parsed.get(key)
if isinstance(value, str) and value.strip():
detail = value.strip()
break
if isinstance(value, list) and value:
detail = "; ".join(str(item) for item in value).strip()
break
except ValueError:
pass
if not detail:
detail = raw.splitlines()[0].strip()
if not detail:
raise SystemExit(0)
if len(detail) > LIMIT:
detail = detail[:LIMIT] + "..."
print(f" — provider said: {detail}")
PY
}
# Post a comment to a Gitea PR (PR comments ARE issue comments) via the # Post a comment to a Gitea PR (PR comments ARE issue comments) via the
# supported REST API and verify it against a PROVIDER-RETURNED created id. The # supported REST API and verify it against a PROVIDER-RETURNED created id. The
# write is a direct POST that returns the created comment object, so we learn # write is a direct POST that returns the created comment object, so we learn
@@ -150,7 +199,7 @@ print(json.dumps({"body": os.environ["COMMENT_BODY"]}))
return 1 return 1
fi fi
if [[ "$write_status" != "201" ]]; then if [[ "$write_status" != "201" ]]; then
echo "Error: Gitea comment write failed with HTTP $write_status" >&2 echo "Error: Gitea comment write failed with HTTP $write_status$(gitea_error_detail "$write_file")" >&2
return 1 return 1
fi fi
@@ -179,7 +228,7 @@ PY
return 1 return 1
fi fi
if [[ "$readback_status" != "200" ]]; then if [[ "$readback_status" != "200" ]]; then
echo "Error: Gitea comment read-back failed with HTTP $readback_status" >&2 echo "Error: Gitea comment read-back failed with HTTP $readback_status$(gitea_error_detail "$readback_file")" >&2
return 1 return 1
fi fi
@@ -370,7 +419,7 @@ gitea_authenticated_login() {
return 1 return 1
fi fi
if [[ "$status" != "200" ]]; then if [[ "$status" != "200" ]]; then
echo "Error: Gitea authenticated-identity read failed with HTTP $status" >&2 echo "Error: Gitea authenticated-identity read failed with HTTP $status$(gitea_error_detail "$response_file")" >&2
return 1 return 1
fi fi
@@ -407,7 +456,7 @@ gitea_read_pr_head_into() {
return 1 return 1
fi fi
if [[ "$status" != "200" ]]; then if [[ "$status" != "200" ]]; then
echo "Error: Gitea PR head read failed with HTTP $status" >&2 echo "Error: Gitea PR head read failed with HTTP $status$(gitea_error_detail "$pr_file")" >&2
return 1 return 1
fi fi
python3 - "$pr_file" <<'PY' python3 - "$pr_file" <<'PY'
@@ -497,7 +546,7 @@ print(json.dumps({
fi fi
# Gitea returns 200 (occasionally 201) with the created review object. # Gitea returns 200 (occasionally 201) with the created review object.
if [[ "$write_status" != "200" && "$write_status" != "201" ]]; then if [[ "$write_status" != "200" && "$write_status" != "201" ]]; then
echo "Error: Gitea review submit failed with HTTP $write_status (#865: no durable review created)" >&2 echo "Error: Gitea review submit failed with HTTP $write_status$(gitea_error_detail "$write_file")" >&2
return 1 return 1
fi fi
@@ -526,7 +575,7 @@ PY
return 1 return 1
fi fi
if [[ "$readback_status" != "200" ]]; then if [[ "$readback_status" != "200" ]]; then
echo "Error: Gitea review read-back failed with HTTP $readback_status" >&2 echo "Error: Gitea review read-back failed with HTTP $readback_status$(gitea_error_detail "$readback_file")" >&2
return 1 return 1
fi fi
@@ -0,0 +1,205 @@
#!/usr/bin/env bash
# Regression suite for #1019: ci-queue-wait.sh's status parser must return a
# state DERIVED FROM ITS INPUT.
#
# The defect this pins: both python sites piped a payload into `python3 - <<'PY'`.
# The heredoc binds stdin to the Python program text, so json.load(sys.stdin) saw
# EOF, the bare `except` fired, and the function returned "unknown" for every input
# — success, pending and failure alike. "unknown" then reaches an `exit 0` arm, so
# the gate-6 queue guard passed unconditionally on both the gitea and github paths.
#
# Every assertion below is on the RETURNED STATE STRING. A test that asserted only
# `rc=0` would have passed against the broken build, which is why this bug survived.
#
# The functions are extracted from the shipped wrapper rather than reimplemented, so
# this suite measures the code that actually runs.
set -uo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
# Overridable so a mutation test can point the suite at a deliberately-broken copy
# without touching the tracked file. The earlier version of this suite required
# `git stash` + `git checkout` to do that, which left a repo-global stash entry
# that any sibling worktree could have popped onto an unrelated branch.
WRAPPER="${CI_QUEUE_WAIT_WRAPPER:-$SCRIPT_DIR/ci-queue-wait.sh}"
PASS=0
FAIL=0
pass() { printf ' PASS %s\n' "$1"; PASS=$((PASS + 1)); }
fail() { printf ' FAIL %s\n' "$1"; FAIL=$((FAIL + 1)); }
if [[ ! -f "$WRAPPER" ]]; then
printf 'FATAL: wrapper not found at %s\n' "$WRAPPER" >&2
exit 1
fi
TMPDIR_T="$(mktemp -d)"
trap 'rm -rf "$TMPDIR_T"' EXIT
extract_fn() {
# $1 = function name, $2 = source file, $3 = destination
sed -n "/^$1()/,/^}/p" "$2" > "$3"
[[ -s "$3" ]] || { printf 'FATAL: could not extract %s from %s\n' "$1" "$2" >&2; exit 1; }
}
extract_fn get_state_from_status_json "$WRAPPER" "$TMPDIR_T/state.sh"
extract_fn print_pending_contexts "$WRAPPER" "$TMPDIR_T/contexts.sh"
state_of() {
# shellcheck disable=SC1091
( source "$TMPDIR_T/state.sh"; printf '%s' "$1" | get_state_from_status_json )
}
expect_state() {
local label="$1" payload="$2" want="$3" got
got="$(state_of "$payload")"
if [[ "$got" == "$want" ]]; then
pass "$label -> $want"
else
fail "$label -> got '$got', want '$want'"
fi
}
echo "=== parser returns a state derived from its input (#1019) ==="
expect_state "success payload" \
'{"state":"success","statuses":[{"status":"success","context":"ci/build"}]}' \
'terminal-success'
expect_state "pending payload" \
'{"state":"pending","statuses":[{"status":"pending","context":"ci/build"}]}' \
'pending'
expect_state "failure payload" \
'{"state":"failure","statuses":[{"status":"failure","context":"ci/build"}]}' \
'terminal-failure'
expect_state "mixed success+pending is pending" \
'{"state":"pending","statuses":[{"status":"success"},{"status":"pending"}]}' \
'pending'
expect_state "running counts as pending" \
'{"state":"pending","statuses":[{"status":"running"}]}' \
'pending'
expect_state "error counts as failure" \
'{"state":"failure","statuses":[{"status":"error"}]}' \
'terminal-failure'
expect_state "empty status set is no-status" \
'{"state":"","statuses":[]}' \
'no-status'
# Control. This is the ONE input for which "unknown" is correct. Without it, a
# regression that hardcoded "unknown" again would still fail the cases above but
# the suite would give no signal that "unknown" remains reachable when it should be.
expect_state "undecodable payload stays unknown" \
'not json at all' \
'unknown'
expect_state "unrecognised status vocabulary is unknown" \
'{"state":"weird","statuses":[{"status":"weird"}]}' \
'unknown'
echo "=== pending contexts are reported to the operator ==="
contexts_of() {
# shellcheck disable=SC1091
( source "$TMPDIR_T/contexts.sh"; printf '%s' "$1" | print_pending_contexts )
}
out="$(contexts_of '{"statuses":[{"status":"pending","context":"ci/alpha"},{"status":"pending","context":"ci/beta"}]}')"
if grep -q 'ci/alpha' <<<"$out" && grep -q 'ci/beta' <<<"$out"; then
pass "both pending contexts emitted"
else
fail "pending contexts not emitted; got: $out"
fi
out="$(contexts_of '{"statuses":[{"status":"success","context":"ci/alpha"}]}')"
# Assert the POSITIVE message, not merely the absence of the context name. Absence
# alone is satisfied by total silence — and the pre-fix build was silent, so an
# absence-only assertion passed against the very defect this suite exists to catch.
if grep -q 'ci/alpha' <<<"$out"; then
fail "a non-pending context was emitted; got: $out"
elif grep -q 'no pending contexts' <<<"$out"; then
pass "non-pending context suppressed, and reported as 'no pending contexts'"
else
fail "expected an explicit 'no pending contexts' report; got: $out"
fi
echo "=== needle: the broken construct is caught, not merely absent today ==="
# Rebuild the pre-fix form and assert this suite would have failed against it.
# Without this, the suite proves the current file is correct but not that it can
# detect the defect returning.
BROKEN="$TMPDIR_T/broken.sh"
cat > "$BROKEN" <<'BROKEN_EOF'
get_state_from_status_json() {
python3 - <<'PY'
import json
import sys
try:
payload = json.load(sys.stdin)
except Exception:
print("unknown")
raise SystemExit(0)
print("terminal-success" if (payload.get("state") or "") == "success" else "pending")
PY
}
BROKEN_EOF
broken_got="$( ( source "$BROKEN"; printf '%s' '{"state":"success","statuses":[]}' | get_state_from_status_json ) )"
if [[ "$broken_got" == "unknown" ]]; then
pass "[NEEDLE ] pre-fix construct reproduces the defect (returns 'unknown' for a success payload)"
else
fail "[NEEDLE ] pre-fix construct did NOT reproduce the defect; got '$broken_got' — the needle no longer pins anything"
fi
# And assert the shipped wrapper does not contain that construct.
#
# The check must have HEREDOC SEMANTICS, not merely match the text. `json.load(sys.stdin)`
# is perfectly correct under `python3 -c '...'` — there the program comes from argv, so
# stdin really is the payload, and ci-queue-wait.sh uses that form legitimately in
# gitea_get_branch_head_sha. Only `python3 - <<DELIM` is defective, because `-` has
# already bound stdin to the program text.
#
# A flat `grep json\.load\(sys\.stdin\)` therefore fails on correct code. It did: the
# first version of this needle flagged the innocent `python3 -c` site. Same shape as
# #1018 F1 (a regex over raw text has no syntax semantics) reproduced inside the needle
# written to pin a different instance of it.
heredoc_stdin_sites() {
awk '
/python3[[:space:]]+-[[:space:]]*<</ {
d = $0
sub(/.*<<[[:space:]]*/, "", d)
gsub(/['"'"'"]/, "", d)
delim = d; inhd = 1; next
}
inhd && $0 == delim { inhd = 0; next }
inhd {
line = $0
sub(/[[:space:]]*#.*$/, "", line)
if (line ~ /sys\.stdin/) printf "%d: %s\n", FNR, $0
}
' "$1"
}
# Positive control FIRST. An empty result from a broken scanner looks exactly like a
# clean file, so the scanner is proven to fire before its silence is trusted.
if [[ -n "$(heredoc_stdin_sites "$BROKEN")" ]]; then
pass "[NEEDLE ] scanner detects a stdin read inside a python3-heredoc"
else
fail "[NEEDLE ] scanner did NOT fire on the known-broken form — its silence proves nothing"
fi
sites="$(heredoc_stdin_sites "$WRAPPER")"
if [[ -n "$sites" ]]; then
fail "[NEEDLE ] wrapper reads stdin inside a python3-heredoc at: $sites"
else
pass "[NEEDLE ] wrapper has no stdin read inside any python3-heredoc"
fi
printf '\nci-queue-wait parse: %d passed, %d failed\n' "$PASS" "$FAIL"
[[ "$FAIL" -eq 0 ]] || exit 1
@@ -58,6 +58,9 @@ CREDENTIALS_FILE="$WORK_DIR/credentials.json"
# A dedicated scratch dir the wrapper is pointed at via TMPDIR, so the leak # A dedicated scratch dir the wrapper is pointed at via TMPDIR, so the leak
# check can assert every POST/GET body + metadata temp file is cleaned up. # check can assert every POST/GET body + metadata temp file is cleaned up.
TMP_SCRATCH="$WORK_DIR/scratch" TMP_SCRATCH="$WORK_DIR/scratch"
# Sandboxed HOME so nothing under the real $HOME (notably the per-slot Gitea token
# store at ~/.config/mosaic/secrets/gitea-tokens/) is reachable from the wrapper.
HOME_DIR="$WORK_DIR/home"
cleanup() { cleanup() {
rm -rf "$WORK_DIR" rm -rf "$WORK_DIR"
@@ -78,9 +81,18 @@ OVERRIDE_TOKEN="override-token-placeholder"
CROSS_HOST_LOGIN="foreign-host-reviewer" CROSS_HOST_LOGIN="foreign-host-reviewer"
CROSS_HOST_TOKEN="cross-host-token-placeholder" CROSS_HOST_TOKEN="cross-host-token-placeholder"
mkdir -p "$REPO_DIR" "$BIN_DIR" "$XDG_DIR" "$STATE_DIR" "$TMP_SCRATCH" mkdir -p "$REPO_DIR" "$BIN_DIR" "$XDG_DIR" "$STATE_DIR" "$TMP_SCRATCH" "$HOME_DIR"
git -C "$REPO_DIR" init -q git -C "$REPO_DIR" init -q
git -C "$REPO_DIR" remote add origin https://git.mosaicstack.dev/mosaicstack/stack.git git -C "$REPO_DIR" remote add origin https://git.mosaicstack.dev/mosaicstack/stack.git
# HERMETICITY: get_gitea_token() step 0 resolves a per-agent identity from
# `git config --get mosaic.gitIdentity`, which on a provisioned agent seat is set
# GLOBALLY and therefore leaks into this fresh repo. It then reads a REAL per-slot
# token from $HOME and returns it WITHOUT ever consulting MOSAIC_CREDENTIALS_FILE,
# so the fixture credentials below are silently ignored and the suite runs against
# production credentials. An empty repo-local value shadows the global one and reads
# back as empty at rc=0, restoring the shared-credential path this suite intends to
# exercise. Paired with the sandboxed HOME in run_review().
git -C "$REPO_DIR" config mosaic.gitIdentity ""
# tea config: the override login carries its own token here. The default login # tea config: the override login carries its own token here. The default login
# name ("mosaicstack") is deliberately absent, so the no-override default path # name ("mosaicstack") is deliberately absent, so the no-override default path
@@ -266,6 +278,24 @@ submitted = json.loads(os.environ["PR_REVIEW_PAYLOAD"])
with open(state_path, encoding="utf-8") as handle: with open(state_path, encoding="utf-8") as handle:
reviews = json.load(handle) reviews = json.load(handle)
# review-refused-422 (#1004): the server REFUSES the submit outright with a
# definite, correct, machine-readable reason in the body — the shape Gitea
# returns when the acting credential authored the PR. Nothing is created. The
# wrapper must surface what the server said and must NOT relabel this as the
# #865 silent-no-op defect class, which is precisely what it is not.
if mode == "review-refused-422":
print("422")
print(json.dumps({"message": "Cannot approve your own pull request"}))
raise SystemExit(0)
# review-refused-html (#1004): a non-JSON error body, as a fronting proxy or
# gateway emits. The detail extraction must degrade to the first raw line rather
# than silently dropping the only explanation available.
if mode == "review-refused-html":
print("502")
print("<html><head><title>502 Bad Gateway</title></head>\n<body>nginx</body></html>")
raise SystemExit(0)
# no-op-concurrent-review: the wrapper's own submit is SUPPRESSED (200, no # no-op-concurrent-review: the wrapper's own submit is SUPPRESSED (200, no
# created object) even though a concurrent same-identity, same-state review at # created object) even though a concurrent same-identity, same-state review at
# the same head already exists. Nothing is persisted; no created id to verify. # the same head already exists. Nothing is persisted; no created id to verify.
@@ -517,6 +547,8 @@ run_review() {
cd "$REPO_DIR" cd "$REPO_DIR"
PATH="$BIN_DIR:$PATH" \ PATH="$BIN_DIR:$PATH" \
TMPDIR="$TMP_SCRATCH" \ TMPDIR="$TMP_SCRATCH" \
HOME="$HOME_DIR" \
MOSAIC_GIT_IDENTITY="" \
XDG_CONFIG_HOME="$XDG_DIR" \ XDG_CONFIG_HOME="$XDG_DIR" \
MOSAIC_CREDENTIALS_FILE="$CREDENTIALS_FILE" \ MOSAIC_CREDENTIALS_FILE="$CREDENTIALS_FILE" \
PR_REVIEW_TEA_LOG="$TEA_LOG" \ PR_REVIEW_TEA_LOG="$TEA_LOG" \
@@ -940,4 +972,44 @@ if grep -q 'Approved and verified' "$OUTPUT_FILE"; then
fi fi
assert_no_temp_leak "review-body-null" assert_no_temp_leak "review-body-null"
# Case 19 (#1004): an outright server REFUSAL must report the provider's own
# reason and must NOT be relabelled as the #865 silent-no-op defect class. The
# old arm hardcoded "(#865: no durable review created)" for EVERY non-2xx, so a
# 422/403/404 — all of them definite, correct refusals the server explained in
# the discarded body — arrived at the caller wearing the name of the one defect
# they are not. That misdirection is what makes an operator re-issue the request
# by hand against the live object to find out what actually happened.
if run_review review-refused-422 approve; then
echo "FAIL: approve reported success when the server refused the submit" >&2
cat "$OUTPUT_FILE" >&2
exit 1
fi
grep -q 'HTTP 422' "$OUTPUT_FILE"
if ! grep -q 'Cannot approve your own pull request' "$OUTPUT_FILE"; then
echo "FAIL: the provider's stated reason was discarded (#1004)" >&2
cat "$OUTPUT_FILE" >&2
exit 1
fi
if grep -q '#865: no durable review created' "$OUTPUT_FILE"; then
echo "FAIL: a server refusal was misattributed to the #865 defect class (#1004)" >&2
cat "$OUTPUT_FILE" >&2
exit 1
fi
assert_no_temp_leak "review-refused-422"
# Case 20 (#1004): a non-JSON error body (a fronting proxy's HTML page) must
# still yield something the caller can act on, rather than a bare status code.
if run_review review-refused-html approve; then
echo "FAIL: approve reported success on a 502" >&2
cat "$OUTPUT_FILE" >&2
exit 1
fi
grep -q 'HTTP 502' "$OUTPUT_FILE"
if ! grep -q '502 Bad Gateway' "$OUTPUT_FILE"; then
echo "FAIL: a non-JSON error body was dropped instead of degrading to its first line (#1004)" >&2
cat "$OUTPUT_FILE" >&2
exit 1
fi
assert_no_temp_leak "review-refused-html"
echo "pr-review.sh REST review + comment create/read-back regression passed" echo "pr-review.sh REST review + comment create/read-back regression passed"
@@ -0,0 +1,165 @@
#!/usr/bin/env bash
# check-test-enumeration.sh — CI test-membership guard (#1017).
#
# CI reaches shell suites through two hand-enumerated surfaces:
# S1 packages/mosaic/package.json scripts."test:framework-shell"
# S2 .woodpecker/ci.yml direct `bash packages/mosaic/framework/tools/...` commands
#
# A hand-enumerated allowlist re-arms its own gap: a new suite never auto-joins,
# so the list silently under-runs the disk (17 of 39 suites were invisible when
# #1017 was filed). This guard makes that under-run impossible to do silently:
#
# FAIL when a suite-shaped file exists on disk and is neither enumerated on
# the UNION of both surfaces nor listed in the exclusions file.
# ("Enumerated", deliberately — F1/F2 on PR #1018 proved this guard sees
# NAMING, not reachability, and its words must not claim otherwise.)
# FAIL when either surface names a path that does not exist on disk
# (a rename manufactures a stale entry silently — checked BOTH directions).
# FAIL when an exclusion entry has no reason, names a path that is gone,
# names a path that is also enumerated (contradiction), or names a path
# outside the population (dead weight that looks like coverage).
#
# POPULATION PATTERN — a deliberate decision, stated per #1017's record:
# basename matches *test*.sh (contains "test", ends ".sh"). Deliberately BROAD:
# the strict `test-*.sh` prefix cannot even name three real boundary files
# (tmux/agent-send.test.sh — CI-run; orchestrator/smoke-test.sh;
# wake/validate-973/microtest-wake-assert.sh), and three independent censuses
# handled that last file three different ways with no trace of the judgement.
# The broad pattern makes such files MEMBERS, so their disposition must be a
# signed exclusion, not an accident of the glob. The SAME pattern is applied to
# both sides of the comparison (disk and enumeration) — a comparison globbed two
# ways runs on two different populations. Scripts outside the pattern on both
# sides symmetrically (e.g. check-resident-budget.sh, verify-sanitized.sh) are
# check-scripts, not suites; their existence is still verified via the
# both-directions rule because every surface-named path must exist on disk.
#
# The surfaces are PARSED, never line-ranged: three seats independently
# mis-scoped hand-written line ranges against these files (#1017 thread). S1 is
# read via JSON + command-chain tokenization; S2 by extracting every
# packages/mosaic/framework/tools/ token wherever it appears in the file.
#
# Exclusions file format (framework/tools/quality/test-enumeration-exclusions.txt):
# <repo-relative-path> | <non-empty reason>
# Lines starting with # and blank lines are ignored. An exclusion is a recorded
# decision someone signed, not an omission nobody made.
set -uo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
ROOT="$(cd "$SCRIPT_DIR/../../../../../.." && pwd)"
while (( $# )); do
case "$1" in
--root) ROOT="$(cd "$2" && pwd)"; shift 2 ;;
*) echo "usage: check-test-enumeration.sh [--root <repo-root>]" >&2; exit 2 ;;
esac
done
PKG_JSON="$ROOT/packages/mosaic/package.json"
CI_YML="$ROOT/.woodpecker/ci.yml"
TOOLS_DIR="$ROOT/packages/mosaic/framework/tools"
EXCLUSIONS="$TOOLS_DIR/quality/test-enumeration-exclusions.txt"
for f in "$PKG_JSON" "$CI_YML"; do
[[ -f "$f" ]] || { echo "FAIL: required surface file missing: $f" >&2; exit 2; }
done
[[ -d "$TOOLS_DIR" ]] || { echo "FAIL: tools dir missing: $TOOLS_DIR" >&2; exit 2; }
fail_count=0
fail() { printf 'FAIL %s\n' "$1"; fail_count=$(( fail_count + 1 )); }
# in_population <repo-relative path> — the single pattern, used for BOTH sides.
in_population() {
local base; base="$(basename "$1")"
[[ "$base" == *test*.sh ]]
}
# --- Surface 1: package.json test:framework-shell, parsed, repo-relative -----
# Tokens are script paths iff they contain "/" and end .sh/.py; interpreter
# names and flags are skipped. Paths are relative to packages/mosaic/.
mapfile -t S1 < <(python3 - "$PKG_JSON" <<'PY'
import json, shlex, sys
cmd = json.load(open(sys.argv[1]))["scripts"].get("test:framework-shell", "")
seen = []
for seg in cmd.split("&&"):
for tok in shlex.split(seg):
if "/" in tok and (tok.endswith(".sh") or tok.endswith(".py")):
path = "packages/mosaic/" + tok
if path not in seen:
seen.append(path)
print("\n".join(seen))
PY
)
# --- Surface 2: ci.yml, every framework/tools token wherever it appears ------
# Comment lines (first non-whitespace char is #) are skipped BEFORE matching:
# commenting an invocation out is the most common way a suite actually gets
# disabled, and a raw-text regex would keep calling it enumerated (F1, 20155 on
# PR #1018 — demonstrated, not argued). Known residual limit: a path named only
# in a TRAILING comment on a live line still matches; no such line exists today
# and full fidelity would need a YAML parser the CI image does not ship.
mapfile -t S2 < <(grep -vE '^[[:space:]]*#' "$CI_YML" \
| grep -oE 'packages/mosaic/framework/tools/[A-Za-z0-9_./-]+\.(sh|py)' | sort -u)
# --- Union, and its population-restricted view -------------------------------
declare -A ENUM=() ENUM_POP=()
for p in "${S1[@]:-}" "${S2[@]:-}"; do
[[ -n "$p" ]] || continue
ENUM["$p"]=1
in_population "$p" && ENUM_POP["$p"]=1
done
# --- Direction B: every surface-named path must exist on disk ----------------
for p in "${!ENUM[@]}"; do
[[ -f "$ROOT/$p" ]] || fail "STALE ENUMERATION: surfaces name '$p' but it does not exist on disk"
done
# --- Exclusions: parsed with the same rigor the enumeration gets -------------
declare -A EXCLUDED=()
if [[ -f "$EXCLUSIONS" ]]; then
lineno=0
while IFS= read -r line; do
lineno=$(( lineno + 1 ))
[[ "$line" =~ ^[[:space:]]*(#|$) ]] && continue
path="${line%%|*}"; reason="${line#*|}"
path="$(echo "$path" | sed 's/^[[:space:]]*//; s/[[:space:]]*$//')"
reason="$(echo "$reason" | sed 's/^[[:space:]]*//; s/[[:space:]]*$//')"
if [[ "$line" != *"|"* || -z "$reason" ]]; then
fail "EXCLUSION MISSING REASON: line $lineno ('$path') — an exclusion is a recorded decision someone signed"
continue
fi
if [[ ! -f "$ROOT/$path" ]]; then
fail "STALE EXCLUSION: line $lineno excludes '$path' which does not exist on disk"
continue
fi
if ! in_population "$path"; then
fail "EXCLUSION OUTSIDE POPULATION: line $lineno excludes '$path' which the population pattern does not name — dead weight that reads as coverage"
continue
fi
if [[ -n "${ENUM[$path]:-}" ]]; then
fail "CONTRADICTORY EXCLUSION: line $lineno excludes '$path' which the surfaces already enumerate"
continue
fi
EXCLUDED["$path"]=1
done < "$EXCLUSIONS"
fi
# --- Direction A: disk population must be enumerated or signed-excluded ------
disk_total=0
unlisted=0
while IFS= read -r f; do
rel="${f#"$ROOT"/}"
in_population "$rel" || continue
disk_total=$(( disk_total + 1 ))
if [[ -z "${ENUM_POP[$rel]:-}" && -z "${EXCLUDED[$rel]:-}" ]]; then
fail "UNENUMERATED: '$rel' exists on disk but is neither enumerated on any CI surface nor signed in the exclusions file"
unlisted=$(( unlisted + 1 ))
fi
done < <(find "$TOOLS_DIR" -type f -name '*.sh' | sort)
if (( fail_count > 0 )); then
printf 'enumeration guard: %d failure(s) — population %d, enumerated (in-population) %d, excluded %d\n' \
"$fail_count" "$disk_total" "${#ENUM_POP[@]}" "${#EXCLUDED[@]}"
exit 1
fi
printf 'enumeration guard: OK — population %d, enumerated (in-population) %d, excluded (signed) %d, surfaces name %d path(s), all present on disk\n' \
"$disk_total" "${#ENUM_POP[@]}" "${#EXCLUDED[@]}" "${#ENUM[@]}"
@@ -0,0 +1,166 @@
#!/usr/bin/env bash
# test-check-test-enumeration.sh — needles for the enumeration guard (#1017).
#
# Every failure mode the guard promises gets BOTH polarities:
# NEEDLE a fixture that MUST trip the guard, asserted on the guard's OWN
# words (--out) — exit 1 alone cannot distinguish "caught the rogue
# file" from "choked on the fixture".
# CONTROL a fixture that MUST pass. A guard that failed unconditionally
# would satisfy every needle here — the null-case defect the guard's
# own subject matter (#1017) exists to make impossible.
#
# The needles encode the specific errors that produced #1017's thread:
# n6 is the 20124 boundary file (a suite the strict prefix cannot name);
# n2b proves surface 2 is PARSED, not line-ranged (three seats mis-scoped
# hand-written ranges against ci.yml);
# n5/n7 keep the exclusions file honest so it cannot become the next silent cap;
# n8/c4 are F1 (20155): a commented-out ci.yml line is NOT enumeration —
# commenting-out is the most common way a suite actually gets disabled,
# and it must fail loud in one direction without false-staling the other.
set -uo pipefail
HERE="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
GUARD="$HERE/check-test-enumeration.sh"
TMP="$(mktemp -d)"; trap 'rm -rf "$TMP"' EXIT
PASS=0; FAIL=0
# fixture <name> — a minimal repo root the guard accepts via --root:
# one suite enumerated on S1 (plus a naming-outlier suite, so the S1 parser's
# handling of non-prefix names is always exercised), one on S2, one check-script
# named on S2 that is outside the population, and an empty exclusions file.
fixture() {
local r="$TMP/$1"
mkdir -p "$r/packages/mosaic/framework/tools/git" \
"$r/packages/mosaic/framework/tools/tmux" \
"$r/packages/mosaic/framework/tools/quality/scripts" \
"$r/.woodpecker"
printf '#!/usr/bin/env bash\nexit 0\n' > "$r/packages/mosaic/framework/tools/git/test-a.sh"
printf '#!/usr/bin/env bash\nexit 0\n' > "$r/packages/mosaic/framework/tools/tmux/outlier.test.sh"
printf '#!/usr/bin/env bash\nexit 0\n' > "$r/packages/mosaic/framework/tools/quality/scripts/test-ci.sh"
printf '#!/usr/bin/env bash\nexit 0\n' > "$r/packages/mosaic/framework/tools/quality/scripts/verify-thing.sh"
cat > "$r/packages/mosaic/package.json" <<'JSON'
{"scripts": {"test:framework-shell": "bash framework/tools/git/test-a.sh && bash framework/tools/tmux/outlier.test.sh"}}
JSON
cat > "$r/.woodpecker/ci.yml" <<'YML'
steps:
sanitize:
commands:
- bash packages/mosaic/framework/tools/quality/scripts/verify-thing.sh
guard:
commands:
- bash packages/mosaic/framework/tools/quality/scripts/test-ci.sh
YML
: > "$r/packages/mosaic/framework/tools/quality/test-enumeration-exclusions.txt"
printf '%s' "$r"
}
# expect <kind> <want-exit> <desc> [--out <substring>] -- <root>
expect() {
local kind="$1" want="$2" desc="$3"; shift 3
local need_out=""
while (( $# )); do
case "$1" in
--out) need_out="$2"; shift 2 ;;
--) shift; break ;;
esac
done
local root="$1" got=0 out
out="$(bash "$GUARD" --root "$root" 2>&1)" || got=$?
local why=""
[[ "$got" == "$want" ]] || why="wanted exit $want, got $got"
if [[ -z "$why" && -n "$need_out" && "$out" != *"$need_out"* ]]; then
why="exit $got as expected, but output never said: $need_out"
fi
if [[ -z "$why" ]]; then
printf ' PASS [%-7s] %s (exit %s)\n' "$kind" "$desc" "$got"
PASS=$(( PASS + 1 ))
else
printf ' FAIL [%-7s] %s — %s\n' "$kind" "$desc" "$why"
printf '%s\n' "$out" | sed 's/^/ | /'
FAIL=$(( FAIL + 1 ))
fi
}
excl() { printf '%s\n' "$2" >> "$1/packages/mosaic/framework/tools/quality/test-enumeration-exclusions.txt"; }
echo "=== c1: a fully consistent fixture passes ==="
R="$(fixture c1)"
expect CONTROL 0 "consistent tree: both surfaces enumerated, nothing unlisted" \
--out "enumeration guard: OK" -- "$R"
echo "=== n1: an on-disk suite reachable from no surface must fail ==="
R="$(fixture n1)"
printf '#!/usr/bin/env bash\nexit 0\n' > "$R/packages/mosaic/framework/tools/git/test-rogue.sh"
expect NEEDLE 1 "unlisted suite is named in the failure" \
--out "UNENUMERATED: 'packages/mosaic/framework/tools/git/test-rogue.sh'" -- "$R"
echo "=== n6: the 20124 boundary file — a suite the strict prefix cannot name ==="
R="$(fixture n6)"
printf '#!/usr/bin/env bash\nexit 0\n' > "$R/packages/mosaic/framework/tools/git/rogue.test.sh"
expect NEEDLE 1 "naming-outlier suite (*.test.sh) is a population member, not invisible" \
--out "UNENUMERATED: 'packages/mosaic/framework/tools/git/rogue.test.sh'" -- "$R"
echo "=== c3: a non-suite script outside the pattern is outside it on BOTH sides ==="
R="$(fixture c3)"
printf '#!/usr/bin/env bash\nexit 0\n' > "$R/packages/mosaic/framework/tools/git/check-unrelated.sh"
expect CONTROL 0 "check-script on disk, unlisted, outside population: not the guard's business" \
--out "enumeration guard: OK" -- "$R"
echo "=== n2/n2b: a surface naming a path absent from disk must fail — both surfaces ==="
R="$(fixture n2)"
rm "$R/packages/mosaic/framework/tools/git/test-a.sh"
expect NEEDLE 1 "S1 (package.json) stale entry" \
--out "STALE ENUMERATION: surfaces name 'packages/mosaic/framework/tools/git/test-a.sh'" -- "$R"
R="$(fixture n2b)"
rm "$R/packages/mosaic/framework/tools/quality/scripts/test-ci.sh"
expect NEEDLE 1 "S2 (ci.yml) stale entry — proves ci.yml is parsed, not line-ranged" \
--out "STALE ENUMERATION: surfaces name 'packages/mosaic/framework/tools/quality/scripts/test-ci.sh'" -- "$R"
echo "=== c2: a rogue suite with a SIGNED exclusion passes, and is counted ==="
R="$(fixture c2)"
printf '#!/usr/bin/env bash\nexit 0\n' > "$R/packages/mosaic/framework/tools/git/test-rogue.sh"
excl "$R" "packages/mosaic/framework/tools/git/test-rogue.sh | non-hermetic pending fixture work (needle-suite specimen)"
expect CONTROL 0 "signed exclusion is honoured and visible in the summary" \
--out "excluded (signed) 1" -- "$R"
echo "=== n3: an exclusion with no reason is not a decision ==="
R="$(fixture n3)"
printf '#!/usr/bin/env bash\nexit 0\n' > "$R/packages/mosaic/framework/tools/git/test-rogue.sh"
excl "$R" "packages/mosaic/framework/tools/git/test-rogue.sh | "
expect NEEDLE 1 "empty reason rejected" --out "EXCLUSION MISSING REASON" -- "$R"
R="$(fixture n3b)"
printf '#!/usr/bin/env bash\nexit 0\n' > "$R/packages/mosaic/framework/tools/git/test-rogue.sh"
excl "$R" "packages/mosaic/framework/tools/git/test-rogue.sh"
expect NEEDLE 1 "missing separator rejected (the path alone is not a signature)" \
--out "EXCLUSION MISSING REASON" -- "$R"
echo "=== n4: an exclusion whose path is gone is stale, not satisfied ==="
R="$(fixture n4)"
excl "$R" "packages/mosaic/framework/tools/git/test-vanished.sh | was excluded once, then deleted"
expect NEEDLE 1 "stale exclusion rejected" --out "STALE EXCLUSION" -- "$R"
echo "=== n5: excluding an enumerated suite is a contradiction, not belt-and-braces ==="
R="$(fixture n5)"
excl "$R" "packages/mosaic/framework/tools/git/test-a.sh | already in CI but excluded anyway"
expect NEEDLE 1 "contradictory exclusion rejected" --out "CONTRADICTORY EXCLUSION" -- "$R"
echo "=== n8/c4: a commented-out ci.yml line is not enumeration (F1, 20155) ==="
R="$(fixture n8)"
printf '#!/usr/bin/env bash\nexit 0\n' > "$R/packages/mosaic/framework/tools/git/test-disabled.sh"
printf ' # - bash packages/mosaic/framework/tools/git/test-disabled.sh\n' >> "$R/.woodpecker/ci.yml"
expect NEEDLE 1 "suite named only in a commented-out invocation is UNENUMERATED" \
--out "UNENUMERATED: 'packages/mosaic/framework/tools/git/test-disabled.sh'" -- "$R"
R="$(fixture c4)"
printf ' # - bash packages/mosaic/framework/tools/git/test-vanished.sh\n' >> "$R/.woodpecker/ci.yml"
expect CONTROL 0 "comment naming an absent path raises no false stale-enumeration" \
--out "enumeration guard: OK" -- "$R"
echo "=== n7: excluding a file outside the population is dead weight, not coverage ==="
R="$(fixture n7)"
excl "$R" "packages/mosaic/framework/tools/quality/scripts/verify-thing.sh | not a suite but signing it anyway"
expect NEEDLE 1 "out-of-population exclusion rejected" --out "EXCLUSION OUTSIDE POPULATION" -- "$R"
echo
printf 'enumeration-guard needles: %d passed, %d failed\n' "$PASS" "$FAIL"
(( FAIL == 0 ))
@@ -0,0 +1,45 @@
# test-enumeration-exclusions.txt — signed exclusions for check-test-enumeration.sh (#1017).
#
# Every entry is a recorded decision: a suite-shaped file that exists on disk,
# is NOT reachable from any CI surface, and carries the reason someone signed
# for that. The guard FAILS on an entry with no reason, a stale path, or a path
# the surfaces already enumerate. Burning an entry down = making it CI-reachable
# (package.json test:framework-shell or a ci.yml step) and deleting its line.
#
# Format: <repo-relative path> | <reason>
# All entries below were signed at #1017's filing base (main 826a8b3b, 2026-07-31)
# by pepper (sb-it-1-dt); measurements cited are one-run assertions from that seat.
# --- tools/git: the #1007 five — non-hermetic, resolve real credentials ---
packages/mosaic/framework/tools/git/test-pr-merge-gitea-empty-uid.sh | resolves real credentials (#1007 census); joins CI after the wrapper-half hermeticity fix (git -C scoping)
packages/mosaic/framework/tools/git/test-issue-create-interactive-auth.sh | resolves real credentials (#1007 census); joins CI after the wrapper-half hermeticity fix
packages/mosaic/framework/tools/git/test-gitea-login-resolution.sh | resolves real credentials (#1007 census); joins CI after the wrapper-half hermeticity fix
packages/mosaic/framework/tools/git/test-pr-metadata-gitea.sh | resolves real credentials (#1007 census, fourth entry via family-grep); joins CI after the wrapper-half hermeticity fix
packages/mosaic/framework/tools/git/test-issue-comment-readback.sh | resolves real credentials (#1007 census, fifth entry); joins CI after the wrapper-half hermeticity fix
# --- tools/git: push guards — measured green locally, CI-image fitness unverified ---
packages/mosaic/framework/tools/git/test-push-guard.sh | measured green at 826a8b3b (46 passed / 0 failed, one run, 2026-07-31); CI-image fitness unverified; #1017 burndown
packages/mosaic/framework/tools/git/test-mutate-push-guard.sh | measured green at 826a8b3b (8/0, 13 mutants killed 0 survived, one run, 2026-07-31); requires setsid (util-linux), absent from the alpine base image; #1017 burndown
packages/mosaic/framework/tools/git/test-issue-create-body-safety.sh | hermeticity unaudited — the unprotected suite in #1007's protected/unprotected split; audit before CI; #1017 burndown
# --- tools/git: unmeasured ---
packages/mosaic/framework/tools/git/test-verify-clean-clone.sh | unmeasured in CI image; asserts git file-mode (100644/755) semantics that need verification on the CI filesystem first; #1017 burndown
packages/mosaic/framework/tools/git/test-help-exit-code.sh | unmeasured in CI image; stub-based (#701 regression harness), likely CI-fit; #1017 burndown
packages/mosaic/framework/tools/git/test-lane-brief-pr-linkage.sh | unmeasured in CI image; fixture-based (#546/#547 regression harness), likely CI-fit; #1017 burndown
# --- tools/tmux: require a live tmux server ---
packages/mosaic/framework/tools/tmux/test-send-message-socket.sh | requires a real tmux server on a throwaway socket; CI image ships no tmux; #1017 burndown (needs tmux in image or a signed permanent exclusion)
packages/mosaic/framework/tools/tmux/test-send-message-verdict.sh | requires real tmux-pane fixtures on a throwaway socket; CI image ships no tmux; #1017 burndown (same condition as its sibling)
# --- single-suite directories: unmeasured in CI ---
packages/mosaic/framework/tools/fleet/test-start-agent-session.sh | unmeasured in CI image; stubs tmux via a fake bin dir, likely CI-fit; #1017 burndown
packages/mosaic/framework/tools/glpi/test-list-http-status.sh | unmeasured in CI image; stub-based (#807 regression harness), likely CI-fit; #1017 burndown
packages/mosaic/framework/tools/orchestrator/test-board-roll.sh | unmeasured in CI image; file-fixture based, likely CI-fit; #1017 burndown
packages/mosaic/framework/tools/woodpecker/test-ci-wait-exit-matrix.sh | unmeasured in CI image; drives ci-wait.sh against a stub pipeline-status.sh, likely CI-fit; #1017 burndown
# --- naming-boundary files the strict test-*.sh prefix cannot even name ---
# (#1017: three independent censuses handled the microtest file three different
# ways — editorial drop, structural exclusion, accidental inclusion — with no
# recorded judgement. These lines ARE that judgement, signed.)
packages/mosaic/framework/tools/orchestrator/smoke-test.sh | behavior smoke checks for coord continue/run workflows, run manually by orchestrator seats; unmeasured in CI; #1017 burndown
packages/mosaic/framework/tools/wake/validate-973/microtest-wake-assert.sh | #973 instrument self-test, run as a precondition of the validate-973 evidence procedure rather than as a standing CI suite; #1017 burndown candidate
@@ -529,7 +529,19 @@ cmd_run() {
echo "detector.sh: WARN — off-host liveness beacon emit failed (see beacon.sh); the off-host absence check remains the authoritative dead-man." >&2 echo "detector.sh: WARN — off-host liveness beacon emit failed (see beacon.sh); the off-host absence check remains the authoritative dead-man." >&2
fi fi
[ "$once" -eq 1 ] && break [ "$once" -eq 1 ] && break
sleep "$interval" # Close the detector lock fd in the sleep child; otherwise an orphaned sleep
# keeps the single-instance flock (fd 9, taken at exec 9> above) alive after
# the detector parent dies. The lock is non-blocking (`flock -n`, above), so
# for as long as that sleep survives a replacement instance is REFUSED and
# exits rather than queueing. This particular hold is BOUNDED by one poll
# interval (WAKE_DETECTOR_INTERVAL, default 30s): when the orphaned sleep
# exits its copy of fd 9 closes, ending this bounded sleep-child hold. It
# does NOT follow that the next start succeeds — other inheritors of fd 9
# (the M1 adapter, M2 sink grandchildren) are outside this patch's scope and
# can keep holding the flock. The cost this removes is a restart window in
# which every supervisor retry fails on the sleep child's account.
# `9>&-` closes ONLY the child's copy — the parent's lock is unaffected.
sleep "$interval" 9>&-
done done
} }
@@ -33,7 +33,11 @@ set -uo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
# #973: three-valued grep assertion helpers (has_match/count_lines); init saves real stderr for abort loudness. # #973: three-valued grep assertion helpers (has_match/count_lines); init saves real stderr for abort loudness.
# shellcheck disable=SC1091 # shellcheck disable=SC1091
. "$SCRIPT_DIR/_wake-common.sh" && wake_assert_init if ! . "$SCRIPT_DIR/_wake-common.sh"; then
echo "WAKE-ASSERT INIT ABORT: cannot source _wake-common.sh — suite ran ZERO wake assertions (#984)" >&2
exit 97
fi
wake_assert_init
BEACON="$SCRIPT_DIR/beacon.sh" BEACON="$SCRIPT_DIR/beacon.sh"
command -v jq >/dev/null 2>&1 || { command -v jq >/dev/null 2>&1 || {
@@ -31,7 +31,11 @@ set -uo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
# #973: three-valued grep assertion helpers (has_match/count_lines); init saves real stderr for abort loudness. # #973: three-valued grep assertion helpers (has_match/count_lines); init saves real stderr for abort loudness.
# shellcheck disable=SC1091 # shellcheck disable=SC1091
. "$SCRIPT_DIR/_wake-common.sh" && wake_assert_init if ! . "$SCRIPT_DIR/_wake-common.sh"; then
echo "WAKE-ASSERT INIT ABORT: cannot source _wake-common.sh — suite ran ZERO wake assertions (#984)" >&2
exit 97
fi
wake_assert_init
DET="$SCRIPT_DIR/detector.sh" DET="$SCRIPT_DIR/detector.sh"
STORE="$SCRIPT_DIR/store.sh" STORE="$SCRIPT_DIR/store.sh"
@@ -37,7 +37,11 @@ set -uo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
# #973: three-valued grep assertion helpers (has_match/count_lines); init saves real stderr for abort loudness. # #973: three-valued grep assertion helpers (has_match/count_lines); init saves real stderr for abort loudness.
# shellcheck disable=SC1091 # shellcheck disable=SC1091
. "$SCRIPT_DIR/_wake-common.sh" && wake_assert_init if ! . "$SCRIPT_DIR/_wake-common.sh"; then
echo "WAKE-ASSERT INIT ABORT: cannot source _wake-common.sh — suite ran ZERO wake assertions (#984)" >&2
exit 97
fi
wake_assert_init
STORE="$SCRIPT_DIR/store.sh" STORE="$SCRIPT_DIR/store.sh"
DIGEST="$SCRIPT_DIR/digest.sh" DIGEST="$SCRIPT_DIR/digest.sh"
SIGN="$SCRIPT_DIR/sign.sh" SIGN="$SCRIPT_DIR/sign.sh"
@@ -119,7 +119,11 @@ set -uo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
# #973: three-valued grep assertion helpers (has_match/count_lines); init saves real stderr for abort loudness. # #973: three-valued grep assertion helpers (has_match/count_lines); init saves real stderr for abort loudness.
# shellcheck disable=SC1091 # shellcheck disable=SC1091
. "$SCRIPT_DIR/_wake-common.sh" && wake_assert_init if ! . "$SCRIPT_DIR/_wake-common.sh"; then
echo "WAKE-ASSERT INIT ABORT: cannot source _wake-common.sh — suite ran ZERO wake assertions (#984)" >&2
exit 97
fi
wake_assert_init
DIGEST="$SCRIPT_DIR/digest.sh" DIGEST="$SCRIPT_DIR/digest.sh"
command -v jq >/dev/null 2>&1 || { command -v jq >/dev/null 2>&1 || {
@@ -27,7 +27,11 @@ set -uo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
# #973: three-valued grep assertion helpers (has_match/count_lines); init saves real stderr for abort loudness. # #973: three-valued grep assertion helpers (has_match/count_lines); init saves real stderr for abort loudness.
# shellcheck disable=SC1091 # shellcheck disable=SC1091
. "$SCRIPT_DIR/_wake-common.sh" && wake_assert_init if ! . "$SCRIPT_DIR/_wake-common.sh"; then
echo "WAKE-ASSERT INIT ABORT: cannot source _wake-common.sh — suite ran ZERO wake assertions (#984)" >&2
exit 97
fi
wake_assert_init
ORACLE="$SCRIPT_DIR/fn-oracle.sh" ORACLE="$SCRIPT_DIR/fn-oracle.sh"
DET="$SCRIPT_DIR/detector.sh" DET="$SCRIPT_DIR/detector.sh"
@@ -36,7 +36,11 @@ set -uo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
# #973: three-valued grep assertion helpers (has_match/count_lines); init saves real stderr for abort loudness. # #973: three-valued grep assertion helpers (has_match/count_lines); init saves real stderr for abort loudness.
# shellcheck disable=SC1091 # shellcheck disable=SC1091
. "$SCRIPT_DIR/_wake-common.sh" && wake_assert_init if ! . "$SCRIPT_DIR/_wake-common.sh"; then
echo "WAKE-ASSERT INIT ABORT: cannot source _wake-common.sh — suite ran ZERO wake assertions (#984)" >&2
exit 97
fi
wake_assert_init
WI="$SCRIPT_DIR/wake-install.sh" WI="$SCRIPT_DIR/wake-install.sh"
BEACON="$SCRIPT_DIR/beacon.sh" BEACON="$SCRIPT_DIR/beacon.sh"
FRAMEWORK_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)" FRAMEWORK_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)"
@@ -48,7 +48,11 @@ set -uo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
# #973: three-valued grep assertion helpers (has_match/count_lines); init saves real stderr for abort loudness. # #973: three-valued grep assertion helpers (has_match/count_lines); init saves real stderr for abort loudness.
# shellcheck disable=SC1091 # shellcheck disable=SC1091
. "$SCRIPT_DIR/_wake-common.sh" && wake_assert_init if ! . "$SCRIPT_DIR/_wake-common.sh"; then
echo "WAKE-ASSERT INIT ABORT: cannot source _wake-common.sh — suite ran ZERO wake assertions (#984)" >&2
exit 97
fi
wake_assert_init
PRE="$SCRIPT_DIR/preimage.sh" PRE="$SCRIPT_DIR/preimage.sh"
STORE="$SCRIPT_DIR/store.sh" STORE="$SCRIPT_DIR/store.sh"
DET="$SCRIPT_DIR/detector.sh" DET="$SCRIPT_DIR/detector.sh"
@@ -31,7 +31,11 @@ set -uo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
# #973: three-valued grep assertion helpers (has_match/count_lines); init saves real stderr for abort loudness. # #973: three-valued grep assertion helpers (has_match/count_lines); init saves real stderr for abort loudness.
# shellcheck disable=SC1091 # shellcheck disable=SC1091
. "$SCRIPT_DIR/_wake-common.sh" && wake_assert_init if ! . "$SCRIPT_DIR/_wake-common.sh"; then
echo "WAKE-ASSERT INIT ABORT: cannot source _wake-common.sh — suite ran ZERO wake assertions (#984)" >&2
exit 97
fi
wake_assert_init
RECON="$SCRIPT_DIR/reconcile.sh" RECON="$SCRIPT_DIR/reconcile.sh"
STORE="$SCRIPT_DIR/store.sh" STORE="$SCRIPT_DIR/store.sh"
DET="$SCRIPT_DIR/detector.sh" DET="$SCRIPT_DIR/detector.sh"
@@ -44,7 +44,11 @@ set -uo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
# #973: three-valued grep assertion helpers (has_match/count_lines); init saves real stderr for abort loudness. # #973: three-valued grep assertion helpers (has_match/count_lines); init saves real stderr for abort loudness.
# shellcheck disable=SC1091 # shellcheck disable=SC1091
. "$SCRIPT_DIR/_wake-common.sh" && wake_assert_init if ! . "$SCRIPT_DIR/_wake-common.sh"; then
echo "WAKE-ASSERT INIT ABORT: cannot source _wake-common.sh — suite ran ZERO wake assertions (#984)" >&2
exit 97
fi
wake_assert_init
STORE="$SCRIPT_DIR/store.sh" STORE="$SCRIPT_DIR/store.sh"
ACK="$SCRIPT_DIR/ack.sh" ACK="$SCRIPT_DIR/ack.sh"
@@ -34,7 +34,11 @@ set -uo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
# #973: three-valued grep assertion helpers (has_match/count_lines); init saves real stderr for abort loudness. # #973: three-valued grep assertion helpers (has_match/count_lines); init saves real stderr for abort loudness.
# shellcheck disable=SC1091 # shellcheck disable=SC1091
. "$SCRIPT_DIR/_wake-common.sh" && wake_assert_init if ! . "$SCRIPT_DIR/_wake-common.sh"; then
echo "WAKE-ASSERT INIT ABORT: cannot source _wake-common.sh — suite ran ZERO wake assertions (#984)" >&2
exit 97
fi
wake_assert_init
STORE="$SCRIPT_DIR/store.sh" STORE="$SCRIPT_DIR/store.sh"
command -v jq >/dev/null 2>&1 || { command -v jq >/dev/null 2>&1 || {
@@ -10,8 +10,9 @@ comparison come from independent code paths.
Subcommands (all print sorted, stable output; non-zero exit on any failure): Subcommands (all print sorted, stable output; non-zero exit on any failure):
expected The expected coordinate set from the ARTIFACT: one expected The expected coordinate set from the ARTIFACT: one
"<helper> <file>:<line+3>" row per denominator row (+3 = the "<helper> <file>:<line+7>" row per denominator row (+7 = 3
uniform header shift the converter applied; converter-verified). converter header lines + 4 lines from the #984 source guard,
uniform across all ten suites).
Multi-grep lines stay ONE coordinate. Multi-grep lines stay ONE coordinate.
static The converted-site inventory from the SOURCE TEXT at the current static The converted-site inventory from the SOURCE TEXT at the current
@@ -23,9 +24,9 @@ Subcommands (all print sorted, stable output; non-zero exit on any failure):
from the text.) from the text.)
arms The forced-error arm list: the 19 denominator canaries plus one arms The forced-error arm list: the 19 denominator canaries plus one
E-form arm (store-ack:733→736, a $(count_lines) capture compared E-form arm (store-ack:733→740, a $(count_lines) capture compared
afterward — the A6 shape) plus one F-form arm (quarantine:560→563, afterward — the A6 shape) plus one F-form arm (quarantine:560→567,
the multi-grep pipeline capture), as "<helper> <file>:<line+3> the multi-grep pipeline capture), as "<helper> <file>:<line+7>
<form>". Both extras are asserted to exist in the artifact with <form>". Both extras are asserted to exist in the artifact with
the expected form — a renumber that moved them fails here, not the expected form — a renumber that moved them fails here, not
silently downstream. silently downstream.
@@ -33,7 +34,8 @@ Subcommands (all print sorted, stable output; non-zero exit on any failure):
sweep Residual sweep: the denominator's own classifier (ported from the sweep Residual sweep: the denominator's own classifier (ported from the
frozen derivation) over the ten suites at the current tree must frozen derivation) over the ten suites at the current tree must
find ZERO unconverted verdict-form grep sites; and, IN THE SAME find ZERO unconverted verdict-form grep sites; and, IN THE SAME
RUN, six per-form specimens planted into a temp copy of a real RUN, eight specimens (six per-form + two absorb-branch probes, #985)
planted into a temp copy of a real
suite must ALL be found with their correct forms — an instrument suite must ALL be found with their correct forms — an instrument
that reports zero must first be seen finding what it claims to that reports zero must first be seen finding what it claims to
find (A5). find (A5).
@@ -50,7 +52,9 @@ HERE = Path(__file__).resolve().parent
WAKE = HERE.parent WAKE = HERE.parent
ART = HERE / "denominator-089615f.json" ART = HERE / "denominator-089615f.json"
HEADER_SHIFT = 3 # converter inserted 3 header lines after SCRIPT_DIR in every suite HEADER_SHIFT = 7 # 3 converter header lines after SCRIPT_DIR + 4 lines from the
# #984 source guard (1-line `. _wake-common.sh && wake_assert_init` became a 5-line
# guarded block) — both uniform across all ten suites, both above every site.
# The two hand-picked extra arms (base coordinates; forms asserted at load). # The two hand-picked extra arms (base coordinates; forms asserted at load).
EXTRA_ARMS = [ EXTRA_ARMS = [
@@ -68,10 +72,14 @@ RX_ASSIGN_SUB = re.compile(r'=\s*"?\$\(.*grep')
RX_IF = re.compile(r"^\s*(el)?if\s+.*grep") RX_IF = re.compile(r"^\s*(el)?if\s+.*grep")
RX_GREP = re.compile(r"(^|[^A-Za-z0-9_.-])grep([^A-Za-z0-9_.-]|$)") RX_GREP = re.compile(r"(^|[^A-Za-z0-9_.-])grep([^A-Za-z0-9_.-]|$)")
# grep in COMMAND position: at line start or after a command separator / subshell
def polarity(line): # opener / shell keyword / `!`. Quote-unaware by design — a quoted "grep" after a
m = RX_FAIL_SAME.search(line) # separator reads as a command and lands the line in residual, which fails LOUD;
return "OR" if m.group(1) == "||" else "AND" # the absorb direction (note) is the one that must never fire on a real verdict.
RX_GREP_CMD = re.compile(
r"(?:^|[;|&(`]|\$\(|\bif\b|\belif\b|\bthen\b|\belse\b|\bdo\b|\bwhile\b|\buntil\b|!)"
r"\s*grep(?:\s|$)"
)
def classify(lines): def classify(lines):
@@ -137,6 +145,28 @@ def classify(lines):
return sites, dispo return sites, dispo
def residual_sites(lines):
"""classify() plus the absorb decision — the ONE path both sweep legs share.
A classified site is absorbed as a note only when its line carries a wake
helper token AND the line shows no grep in command position: a converted
line whose PATTERN argument merely contains the word grep. A helper line
that also runs a real grep verdict (has_match ... && grep -q SECRET ... &&
fail) stays residual (#985). Multi-line forms anchor the site at the line
containing grep, so a command-position grep on a continuation line never
shares its line with the helper token and stays residual by construction.
"""
sites, dispo = classify(lines)
residual, notes = [], []
for ln, form, text in sites:
line = lines[ln - 1]
if RX_HELPER.search(line) and not RX_GREP_CMD.search(line):
notes.append((ln, form, text))
else:
residual.append((ln, form, text))
return residual, notes, dispo
def load_art(): def load_art():
art = json.loads(ART.read_text()) art = json.loads(ART.read_text())
assert art["total"] == 261 == len(art["rows"]), "artifact self-consistency" assert art["total"] == 261 == len(art["rows"]), "artifact self-consistency"
@@ -199,13 +229,20 @@ def cmd_arms():
return 0 return 0
# (expected classify form, expected disposition through residual_sites, snippet)
PLANTS = [ PLANTS = [
("A-same-line", ['grep -q needle haystack || fail "plant-A"']), ("A-same-line", "residual", ['grep -q needle haystack || fail "plant-A"']),
("B-cont-operator", ["grep -q needle haystack ||", ' fail "plant-B"']), ("B-cont-operator", "residual", ["grep -q needle haystack ||", ' fail "plant-B"']),
("C-cont-backslash", ["grep -q needle \\", ' haystack || fail "plant-C"']), ("C-cont-backslash", "residual", ["grep -q needle \\", ' haystack || fail "plant-C"']),
("D-if-form", ["if ! grep -q needle haystack; then", ' fail "plant-D"', "fi"]), ("D-if-form", "residual", ["if ! grep -q needle haystack; then", ' fail "plant-D"', "fi"]),
("E-count-capture", ['[ "$(grep -c needle haystack)" = "1" ] || fail "plant-E"']), ("E-count-capture", "residual", ['[ "$(grep -c needle haystack)" = "1" ] || fail "plant-E"']),
("F-extract-capture", ['val="$(grep needle haystack)"']), ("F-extract-capture", "residual", ['val="$(grep needle haystack)"']),
# G: a converted line that ALSO runs a raw grep verdict — the helper token
# must not absorb it (#985)
("A-same-line", "residual", ['has_match -q needle "$F" && grep -q SECRET "$F" && fail "plant-G"']),
# H: negative control — helper whose PATTERN argument is the word grep;
# must be absorbed as a note, never residual
("A-same-line", "note", ['has_match -q "grep" haystack || fail "plant-H"']),
] ]
@@ -215,22 +252,20 @@ def cmd_sweep():
# leg 1: real suites at the current tree must be residual-free # leg 1: real suites at the current tree must be residual-free
for f in suite_files(art): for f in suite_files(art):
lines = (WAKE / f).read_text().split("\n") residual, notes, _dispo = residual_sites((WAKE / f).read_text().split("\n"))
sites, _dispo = classify(lines) for ln, form, text in notes:
residual = [] # converted line whose PATTERN argument contains the word grep:
for ln, form, text in sites: # not an unconverted site, but never silently absorbed either
if RX_HELPER.search(lines[ln - 1]): print(f"SWEEP-NOTE {f}:{ln} converted line matches grep-token ({form}): {text[:80]}")
# converted line whose PATTERN argument contains the word grep:
# not an unconverted site, but never silently absorbed either
print(f"SWEEP-NOTE {f}:{ln} converted line matches grep-token ({form}): {text[:80]}")
continue
residual.append((ln, form, text))
for ln, form, text in residual: for ln, form, text in residual:
print(f"SWEEP-RESIDUAL {f}:{ln} {form}: {text[:100]}") print(f"SWEEP-RESIDUAL {f}:{ln} {form}: {text[:100]}")
bad += 1 bad += 1
print(f"SWEEP {f}: {len(residual)} residual verdict site(s)") print(f"SWEEP {f}: {len(residual)} residual verdict site(s)")
# leg 2, SAME RUN: the instrument must find six per-form plants # leg 2, SAME RUN, SAME PATH as leg 1: the instrument must find every plant
# with the right form AND the right absorb disposition — plants G/H exercise
# the absorb branch itself, so this leg must go through residual_sites(),
# not raw classify()
donor = suite_files(art)[0] donor = suite_files(art)[0]
with tempfile.TemporaryDirectory() as td: with tempfile.TemporaryDirectory() as td:
planted = Path(td) / donor planted = Path(td) / donor
@@ -238,25 +273,28 @@ def cmd_sweep():
base_lines = planted.read_text().split("\n") base_lines = planted.read_text().split("\n")
offset = len(base_lines) offset = len(base_lines)
expect = {} expect = {}
for form, snippet in PLANTS: for form, dispo, snippet in PLANTS:
expect[offset + 1] = form # first physical line of each plant expect[offset + 1] = (form, dispo) # first physical line of each plant
base_lines.extend(snippet) base_lines.extend(snippet)
offset = len(base_lines) offset = len(base_lines)
planted.write_text("\n".join(base_lines)) planted.write_text("\n".join(base_lines))
sites, _ = classify(planted.read_text().split("\n")) residual, notes, _ = residual_sites(planted.read_text().split("\n"))
found = {ln: form for ln, form, _t in sites if ln in expect} found = {ln: (form, "residual") for ln, form, _t in residual}
unexpected = [(ln, form) for ln, form, _t in sites if ln not in expect] found.update({ln: (form, "note") for ln, form, _t in notes})
hits = sum(1 for ln, form in expect.items() if found.get(ln) == form) unexpected = [(ln, form) for ln, form, _t in residual if ln not in expect]
print(f"SWEEP-PLANTS found={hits}/6 in planted copy of {donor}") hits = sum(1 for ln, want in expect.items() if found.get(ln) == want)
if hits != 6: n_plants = len(PLANTS)
for ln, form in sorted(expect.items()): print(f"SWEEP-PLANTS found={hits}/{n_plants} in planted copy of {donor}")
got = found.get(ln, "<missed>") if hits != n_plants:
if got != form: for ln, want in sorted(expect.items()):
print(f"SWEEP-PLANT-MISS line {ln}: expected {form}, got {got}") got = found.get(ln, ("<missed>", "<missed>"))
if got != want:
print(f"SWEEP-PLANT-MISS line {ln}: expected {want}, got {got}")
bad += 1 bad += 1
if unexpected: if unexpected:
# the donor is a converted suite: any non-plant site the sweep finds # the donor is a converted suite: any non-plant RESIDUAL site in the
# in the copy contradicts the zero it just reported on the original # copy contradicts the zero leg 1 just reported on the original
# (non-plant notes mirror leg 1's treatment: printed there, not bad)
for ln, form in unexpected: for ln, form in unexpected:
print(f"SWEEP-PLANT-UNEXPECTED {donor}(copy):{ln} {form}") print(f"SWEEP-PLANT-UNEXPECTED {donor}(copy):{ln} {form}")
bad += 1 bad += 1
@@ -26,12 +26,12 @@
# #
# Verified guard per site (line numbers at branch tip, +3 header shift): # Verified guard per site (line numbers at branch tip, +3 header shift):
count_lines test-wake-beacon.sh:350 — red-path summary; guard `[ -s "$FAILFILE" ]` at line 349; template execution measured by microtest C11; text verified by static inventory count_lines test-wake-beacon.sh:354 — red-path summary; guard `[ -s "$FAILFILE" ]` at line 353; template execution measured by microtest C11; text verified by static inventory
count_lines test-wake-detector.sh:702 — red-path summary; guard `[ -s "$FAILFILE" ]` at line 701; template execution measured by microtest C11; text verified by static inventory count_lines test-wake-detector.sh:706 — red-path summary; guard `[ -s "$FAILFILE" ]` at line 705; template execution measured by microtest C11; text verified by static inventory
count_lines test-wake-digest-hmac.sh:434 — red-path summary; guard `[ -s "$FAILFILE" ]` at line 433; template execution measured by microtest C11; text verified by static inventory count_lines test-wake-digest-hmac.sh:438 — red-path summary; guard `[ -s "$FAILFILE" ]` at line 437; template execution measured by microtest C11; text verified by static inventory
count_lines test-wake-digest-quarantine.sh:584 — red-path summary; guard `[ -s "$FAILFILE" ]` at line 583; template execution measured by microtest C11; text verified by static inventory count_lines test-wake-digest-quarantine.sh:588 — red-path summary; guard `[ -s "$FAILFILE" ]` at line 587; template execution measured by microtest C11; text verified by static inventory
count_lines test-wake-fn-oracle.sh:132 — red-path summary; guard `[ -s "$FAILFILE" ]` at line 131; template execution measured by microtest C11; text verified by static inventory count_lines test-wake-fn-oracle.sh:136 — red-path summary; guard `[ -s "$FAILFILE" ]` at line 135; template execution measured by microtest C11; text verified by static inventory
count_lines test-wake-install.sh:434 — red-path summary; guard `[ -s "$FAILFILE" ]` at line 433; template execution measured by microtest C11; text verified by static inventory count_lines test-wake-install.sh:438 — red-path summary; guard `[ -s "$FAILFILE" ]` at line 437; template execution measured by microtest C11; text verified by static inventory
count_lines test-wake-reconcile.sh:389 — red-path summary; guard `[ -s "$FAILFILE" ]` at line 388; template execution measured by microtest C11; text verified by static inventory count_lines test-wake-reconcile.sh:393 — red-path summary; guard `[ -s "$FAILFILE" ]` at line 392; template execution measured by microtest C11; text verified by static inventory
count_lines test-wake-store-ack.sh:741 — red-path summary; guard `[ -s "$FAILFILE" ]` at line 740; template execution measured by microtest C11; text verified by static inventory count_lines test-wake-store-ack.sh:745 — red-path summary; guard `[ -s "$FAILFILE" ]` at line 744; template execution measured by microtest C11; text verified by static inventory
count_lines test-wake-store-enqueue-race.sh:208 — red-path summary (with "#927 TOCTOU reproduced (RED)" tail); guard `[ -s "$FAILFILE" ]` at line 207; template execution measured by microtest C11; text verified by static inventory count_lines test-wake-store-enqueue-race.sh:212 — red-path summary (with "#927 TOCTOU reproduced (RED)" tail); guard `[ -s "$FAILFILE" ]` at line 211; template execution measured by microtest C11; text verified by static inventory
@@ -4,8 +4,8 @@
# #
# 0. instrument self-test (microtest) — no validate evidence is trusted # 0. instrument self-test (microtest) — no validate evidence is trusted
# before the instrument itself has been proven, including its abort arms. # before the instrument itself has been proven, including its abort arms.
# 1. expected set: 261 coordinates from the FROZEN artifact (+3 header # 1. expected set: 261 coordinates from the FROZEN artifact (+7 header
# shift), count asserted against the number declared below BEFORE any # shift: 3 converter lines + 4 #984 guard lines), count asserted against the number declared below BEFORE any
# suite runs. # suite runs.
# 2. static inventory: converted call sites re-derived from SOURCE TEXT, # 2. static inventory: converted call sites re-derived from SOURCE TEXT,
# must equal the expected set exactly (amendment ONE, leg 1 — the # must equal the expected set exactly (amendment ONE, leg 1 — the
@@ -33,7 +33,8 @@
# site's ledger row must already be present (the append lands before the # site's ledger row must already be present (the append lands before the
# grep). # grep).
# 6. residual sweep: the denominator's own classifier finds zero unconverted # 6. residual sweep: the denominator's own classifier finds zero unconverted
# verdict greps in the suites — and six per-form plants in the same run. # verdict greps in the suites — and eight plants (six per-form + two
# absorb-branch probes, #985) in the same run.
# #
# Output discipline (A10): every line that reports on a suite names the file # Output discipline (A10): every line that reports on a suite names the file
# under test; exit codes are reported before failure counts. # under test; exit codes are reported before failure counts.
@@ -180,8 +181,15 @@ while read -r helper site form; do
bad="$bad no-ARMED-line" bad="$bad no-ARMED-line"
printf '%s\n' "$out" | grep -q "WAKE-ASSERT ABORT: ${helper} at ${site}: grep exit" || printf '%s\n' "$out" | grep -q "WAKE-ASSERT ABORT: ${helper} at ${site}: grep exit" ||
bad="$bad no-ABORT-line" bad="$bad no-ABORT-line"
printf '%s\n' "$out" | grep -Eq "$(sentinel_for "$f")" && # AND-polarity check (a match is the defect): a grep error (rc>=2) must be
bad="$bad sentinel-emitted" # its own loud arm — it cannot fall through as "no sentinel = pass".
rc_sent=0
printf '%s\n' "$out" | grep -Eq "$(sentinel_for "$f")" || rc_sent=$?
case "$rc_sent" in
0) bad="$bad sentinel-emitted" ;;
1) : ;;
*) bad="$bad sentinel-grep-error-rc=$rc_sent" ;;
esac
grep -q "^${helper} ${site}\$" "$aled" || grep -q "^${helper} ${site}\$" "$aled" ||
bad="$bad no-ledger-row" bad="$bad no-ledger-row"
if [ -z "$bad" ]; then if [ -z "$bad" ]; then
+1 -1
View File
@@ -25,7 +25,7 @@
"lint": "eslint src", "lint": "eslint src",
"typecheck": "tsc --noEmit", "typecheck": "tsc --noEmit",
"test": "vitest run --passWithNoTests && pnpm run test:framework-shell", "test": "vitest run --passWithNoTests && pnpm run test:framework-shell",
"test:framework-shell": "python3 src/lease-broker/daemon_deadline_unittest.py && python3 src/lease-broker/normative_fragments_unittest.py && python3 src/lease-broker/receipt_challenge_unittest.py && python3 src/lease-broker/context_recovery_unittest.py && python3 src/lease-broker/recovery_runtime_unittest.py && python3 src/lease-broker/recovery_b1_adversarial_unittest.py && python3 src/lease-broker/framework_skill_portability_unittest.py && python3 src/mutator-gate/runtime_tools_unittest.py && python3 src/mutator-gate/runtime_launch_guard_unittest.py && python3 src/mutator-gate/version_coupling_unittest.py && python3 framework/tools/lease-broker/check-runtime-launches.py --root ../.. && bash framework/tools/codex/test-pr-diff-context.sh && bash framework/tools/qa/test-deps-preflight.sh && bash framework/tools/git/test-pr-review-gitea-comment.sh && bash framework/tools/git/test-pr-review-repo-host-override.sh && bash framework/tools/git/test-ci-queue-wait-branch-absent.sh && bash framework/tools/git/test-git-credential-mosaic.sh && bash framework/tools/git/test-gitea-token-identity.sh && bash framework/tools/_scripts/test-install-ordering-guard.sh && bash framework/tools/tmux/agent-send.test.sh && bash framework/tools/wake/test-wake-store-ack.sh && bash framework/tools/wake/test-wake-store-enqueue-race.sh && bash framework/tools/wake/test-wake-digest-hmac.sh && bash framework/tools/wake/test-wake-digest-quarantine.sh && bash framework/tools/wake/test-wake-detector.sh && bash framework/tools/wake/test-wake-fn-oracle.sh && bash framework/tools/wake/test-wake-reconcile.sh && bash framework/tools/wake/test-wake-beacon.sh && bash framework/tools/wake/test-wake-preimage.sh && bash framework/tools/wake/test-wake-install.sh" "test:framework-shell": "bash framework/tools/quality/scripts/check-test-enumeration.sh && bash framework/tools/quality/scripts/test-check-test-enumeration.sh && python3 src/lease-broker/daemon_deadline_unittest.py && python3 src/lease-broker/normative_fragments_unittest.py && python3 src/lease-broker/receipt_challenge_unittest.py && python3 src/lease-broker/context_recovery_unittest.py && python3 src/lease-broker/recovery_runtime_unittest.py && python3 src/lease-broker/recovery_b1_adversarial_unittest.py && python3 src/lease-broker/framework_skill_portability_unittest.py && python3 src/mutator-gate/runtime_tools_unittest.py && python3 src/mutator-gate/runtime_launch_guard_unittest.py && python3 src/mutator-gate/version_coupling_unittest.py && python3 framework/tools/lease-broker/check-runtime-launches.py --root ../.. && bash framework/tools/codex/test-pr-diff-context.sh && bash framework/tools/qa/test-deps-preflight.sh && bash framework/tools/git/test-pr-review-gitea-comment.sh && bash framework/tools/git/test-pr-review-repo-host-override.sh && bash framework/tools/git/test-ci-queue-wait-branch-absent.sh && bash framework/tools/git/test-ci-queue-wait-parse.sh && bash framework/tools/git/test-git-credential-mosaic.sh && bash framework/tools/git/test-gitea-token-identity.sh && bash framework/tools/_scripts/test-install-ordering-guard.sh && bash framework/tools/tmux/agent-send.test.sh && bash framework/tools/wake/test-wake-store-ack.sh && bash framework/tools/wake/test-wake-store-enqueue-race.sh && bash framework/tools/wake/test-wake-digest-hmac.sh && bash framework/tools/wake/test-wake-digest-quarantine.sh && bash framework/tools/wake/test-wake-detector.sh && bash framework/tools/wake/test-wake-fn-oracle.sh && bash framework/tools/wake/test-wake-reconcile.sh && bash framework/tools/wake/test-wake-beacon.sh && bash framework/tools/wake/test-wake-preimage.sh && bash framework/tools/wake/test-wake-install.sh"
}, },
"dependencies": { "dependencies": {
"@mosaicstack/brain": "workspace:*", "@mosaicstack/brain": "workspace:*",