Compare commits
7
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
68bdeba758 | ||
|
|
5e98419d0e | ||
|
|
bd57ef0c74 | ||
|
|
8cb5866a3d | ||
|
|
10a7fe43c5 | ||
|
|
6109f81769 | ||
|
|
7833ddbf4e |
@@ -41,11 +41,6 @@ steps:
|
||||
# (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
|
||||
# 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
|
||||
# operator-owned path. The HARD GATE proves an unanticipated operator sentinel
|
||||
|
||||
@@ -34,18 +34,12 @@ EOF
|
||||
# get_remote_host and get_gitea_token are provided by detect-platform.sh
|
||||
|
||||
get_state_from_status_json() {
|
||||
# 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'
|
||||
python3 - <<'PY'
|
||||
import json
|
||||
import os
|
||||
import sys
|
||||
|
||||
try:
|
||||
payload = json.loads(os.environ.get("CI_QUEUE_STATUS_JSON", ""))
|
||||
payload = json.load(sys.stdin)
|
||||
except Exception:
|
||||
print("unknown")
|
||||
raise SystemExit(0)
|
||||
@@ -89,15 +83,12 @@ PY
|
||||
}
|
||||
|
||||
print_pending_contexts() {
|
||||
# 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'
|
||||
python3 - <<'PY'
|
||||
import json
|
||||
import os
|
||||
import sys
|
||||
|
||||
try:
|
||||
payload = json.loads(os.environ.get("CI_QUEUE_STATUS_JSON", ""))
|
||||
payload = json.load(sys.stdin)
|
||||
except Exception:
|
||||
print("[ci-queue-wait] unable to decode status payload")
|
||||
raise SystemExit(0)
|
||||
|
||||
@@ -109,55 +109,6 @@ else
|
||||
detect_platform >/dev/null
|
||||
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
|
||||
# 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
|
||||
@@ -199,7 +150,7 @@ print(json.dumps({"body": os.environ["COMMENT_BODY"]}))
|
||||
return 1
|
||||
fi
|
||||
if [[ "$write_status" != "201" ]]; then
|
||||
echo "Error: Gitea comment write failed with HTTP $write_status$(gitea_error_detail "$write_file")" >&2
|
||||
echo "Error: Gitea comment write failed with HTTP $write_status" >&2
|
||||
return 1
|
||||
fi
|
||||
|
||||
@@ -228,7 +179,7 @@ PY
|
||||
return 1
|
||||
fi
|
||||
if [[ "$readback_status" != "200" ]]; then
|
||||
echo "Error: Gitea comment read-back failed with HTTP $readback_status$(gitea_error_detail "$readback_file")" >&2
|
||||
echo "Error: Gitea comment read-back failed with HTTP $readback_status" >&2
|
||||
return 1
|
||||
fi
|
||||
|
||||
@@ -419,7 +370,7 @@ gitea_authenticated_login() {
|
||||
return 1
|
||||
fi
|
||||
if [[ "$status" != "200" ]]; then
|
||||
echo "Error: Gitea authenticated-identity read failed with HTTP $status$(gitea_error_detail "$response_file")" >&2
|
||||
echo "Error: Gitea authenticated-identity read failed with HTTP $status" >&2
|
||||
return 1
|
||||
fi
|
||||
|
||||
@@ -456,7 +407,7 @@ gitea_read_pr_head_into() {
|
||||
return 1
|
||||
fi
|
||||
if [[ "$status" != "200" ]]; then
|
||||
echo "Error: Gitea PR head read failed with HTTP $status$(gitea_error_detail "$pr_file")" >&2
|
||||
echo "Error: Gitea PR head read failed with HTTP $status" >&2
|
||||
return 1
|
||||
fi
|
||||
python3 - "$pr_file" <<'PY'
|
||||
@@ -546,7 +497,7 @@ print(json.dumps({
|
||||
fi
|
||||
# Gitea returns 200 (occasionally 201) with the created review object.
|
||||
if [[ "$write_status" != "200" && "$write_status" != "201" ]]; then
|
||||
echo "Error: Gitea review submit failed with HTTP $write_status$(gitea_error_detail "$write_file")" >&2
|
||||
echo "Error: Gitea review submit failed with HTTP $write_status (#865: no durable review created)" >&2
|
||||
return 1
|
||||
fi
|
||||
|
||||
@@ -575,7 +526,7 @@ PY
|
||||
return 1
|
||||
fi
|
||||
if [[ "$readback_status" != "200" ]]; then
|
||||
echo "Error: Gitea review read-back failed with HTTP $readback_status$(gitea_error_detail "$readback_file")" >&2
|
||||
echo "Error: Gitea review read-back failed with HTTP $readback_status" >&2
|
||||
return 1
|
||||
fi
|
||||
|
||||
|
||||
@@ -1,205 +0,0 @@
|
||||
#!/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,9 +58,6 @@ CREDENTIALS_FILE="$WORK_DIR/credentials.json"
|
||||
# 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.
|
||||
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() {
|
||||
rm -rf "$WORK_DIR"
|
||||
@@ -81,18 +78,9 @@ OVERRIDE_TOKEN="override-token-placeholder"
|
||||
CROSS_HOST_LOGIN="foreign-host-reviewer"
|
||||
CROSS_HOST_TOKEN="cross-host-token-placeholder"
|
||||
|
||||
mkdir -p "$REPO_DIR" "$BIN_DIR" "$XDG_DIR" "$STATE_DIR" "$TMP_SCRATCH" "$HOME_DIR"
|
||||
mkdir -p "$REPO_DIR" "$BIN_DIR" "$XDG_DIR" "$STATE_DIR" "$TMP_SCRATCH"
|
||||
git -C "$REPO_DIR" init -q
|
||||
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
|
||||
# name ("mosaicstack") is deliberately absent, so the no-override default path
|
||||
@@ -278,24 +266,6 @@ submitted = json.loads(os.environ["PR_REVIEW_PAYLOAD"])
|
||||
with open(state_path, encoding="utf-8") as 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
|
||||
# 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.
|
||||
@@ -547,8 +517,6 @@ run_review() {
|
||||
cd "$REPO_DIR"
|
||||
PATH="$BIN_DIR:$PATH" \
|
||||
TMPDIR="$TMP_SCRATCH" \
|
||||
HOME="$HOME_DIR" \
|
||||
MOSAIC_GIT_IDENTITY="" \
|
||||
XDG_CONFIG_HOME="$XDG_DIR" \
|
||||
MOSAIC_CREDENTIALS_FILE="$CREDENTIALS_FILE" \
|
||||
PR_REVIEW_TEA_LOG="$TEA_LOG" \
|
||||
@@ -972,44 +940,4 @@ if grep -q 'Approved and verified' "$OUTPUT_FILE"; then
|
||||
fi
|
||||
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"
|
||||
|
||||
@@ -1,165 +0,0 @@
|
||||
#!/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[@]}"
|
||||
@@ -1,166 +0,0 @@
|
||||
#!/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 ))
|
||||
@@ -1,45 +0,0 @@
|
||||
# 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,19 +529,7 @@ 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
|
||||
fi
|
||||
[ "$once" -eq 1 ] && break
|
||||
# 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>&-
|
||||
sleep "$interval"
|
||||
done
|
||||
}
|
||||
|
||||
|
||||
@@ -33,11 +33,7 @@ set -uo pipefail
|
||||
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.
|
||||
# shellcheck disable=SC1091
|
||||
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
|
||||
. "$SCRIPT_DIR/_wake-common.sh" && wake_assert_init
|
||||
BEACON="$SCRIPT_DIR/beacon.sh"
|
||||
|
||||
command -v jq >/dev/null 2>&1 || {
|
||||
|
||||
@@ -31,11 +31,7 @@ set -uo pipefail
|
||||
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.
|
||||
# shellcheck disable=SC1091
|
||||
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
|
||||
. "$SCRIPT_DIR/_wake-common.sh" && wake_assert_init
|
||||
DET="$SCRIPT_DIR/detector.sh"
|
||||
STORE="$SCRIPT_DIR/store.sh"
|
||||
|
||||
|
||||
@@ -37,11 +37,7 @@ set -uo pipefail
|
||||
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.
|
||||
# shellcheck disable=SC1091
|
||||
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
|
||||
. "$SCRIPT_DIR/_wake-common.sh" && wake_assert_init
|
||||
STORE="$SCRIPT_DIR/store.sh"
|
||||
DIGEST="$SCRIPT_DIR/digest.sh"
|
||||
SIGN="$SCRIPT_DIR/sign.sh"
|
||||
|
||||
@@ -119,11 +119,7 @@ set -uo pipefail
|
||||
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.
|
||||
# shellcheck disable=SC1091
|
||||
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
|
||||
. "$SCRIPT_DIR/_wake-common.sh" && wake_assert_init
|
||||
DIGEST="$SCRIPT_DIR/digest.sh"
|
||||
|
||||
command -v jq >/dev/null 2>&1 || {
|
||||
|
||||
@@ -27,11 +27,7 @@ set -uo pipefail
|
||||
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.
|
||||
# shellcheck disable=SC1091
|
||||
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
|
||||
. "$SCRIPT_DIR/_wake-common.sh" && wake_assert_init
|
||||
ORACLE="$SCRIPT_DIR/fn-oracle.sh"
|
||||
DET="$SCRIPT_DIR/detector.sh"
|
||||
|
||||
|
||||
@@ -36,11 +36,7 @@ set -uo pipefail
|
||||
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.
|
||||
# shellcheck disable=SC1091
|
||||
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
|
||||
. "$SCRIPT_DIR/_wake-common.sh" && wake_assert_init
|
||||
WI="$SCRIPT_DIR/wake-install.sh"
|
||||
BEACON="$SCRIPT_DIR/beacon.sh"
|
||||
FRAMEWORK_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)"
|
||||
|
||||
@@ -48,11 +48,7 @@ set -uo pipefail
|
||||
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.
|
||||
# shellcheck disable=SC1091
|
||||
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
|
||||
. "$SCRIPT_DIR/_wake-common.sh" && wake_assert_init
|
||||
PRE="$SCRIPT_DIR/preimage.sh"
|
||||
STORE="$SCRIPT_DIR/store.sh"
|
||||
DET="$SCRIPT_DIR/detector.sh"
|
||||
|
||||
@@ -31,11 +31,7 @@ set -uo pipefail
|
||||
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.
|
||||
# shellcheck disable=SC1091
|
||||
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
|
||||
. "$SCRIPT_DIR/_wake-common.sh" && wake_assert_init
|
||||
RECON="$SCRIPT_DIR/reconcile.sh"
|
||||
STORE="$SCRIPT_DIR/store.sh"
|
||||
DET="$SCRIPT_DIR/detector.sh"
|
||||
|
||||
@@ -44,11 +44,7 @@ set -uo pipefail
|
||||
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.
|
||||
# shellcheck disable=SC1091
|
||||
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
|
||||
. "$SCRIPT_DIR/_wake-common.sh" && wake_assert_init
|
||||
STORE="$SCRIPT_DIR/store.sh"
|
||||
ACK="$SCRIPT_DIR/ack.sh"
|
||||
|
||||
|
||||
@@ -34,11 +34,7 @@ set -uo pipefail
|
||||
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.
|
||||
# shellcheck disable=SC1091
|
||||
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
|
||||
. "$SCRIPT_DIR/_wake-common.sh" && wake_assert_init
|
||||
STORE="$SCRIPT_DIR/store.sh"
|
||||
|
||||
command -v jq >/dev/null 2>&1 || {
|
||||
|
||||
@@ -10,9 +10,8 @@ comparison come from independent code paths.
|
||||
Subcommands (all print sorted, stable output; non-zero exit on any failure):
|
||||
|
||||
expected The expected coordinate set from the ARTIFACT: one
|
||||
"<helper> <file>:<line+7>" row per denominator row (+7 = 3
|
||||
converter header lines + 4 lines from the #984 source guard,
|
||||
uniform across all ten suites).
|
||||
"<helper> <file>:<line+3>" row per denominator row (+3 = the
|
||||
uniform header shift the converter applied; converter-verified).
|
||||
Multi-grep lines stay ONE coordinate.
|
||||
|
||||
static The converted-site inventory from the SOURCE TEXT at the current
|
||||
@@ -24,9 +23,9 @@ Subcommands (all print sorted, stable output; non-zero exit on any failure):
|
||||
from the text.)
|
||||
|
||||
arms The forced-error arm list: the 19 denominator canaries plus one
|
||||
E-form arm (store-ack:733→740, a $(count_lines) capture compared
|
||||
afterward — the A6 shape) plus one F-form arm (quarantine:560→567,
|
||||
the multi-grep pipeline capture), as "<helper> <file>:<line+7>
|
||||
E-form arm (store-ack:733→736, a $(count_lines) capture compared
|
||||
afterward — the A6 shape) plus one F-form arm (quarantine:560→563,
|
||||
the multi-grep pipeline capture), as "<helper> <file>:<line+3>
|
||||
<form>". Both extras are asserted to exist in the artifact with
|
||||
the expected form — a renumber that moved them fails here, not
|
||||
silently downstream.
|
||||
@@ -34,8 +33,7 @@ Subcommands (all print sorted, stable output; non-zero exit on any failure):
|
||||
sweep Residual sweep: the denominator's own classifier (ported from the
|
||||
frozen derivation) over the ten suites at the current tree must
|
||||
find ZERO unconverted verdict-form grep sites; and, IN THE SAME
|
||||
RUN, eight specimens (six per-form + two absorb-branch probes, #985)
|
||||
planted into a temp copy of a real
|
||||
RUN, six per-form specimens planted into a temp copy of a real
|
||||
suite must ALL be found with their correct forms — an instrument
|
||||
that reports zero must first be seen finding what it claims to
|
||||
find (A5).
|
||||
@@ -52,9 +50,7 @@ HERE = Path(__file__).resolve().parent
|
||||
WAKE = HERE.parent
|
||||
ART = HERE / "denominator-089615f.json"
|
||||
|
||||
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.
|
||||
HEADER_SHIFT = 3 # converter inserted 3 header lines after SCRIPT_DIR in every suite
|
||||
|
||||
# The two hand-picked extra arms (base coordinates; forms asserted at load).
|
||||
EXTRA_ARMS = [
|
||||
@@ -72,14 +68,10 @@ RX_ASSIGN_SUB = re.compile(r'=\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_.-]|$)")
|
||||
|
||||
# grep in COMMAND position: at line start or after a command separator / subshell
|
||||
# opener / shell keyword / `!`. Quote-unaware by design — a quoted "grep" after a
|
||||
# separator reads as a command and lands the line in residual, which fails LOUD;
|
||||
# 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 polarity(line):
|
||||
m = RX_FAIL_SAME.search(line)
|
||||
return "OR" if m.group(1) == "||" else "AND"
|
||||
|
||||
|
||||
def classify(lines):
|
||||
@@ -145,28 +137,6 @@ def classify(lines):
|
||||
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():
|
||||
art = json.loads(ART.read_text())
|
||||
assert art["total"] == 261 == len(art["rows"]), "artifact self-consistency"
|
||||
@@ -229,20 +199,13 @@ def cmd_arms():
|
||||
return 0
|
||||
|
||||
|
||||
# (expected classify form, expected disposition through residual_sites, snippet)
|
||||
PLANTS = [
|
||||
("A-same-line", "residual", ['grep -q needle haystack || fail "plant-A"']),
|
||||
("B-cont-operator", "residual", ["grep -q needle haystack ||", ' fail "plant-B"']),
|
||||
("C-cont-backslash", "residual", ["grep -q needle \\", ' haystack || fail "plant-C"']),
|
||||
("D-if-form", "residual", ["if ! grep -q needle haystack; then", ' fail "plant-D"', "fi"]),
|
||||
("E-count-capture", "residual", ['[ "$(grep -c needle haystack)" = "1" ] || fail "plant-E"']),
|
||||
("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"']),
|
||||
("A-same-line", ['grep -q needle haystack || fail "plant-A"']),
|
||||
("B-cont-operator", ["grep -q needle haystack ||", ' fail "plant-B"']),
|
||||
("C-cont-backslash", ["grep -q needle \\", ' haystack || fail "plant-C"']),
|
||||
("D-if-form", ["if ! grep -q needle haystack; then", ' fail "plant-D"', "fi"]),
|
||||
("E-count-capture", ['[ "$(grep -c needle haystack)" = "1" ] || fail "plant-E"']),
|
||||
("F-extract-capture", ['val="$(grep needle haystack)"']),
|
||||
]
|
||||
|
||||
|
||||
@@ -252,20 +215,22 @@ def cmd_sweep():
|
||||
|
||||
# leg 1: real suites at the current tree must be residual-free
|
||||
for f in suite_files(art):
|
||||
residual, notes, _dispo = residual_sites((WAKE / f).read_text().split("\n"))
|
||||
for ln, form, text in notes:
|
||||
# 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]}")
|
||||
lines = (WAKE / f).read_text().split("\n")
|
||||
sites, _dispo = classify(lines)
|
||||
residual = []
|
||||
for ln, form, text in sites:
|
||||
if RX_HELPER.search(lines[ln - 1]):
|
||||
# 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:
|
||||
print(f"SWEEP-RESIDUAL {f}:{ln} {form}: {text[:100]}")
|
||||
bad += 1
|
||||
print(f"SWEEP {f}: {len(residual)} residual verdict site(s)")
|
||||
|
||||
# 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()
|
||||
# leg 2, SAME RUN: the instrument must find six per-form plants
|
||||
donor = suite_files(art)[0]
|
||||
with tempfile.TemporaryDirectory() as td:
|
||||
planted = Path(td) / donor
|
||||
@@ -273,28 +238,25 @@ def cmd_sweep():
|
||||
base_lines = planted.read_text().split("\n")
|
||||
offset = len(base_lines)
|
||||
expect = {}
|
||||
for form, dispo, snippet in PLANTS:
|
||||
expect[offset + 1] = (form, dispo) # first physical line of each plant
|
||||
for form, snippet in PLANTS:
|
||||
expect[offset + 1] = form # first physical line of each plant
|
||||
base_lines.extend(snippet)
|
||||
offset = len(base_lines)
|
||||
planted.write_text("\n".join(base_lines))
|
||||
residual, notes, _ = residual_sites(planted.read_text().split("\n"))
|
||||
found = {ln: (form, "residual") for ln, form, _t in residual}
|
||||
found.update({ln: (form, "note") for ln, form, _t in notes})
|
||||
unexpected = [(ln, form) for ln, form, _t in residual if ln not in expect]
|
||||
hits = sum(1 for ln, want in expect.items() if found.get(ln) == want)
|
||||
n_plants = len(PLANTS)
|
||||
print(f"SWEEP-PLANTS found={hits}/{n_plants} in planted copy of {donor}")
|
||||
if hits != n_plants:
|
||||
for ln, want in sorted(expect.items()):
|
||||
got = found.get(ln, ("<missed>", "<missed>"))
|
||||
if got != want:
|
||||
print(f"SWEEP-PLANT-MISS line {ln}: expected {want}, got {got}")
|
||||
sites, _ = classify(planted.read_text().split("\n"))
|
||||
found = {ln: form for ln, form, _t in sites if ln in expect}
|
||||
unexpected = [(ln, form) for ln, form, _t in sites if ln not in expect]
|
||||
hits = sum(1 for ln, form in expect.items() if found.get(ln) == form)
|
||||
print(f"SWEEP-PLANTS found={hits}/6 in planted copy of {donor}")
|
||||
if hits != 6:
|
||||
for ln, form in sorted(expect.items()):
|
||||
got = found.get(ln, "<missed>")
|
||||
if got != form:
|
||||
print(f"SWEEP-PLANT-MISS line {ln}: expected {form}, got {got}")
|
||||
bad += 1
|
||||
if unexpected:
|
||||
# the donor is a converted suite: any non-plant RESIDUAL site in the
|
||||
# copy contradicts the zero leg 1 just reported on the original
|
||||
# (non-plant notes mirror leg 1's treatment: printed there, not bad)
|
||||
# the donor is a converted suite: any non-plant site the sweep finds
|
||||
# in the copy contradicts the zero it just reported on the original
|
||||
for ln, form in unexpected:
|
||||
print(f"SWEEP-PLANT-UNEXPECTED {donor}(copy):{ln} {form}")
|
||||
bad += 1
|
||||
|
||||
@@ -26,12 +26,12 @@
|
||||
#
|
||||
# Verified guard per site (line numbers at branch tip, +3 header shift):
|
||||
|
||||
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: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: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: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: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: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: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: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: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
|
||||
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-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-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-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-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-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-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-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-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
|
||||
|
||||
@@ -4,8 +4,8 @@
|
||||
#
|
||||
# 0. instrument self-test (microtest) — no validate evidence is trusted
|
||||
# before the instrument itself has been proven, including its abort arms.
|
||||
# 1. expected set: 261 coordinates from the FROZEN artifact (+7 header
|
||||
# shift: 3 converter lines + 4 #984 guard lines), count asserted against the number declared below BEFORE any
|
||||
# 1. expected set: 261 coordinates from the FROZEN artifact (+3 header
|
||||
# shift), count asserted against the number declared below BEFORE any
|
||||
# suite runs.
|
||||
# 2. static inventory: converted call sites re-derived from SOURCE TEXT,
|
||||
# must equal the expected set exactly (amendment ONE, leg 1 — the
|
||||
@@ -33,8 +33,7 @@
|
||||
# site's ledger row must already be present (the append lands before the
|
||||
# grep).
|
||||
# 6. residual sweep: the denominator's own classifier finds zero unconverted
|
||||
# verdict greps in the suites — and eight plants (six per-form + two
|
||||
# absorb-branch probes, #985) in the same run.
|
||||
# verdict greps in the suites — and six per-form plants in the same run.
|
||||
#
|
||||
# Output discipline (A10): every line that reports on a suite names the file
|
||||
# under test; exit codes are reported before failure counts.
|
||||
@@ -181,15 +180,8 @@ while read -r helper site form; do
|
||||
bad="$bad no-ARMED-line"
|
||||
printf '%s\n' "$out" | grep -q "WAKE-ASSERT ABORT: ${helper} at ${site}: grep exit" ||
|
||||
bad="$bad no-ABORT-line"
|
||||
# AND-polarity check (a match is the defect): a grep error (rc>=2) must be
|
||||
# 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
|
||||
printf '%s\n' "$out" | grep -Eq "$(sentinel_for "$f")" &&
|
||||
bad="$bad sentinel-emitted"
|
||||
grep -q "^${helper} ${site}\$" "$aled" ||
|
||||
bad="$bad no-ledger-row"
|
||||
if [ -z "$bad" ]; then
|
||||
|
||||
@@ -25,7 +25,7 @@
|
||||
"lint": "eslint src",
|
||||
"typecheck": "tsc --noEmit",
|
||||
"test": "vitest run --passWithNoTests && pnpm run test:framework-shell",
|
||||
"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"
|
||||
"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"
|
||||
},
|
||||
"dependencies": {
|
||||
"@mosaicstack/brain": "workspace:*",
|
||||
|
||||
Reference in New Issue
Block a user