Skip to content

Commit ccce312

Browse files
committed
fix(wave0): kill false-green validators; make every check able to fail
Holes-before-goals pass on the enforcement surface: several validators reported success while checking nothing, or masked real errors behind `|| true`. Each is now state-aware and provably able to fail, with a permanent regression test that exercises both the pass and fail paths (a check that cannot fail is not a check). - a2ml/scripts/check-6scm.sh: no longer exits 0 vacuously when the .scm sources have migrated away. Distinguishes obsolete-no-op (retired, superseded by 6a2/descriptiles), orphan-mirror DRIFT (fails), and out-of-sync mirror (fails). - .github/workflows/boj-build.yml: replace the "K9 validation would run here" placeholder with real structural validation via new scripts/check-mustfile-structure.sh (every Mustfile check must carry a severity and a means of discharge — `- run:` or `- verification:`; hollow checks fail loudly). - rhodium-standard-repositories/rsr-audit.sh: fix argument parsing (standards#387) so the documented `--format json` works and an invalid format errors (exit 4) instead of silently defaulting to text; keep the bare-positional form for backward compatibility. - Justfile `validate`: drop the blanket `|| true` on the RSR self-audit. Via scripts/rsr-selfaudit.sh a low grade stays informational/non-blocking (a monorepo is not expected to score Gold) but a broken audit (exit 4) now fails validate loudly. registry-check remains the hard gate. - audit-contractiles.sh: remove hardcoded /var/mnt/eclipse owner-machine paths; take repos as args or $CONTRACTILE_AUDIT_REPOS, default to self, and never silently audit zero repos. Fix a corrupted box-drawing char. - scripts/tests/wave0-false-green-test.sh: 13 assertions covering the pass + fail path of each fixed validator; Justfile recipes `false-green-test` and `mustfile-check`. Licence/SPDX untouched (flag-only policy). continue-on-error soft-gates left in place (documented, with real blocking equivalents) — honest labelling / promotion tracked for Wave 1. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0114ps6mY5jAH4SzbGxeuYjc
1 parent a44c93a commit ccce312

8 files changed

Lines changed: 293 additions & 28 deletions

File tree

.github/workflows/boj-build.yml

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -66,15 +66,14 @@ jobs:
6666

6767
- name: K9-SVC Validation
6868
run: |
69+
set -euo pipefail
6970
echo "Running K9-SVC contractile validation..."
70-
if [ -f .machine_readable/contractiles/must/Mustfile.a2ml ]; then
71-
echo "✅ Mustfile found - running validation"
72-
# Placeholder for actual K9 validation
73-
echo "K9 validation would run here"
74-
else
75-
echo "❌ Mustfile not found"
76-
exit 1
77-
fi
71+
# Real structural validation (no placeholder): every Mustfile check
72+
# must carry a severity and a means of discharge (run or verification).
73+
# A hollow check fails loudly. See scripts/check-mustfile-structure.sh
74+
# (unit-tested by scripts/tests/wave0-false-green-test.sh). Executable
75+
# invocation of `- run:` checks arrives with the Wave-1 must-verify job.
76+
bash scripts/check-mustfile-structure.sh
7877
7978
- name: Contractile Check
8079
run: |

Justfile

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -41,11 +41,23 @@ staleness-test:
4141
@echo "=== propagate-workflow-pins ==="
4242
@bash scripts/tests/propagate-workflow-pins-test.sh
4343

44-
# Aggregate compliance gate: registry drift (hard dep) + RSR self-audit (informational)
44+
# Wave-0 anti-false-green regression: proves each fixed validator CAN fail
45+
false-green-test:
46+
@bash scripts/tests/wave0-false-green-test.sh
47+
48+
# Structural validation of the Mustfile contract (severity + run/verification per check)
49+
mustfile-check path=".machine_readable/contractiles/must/Mustfile.a2ml":
50+
@bash scripts/check-mustfile-structure.sh "{{path}}"
51+
52+
# Aggregate compliance gate: registry drift is the HARD gate (registry-check,
53+
# a hard dep). The RSR self-audit is INFORMATIONAL — a monorepo is not expected
54+
# to score Gold — but a *broken* audit (exit 4 / unexpected) must fail loudly
55+
# rather than pass silently under a blanket `|| true` (Wave-0 false-green fix).
4556
validate: registry-check
46-
@echo "=== validate: RSR compliance gate ==="
47-
@bash rhodium-standard-repositories/rsr-audit.sh . text || true
48-
@echo "=== validate: done (see rsr-audit output above) ==="
57+
@echo "=== validate: registry drift (HARD GATE) — passed as a dependency above ==="
58+
@echo "=== validate: RSR self-audit (INFORMATIONAL grade; errors fail loudly) ==="
59+
@bash scripts/rsr-selfaudit.sh .
60+
@echo "=== validate: done ==="
4961

5062
# Print role-appropriate LLM warm-up context (machine front door)
5163
llm-context role="dev":

a2ml/scripts/check-6scm.sh

Lines changed: 52 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,57 @@
11
#!/usr/bin/env bash
2+
# SPDX-License-Identifier: MPL-2.0
3+
#
4+
# check-6scm.sh — verify the .machine_readable/6scm/ mirror is honest.
5+
#
6+
# The 6scm mirror duplicated the legacy Scheme (.scm) descriptor set into
7+
# .machine_readable/6scm/ so tools that only read Scheme had a copy. That
8+
# metadata has migrated to .machine_readable/6a2/ (A2ML / descriptiles), so
9+
# for most repos there are no .scm sources left to mirror.
10+
#
11+
# A validator that validates nothing must not report success. This script
12+
# therefore distinguishes three states LOUDLY (never a silent green):
13+
#
14+
# 1. Sources present -> real diff check; fail on missing/out-of-sync mirror.
15+
# 2. No sources, no mirror -> OBSOLETE no-op: exit 0 with an explicit
16+
# "retired" message (it does NOT claim "in sync").
17+
# 3. No sources, but an orphaned mirror with files -> DRIFT: fail loudly.
18+
#
19+
# See CANONICAL-NAMES.adoc (6a2/descriptiles supersedes the legacy set) and
20+
# standards Wave-0 "kill the false green" remediation.
21+
222
set -euo pipefail
323

24+
SCM_NAMES=(AGENTIC.scm ECOSYSTEM.scm META.scm NEUROSYM.scm PLAYBOOK.scm STATE.scm)
25+
MIRROR_DIR=".machine_readable/6scm"
26+
27+
# Collect the sources that actually exist.
28+
sources=()
29+
for f in "${SCM_NAMES[@]}"; do
30+
[ -f ".machine_readable/$f" ] && sources+=("$f")
31+
done
32+
33+
# State 2/3: no sources to mirror — the mechanism is obsolete for this repo.
34+
if [ "${#sources[@]}" -eq 0 ]; then
35+
orphans=0
36+
if [ -d "$MIRROR_DIR" ]; then
37+
while IFS= read -r _; do orphans=$((orphans + 1)); done \
38+
< <(find "$MIRROR_DIR" -type f 2>/dev/null)
39+
fi
40+
if [ "$orphans" -gt 0 ]; then
41+
echo "DRIFT: $MIRROR_DIR holds $orphans mirror file(s) but no .scm sources exist." >&2
42+
echo " The 6scm mirror is obsolete (metadata migrated to .machine_readable/6a2/)." >&2
43+
echo " Remove $MIRROR_DIR and retire the sync-6scm/check-6scm recipes." >&2
44+
exit 1
45+
fi
46+
echo "OBSOLETE (no-op): no .scm sources; 6scm mirror retired (superseded by 6a2/descriptiles). Nothing to mirror."
47+
exit 0
48+
fi
49+
50+
# State 1: sources exist — the mirror must be present and byte-identical.
451
missing=0
5-
for f in AGENTIC.scm ECOSYSTEM.scm META.scm NEUROSYM.scm PLAYBOOK.scm STATE.scm; do
52+
for f in "${sources[@]}"; do
653
src=".machine_readable/$f"
7-
dst=".machine_readable/6scm/$f"
8-
if [ ! -f "$src" ]; then
9-
continue
10-
fi
54+
dst="$MIRROR_DIR/$f"
1155
if [ ! -f "$dst" ]; then
1256
echo "Missing mirror: $dst" >&2
1357
missing=1
@@ -19,4 +63,7 @@ for f in AGENTIC.scm ECOSYSTEM.scm META.scm NEUROSYM.scm PLAYBOOK.scm STATE.scm;
1963
fi
2064
done
2165

66+
if [ "$missing" -eq 0 ]; then
67+
echo "6scm mirror in sync (${#sources[@]} source(s) verified)."
68+
fi
2269
exit $missing

audit-contractiles.sh

Lines changed: 20 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -8,17 +8,28 @@ set -euo pipefail
88
echo "═══════════════════════════════════════════════════════════════════════════════"
99
echo " Hyperpolymath Contractile System Audit"
1010
echo " $(date '+%Y-%m-%d %H:%M:%S')"
11-
echo "════╕══════════════════════════════════════════════════════════════════════════════"
11+
echo "═══════════════════════════════════════════════════════════════════════════════"
1212
echo ""
1313

14-
# Repositories to audit
15-
REPOS=(
16-
"/var/mnt/eclipse/repos/burble"
17-
"/var/mnt/eclipse/repos/panll"
18-
"/var/mnt/eclipse/repos/nextgen-databases"
19-
"/var/mnt/eclipse/repos/rescript"
20-
"/var/mnt/eclipse/repos/standards"
21-
)
14+
# Repositories to audit.
15+
# Previously these were hardcoded to the owner's machine (/var/mnt/eclipse/...),
16+
# so the script could not run in CI or on any other host (Wave-0 fix). Supply
17+
# repos explicitly as positional args, or via $CONTRACTILE_AUDIT_REPOS
18+
# (colon-separated). With neither set, default to auditing THIS repo so a bare
19+
# invocation is still useful — and never silently audit zero repos.
20+
if [ "$#" -gt 0 ]; then
21+
REPOS=("$@")
22+
elif [ -n "${CONTRACTILE_AUDIT_REPOS:-}" ]; then
23+
IFS=':' read -r -a REPOS <<< "$CONTRACTILE_AUDIT_REPOS"
24+
else
25+
SELF_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
26+
REPOS=("$SELF_DIR")
27+
fi
28+
29+
if [ "${#REPOS[@]}" -eq 0 ]; then
30+
echo "error: no repositories to audit (pass paths as args or set CONTRACTILE_AUDIT_REPOS)" >&2
31+
exit 2
32+
fi
2233

2334
# Contractile types to check
2435
# `lust` deprecated 2026-04-18 — wishes absorbed into intend/Intentfile.a2ml

rhodium-standard-repositories/rsr-audit.sh

Lines changed: 28 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,8 +30,34 @@ RSR_FREEZE_DATE="2025-12-27"
3030
# Configuration
3131
# =============================================================================
3232

33-
REPO_PATH="${1:-.}"
34-
OUTPUT_FORMAT="${2:-text}"
33+
# Argument parsing (standards#387).
34+
# Historically the second positional arg was read verbatim as the format, so the
35+
# DOCUMENTED `--format json` form silently fell through to the text default while
36+
# only the bare positional `text|json|html` worked. Accept both now, and reject
37+
# an unknown format loudly (exit 4) instead of silently defaulting.
38+
REPO_PATH="."
39+
OUTPUT_FORMAT="text"
40+
_repo_seen=0
41+
while [[ $# -gt 0 ]]; do
42+
case "$1" in
43+
--format=*)
44+
OUTPUT_FORMAT="${1#*=}"; shift ;;
45+
--format)
46+
OUTPUT_FORMAT="${2:-}"; shift 2 || { echo "error: --format needs a value" >&2; exit 4; } ;;
47+
text|json|html)
48+
# Backward-compatible bare positional format (e.g. Justfile passes `. text`).
49+
OUTPUT_FORMAT="$1"; shift ;;
50+
-*)
51+
echo "error: unknown option: $1" >&2; exit 4 ;;
52+
*)
53+
if [[ $_repo_seen -eq 0 ]]; then REPO_PATH="$1"; _repo_seen=1; shift
54+
else echo "error: unexpected argument: $1" >&2; exit 4; fi ;;
55+
esac
56+
done
57+
case "$OUTPUT_FORMAT" in
58+
text|json|html) ;;
59+
*) echo "error: invalid --format '$OUTPUT_FORMAT' (want: text|json|html)" >&2; exit 4 ;;
60+
esac
3561
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
3662

3763
# Compliance thresholds
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
#!/usr/bin/env bash
2+
# SPDX-License-Identifier: MPL-2.0
3+
#
4+
# check-mustfile-structure.sh — structural validation of a Mustfile.a2ml.
5+
#
6+
# A Mustfile declares hard repository invariants as '### <id>' blocks. Every
7+
# declared check MUST carry:
8+
# * a `- severity:` field, AND
9+
# * a means of discharge — either an executable `- run:` command OR an
10+
# explicit `- verification:` (governance / manually-verified) field.
11+
#
12+
# A check that declares neither run nor verification is a hollow assertion: it
13+
# looks like enforcement but discharges nothing. This validator fails loudly on
14+
# such a check rather than letting it pass as green. It is the structural half
15+
# of Mustfile checking; scripts/run-mustfile.sh (Wave 1) executes the `- run:`
16+
# commands.
17+
#
18+
# Usage: check-mustfile-structure.sh [path/to/Mustfile.a2ml]
19+
# Default path: .machine_readable/contractiles/must/Mustfile.a2ml
20+
# Exit: 0 valid · 1 malformed/hollow · 2 file not found
21+
22+
set -euo pipefail
23+
24+
MUST="${1:-.machine_readable/contractiles/must/Mustfile.a2ml}"
25+
26+
if [ ! -f "$MUST" ]; then
27+
echo "error: Mustfile not found: $MUST" >&2
28+
exit 2
29+
fi
30+
31+
awk '
32+
function flush() {
33+
if (name == "") return
34+
if (!(has_run || has_ver)) { printf " ❌ %s: no `- run:` or `- verification:` (hollow check)\n", name; fails++ }
35+
if (!has_sev) { printf " ❌ %s: no `- severity:`\n", name; fails++ }
36+
}
37+
/^### / { flush(); name=$0; has_run=0; has_ver=0; has_sev=0; checks++; next }
38+
/^[[:space:]]*-[[:space:]]*run:/ { has_run=1 }
39+
/^[[:space:]]*-[[:space:]]*verification:/ { has_ver=1 }
40+
/^[[:space:]]*-[[:space:]]*severity:/ { has_sev=1 }
41+
END {
42+
flush()
43+
if (checks < 1) { print "❌ Mustfile declares no checks (expected one or more \"### <id>\" blocks)"; exit 1 }
44+
if (fails > 0) { printf "❌ Malformed Mustfile: %d structural problem(s) across %d checks\n", fails, checks; exit 1 }
45+
printf "✅ Mustfile structurally valid (%d checks, each with severity + run/verification)\n", checks
46+
}
47+
' "$MUST"

scripts/rsr-selfaudit.sh

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
#!/usr/bin/env bash
2+
# SPDX-License-Identifier: MPL-2.0
3+
#
4+
# rsr-selfaudit.sh — run the RSR self-audit as an INFORMATIONAL grade, but fail
5+
# loudly if the audit tool itself errors.
6+
#
7+
# rsr-audit.sh encodes the grade in its exit code: 0=Gold 1=Silver 2=Bronze
8+
# 3=Non-compliant 4=error. This monorepo is not expected to score Gold, so a
9+
# low grade is informational and non-blocking. What must NOT happen is a broken
10+
# audit (exit 4, or an unexpected code) sliding through as green — the previous
11+
# `rsr-audit.sh … || true` swallowed exactly that. Here a grade is reported and
12+
# returns 0; only a genuine audit error returns non-zero.
13+
#
14+
# Usage: rsr-selfaudit.sh [repo-path] (default: .)
15+
16+
set -uo pipefail
17+
18+
REPO="${1:-.}"
19+
SELF_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
20+
AUDIT="$SELF_DIR/../rhodium-standard-repositories/rsr-audit.sh"
21+
22+
bash "$AUDIT" "$REPO" text
23+
rc=$?
24+
case "$rc" in
25+
0) echo " -> RSR self-audit: Gold (100%)" ;;
26+
1) echo " -> RSR self-audit: Silver (informational, non-blocking)" ;;
27+
2) echo " -> RSR self-audit: Bronze (informational, non-blocking)" ;;
28+
3) echo " -> RSR self-audit: Non-compliant (informational, non-blocking)" ;;
29+
*) echo " -> RSR self-audit ERRORED (exit $rc) — failing loudly" >&2; exit 1 ;;
30+
esac
Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
#!/usr/bin/env bash
2+
# SPDX-License-Identifier: MPL-2.0
3+
set -uo pipefail
4+
#
5+
# Wave-0 "kill the false green" regression test.
6+
#
7+
# Every validator fixed in Wave 0 must be able to FAIL — a check that cannot
8+
# fail is not a check. This exercises each one's success AND failure path so a
9+
# future regression that reintroduces a vacuous pass is caught here.
10+
#
11+
# Covers:
12+
# * a2ml/scripts/check-6scm.sh (obsolete no-op / orphan drift / out-of-sync)
13+
# * scripts/check-mustfile-structure.sh (valid Mustfile / hollow check)
14+
# * rhodium-standard-repositories/rsr-audit.sh (bad format exits 4 / --format json works)
15+
# * audit-contractiles.sh (loud error on zero repos)
16+
17+
ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
18+
TMP="$(mktemp -d)"
19+
trap 'rm -rf "$TMP"' EXIT
20+
21+
pass=0 fail=0
22+
ok() { echo "$1"; pass=$((pass + 1)); }
23+
bad() { echo "$1"; fail=$((fail + 1)); }
24+
# expect <wanted-code> <actual-code> <label>
25+
expect() { if [ "$2" -eq "$1" ]; then ok "$3 (exit $2)"; else bad "$3 (wanted $1, got $2)"; fi; }
26+
27+
echo "== check-6scm.sh =="
28+
SIX="$ROOT/a2ml/scripts/check-6scm.sh"
29+
# (1) obsolete no-op: no sources, no mirror -> exit 0
30+
w="$TMP/six-obsolete"; mkdir -p "$w/.machine_readable/6a2"; cp "$SIX" "$w/check.sh"
31+
( cd "$w" && bash check.sh >/dev/null 2>&1 ); expect 0 $? "obsolete no-op passes"
32+
# (2) orphan drift: mirror files but no sources -> exit 1
33+
w="$TMP/six-orphan"; mkdir -p "$w/.machine_readable/6scm"; echo x > "$w/.machine_readable/6scm/STATE.scm"; cp "$SIX" "$w/check.sh"
34+
( cd "$w" && bash check.sh >/dev/null 2>&1 ); expect 1 $? "orphan-mirror drift fails"
35+
# (3) out-of-sync: source present, mirror differs -> exit 1
36+
w="$TMP/six-desync"; mkdir -p "$w/.machine_readable/6scm"
37+
printf 'a\n' > "$w/.machine_readable/STATE.scm"; printf 'b\n' > "$w/.machine_readable/6scm/STATE.scm"; cp "$SIX" "$w/check.sh"
38+
( cd "$w" && bash check.sh >/dev/null 2>&1 ); expect 1 $? "out-of-sync mirror fails"
39+
# (4) in-sync: source present, mirror identical -> exit 0
40+
w="$TMP/six-sync"; mkdir -p "$w/.machine_readable/6scm"
41+
printf 'a\n' > "$w/.machine_readable/STATE.scm"; printf 'a\n' > "$w/.machine_readable/6scm/STATE.scm"; cp "$SIX" "$w/check.sh"
42+
( cd "$w" && bash check.sh >/dev/null 2>&1 ); expect 0 $? "in-sync mirror passes"
43+
44+
echo "== check-mustfile-structure.sh =="
45+
MS="$ROOT/scripts/check-mustfile-structure.sh"
46+
# valid: the repo's real Mustfile -> exit 0
47+
bash "$MS" "$ROOT/.machine_readable/contractiles/must/Mustfile.a2ml" >/dev/null 2>&1
48+
expect 0 $? "real Mustfile is structurally valid"
49+
# hollow: a check with neither run nor verification -> exit 1
50+
hf="$TMP/hollow.a2ml"; printf '### hollow\n- description: nothing runnable\n- severity: high\n' > "$hf"
51+
bash "$MS" "$hf" >/dev/null 2>&1; expect 1 $? "hollow check fails"
52+
# governance: a check with verification (no run) is accepted -> exit 0
53+
gf="$TMP/gov.a2ml"; printf '### gov\n- description: manual\n- verification: reviewed by owner\n- severity: high\n' > "$gf"
54+
bash "$MS" "$gf" >/dev/null 2>&1; expect 0 $? "verification-only check accepted"
55+
# missing file -> exit 2
56+
bash "$MS" "$TMP/nope.a2ml" >/dev/null 2>&1; expect 2 $? "missing Mustfile errors"
57+
58+
echo "== rsr-audit.sh (standards#387 arg parsing) =="
59+
RSR="$ROOT/rhodium-standard-repositories/rsr-audit.sh"
60+
# bad format -> exit 4 (no longer silently defaults to text)
61+
bash "$RSR" . --format xml >/dev/null 2>&1; expect 4 $? "invalid --format errors loudly"
62+
# documented --format json now produces JSON. Capture first: rsr-audit's exit
63+
# code encodes the grade (non-zero for < Gold), so piping into grep under
64+
# `pipefail` would mask a successful match — assert on the captured text.
65+
json_out="$(bash "$RSR" . --format json 2>/dev/null || true)"
66+
case "$json_out" in
67+
*'"compliance_level"'*) ok "--format json emits JSON report" ;;
68+
*) bad "--format json did not emit JSON" ;;
69+
esac
70+
# bare positional 'text' (Justfile backward-compat) still works: grade code 0-3
71+
bash "$RSR" . text >/dev/null 2>&1; rc=$?
72+
if [ "$rc" -ge 0 ] && [ "$rc" -le 3 ]; then ok "bare positional 'text' returns a grade code ($rc)"; else bad "bare positional 'text' returned $rc"; fi
73+
74+
echo "== audit-contractiles.sh =="
75+
AC="$ROOT/audit-contractiles.sh"
76+
# Runs against an explicit repo path with no hardcoded owner (/var/mnt/...) paths.
77+
# Capture the output first (avoid pipefail masking the script's own exit code).
78+
ac_out="$(bash "$AC" "$ROOT" 2>&1 || true)"
79+
case "$ac_out" in
80+
*"Contractile System Audit"*) ok "runs against an explicit repo path (no hardcoded owner paths)" ;;
81+
*) bad "did not run against explicit repo path" ;;
82+
esac
83+
# No hardcoded owner path is USED (a quoted absolute /var/mnt/... array element);
84+
# a prose mention in a comment is fine, an actual code path is not.
85+
if grep -qE '^[[:space:]]*"/var/mnt/' "$AC"; then
86+
bad "hardcoded owner path still used in code"
87+
else
88+
ok "no hardcoded owner path used in code"
89+
fi
90+
91+
echo
92+
echo "Wave-0 false-green regression: $pass passed, $fail failed"
93+
[ "$fail" -eq 0 ]

0 commit comments

Comments
 (0)