refactor(gates): source the boundary resolver into skill-size-check

Why: scripts/skill-size-check.sh embedded a byte-identical 1,061-line copy
of the ADR-0020 boundary resolver only because it was also exported
through .pre-commit-hooks.yaml, whose consumers could not reach a file
inside the plugin. 4de5b6b retired that export, so the hook now runs only
in this repo and can source factory-audit's lib-boundary-resolver.sh like
validate.sh does. One copy removes the edit-one-paste-the-other hazard.

Implementation Notes:
- The hook's Python program is assembled from its own preamble, the
  library's resolver and its own checks, read from quoted here-docs. The
  assembled program matches the old one line for line except one comment,
  and the hook's stdout, stderr and exit code are identical over every
  corpus SKILL.md and the 26 differential-suite fixtures.
- The hook fails closed, naming the library, when it is missing or
  defines no resolver.
- test-adr0020-contract.sh assertion 1 now pins the single copy: one
  marker pair in the library, none in the hook, fail-closed on a missing
  or gutted library, and a sentinel planted in a copied library that must
  appear in the hook's output. 1a expects exactly one authority. 27 -> 29
  passes.
- ADR-0020 and ADR-0025 carry dated amendments; gates.md and the
  library, hook and mode-library comments no longer describe two copies.
- factory-audit is new on this branch, so the version-bump gate exempts
  it; kyberforge is already at 2.0.0 against main's 1.6.2.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-09-16 13:25:16 +00:00
parent adaa978d20
commit ef27c9751a
9 changed files with 239 additions and 1196 deletions

View File

@@ -3,22 +3,20 @@
# itself. None of them was pinned anywhere before this file, and each one fails
# silently — which is the whole reason they need a test rather than a comment:
#
# 1. "ONE resolver, embedded VERBATIM in two scripts." The block between the
# BEGIN/END markers is copied, not imported, because a cache-installed
# plugin's scripts cannot read files outside their own plugin directory.
# ADR-0025 merged skill-audit and agent-audit, which dropped the count from
# three copies to two: factory-audit now holds ONE copy in a sourced
# lib-boundary-resolver.sh, and scripts/skill-size-check.sh keeps its
# embedded copy because it is a repo-root hook kept fit for a published
# hook manifest (retired; ADR-0014), where entry[0] is the only token
# pre-commit rewrites — it could not reach a file inside the plugin at a
# path any consumer has. Nothing but this file asserts the two copies are still identical, and
# a one-line edit to a single copy is invisible: every constant-agreement
# assertion in tests/test-skill-size-check.sh still passes, because the
# CONSTANTS are not what drifted.
# 1a. The resolver's two copies are the ONLY two, and validate.sh sources
# factory-audit's in both mode branches — the same authority checks 1b
# makes for the parser, which byte-identity alone cannot make.
# 1. "ONE resolver, and both of its consumers run it." The ADR-0020 boundary
# resolver has exactly one copy, factory-audit's sourced
# lib-boundary-resolver.sh. scripts/skill-size-check.sh used to embed a
# second, byte-identical copy, because it was also exported through a
# published hook manifest whose consumers could not reach a file inside the
# plugin; 4de5b6b retired that export (ADR-0014), so the hook now sources
# the library too. What must not fail silently: the hook growing its own
# copy back, the library being gutted, or the hook no longer running the
# library's text at all. So this asserts the library is real content, the
# hook carries no marker pair, the hook fails closed without the library,
# and — by a sentinel planted in a copied library — that the text the hook
# executes IS the library's.
# 1a. The library is the ONLY authority, and validate.sh sources it in both
# mode branches — the same authority checks 1b makes for the parser.
# 1b. The same claim, one directory over, for the Contributing-files parser.
# That one was worse: it was embedded in both validate-provenance.sh copies,
# the agent-audit copy's docstring ASSERTED it was kept behaviourally
@@ -49,8 +47,8 @@ REPO_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
HOOK="$REPO_ROOT/scripts/skill-size-check.sh"
FACTORY_AUDIT="$REPO_ROOT/plugins/kyberforge/.apm/skills/factory-audit"
# ADR-0025: one auto-detecting entry point, and one sourced copy of the resolver
# behind it. The entry point is what the preflight assertions run; the library is
# what the byte-identity assertion hashes.
# behind it, which the root hook sources as well. The entry point is what the
# preflight assertions run; the library is what assertion 1 inspects.
FACTORY_VALIDATE="$FACTORY_AUDIT/scripts/validate.sh"
FACTORY_RESOLVER="$FACTORY_AUDIT/scripts/lib-boundary-resolver.sh"
PASS=0
@@ -66,70 +64,111 @@ BEGIN_MARKER='# ===== BEGIN ADR-0020 SHARED BOUNDARY RESOLVER ====='
END_MARKER='# ===== END ADR-0020 SHARED BOUNDARY RESOLVER ====='
# ---------------------------------------------------------------------------
# 1. The shared resolver block is byte-identical in both scripts
# 1. The resolver has one copy, and the root hook runs it
# ---------------------------------------------------------------------------
# Two copies, not three, since ADR-0025 merged skill-audit and agent-audit:
# scripts/skill-size-check.sh (embedded — sourcing the plugin's copy was
# considered and refuted, see the header) and factory-audit's single sourced
# lib-boundary-resolver.sh. The claim is unchanged and so is the check: the
# span between the markers must be byte-identical wherever it appears.
# One copy since the 2026-09-16 change: factory-audit's lib-boundary-resolver.sh.
# scripts/skill-size-check.sh sources it rather than embedding a second copy, so
# there is no pair left to hash. What replaces the hash is the set of ways a
# single sourced copy can still fail quietly.
echo ""
echo "--- the ADR-0020 shared resolver block is byte-identical in both scripts ---"
echo "--- the ADR-0020 resolver has one copy, and skill-size-check.sh runs it ---"
# Marker discipline first. An unbalanced or duplicated marker pair makes the
# extraction below silently measure the wrong span — a sed range that never
# closes swallows the rest of the file, and one that opens twice concatenates
# two spans. Both would still compare "equal" if both were mangled the
# same way, so the shape is asserted before the contents.
MARKERS_OK=true
for f in "$HOOK" "$FACTORY_RESOLVER"; do
if [[ ! -f "$f" ]]; then
fail "script not found: $f"
MARKERS_OK=false
continue
fi
b="$(grep -cFx "$BEGIN_MARKER" "$f" || true)"
e="$(grep -cFx "$END_MARKER" "$f" || true)"
if [[ "$b" == "1" && "$e" == "1" ]]; then
pass "${f#"$REPO_ROOT/"} carries exactly one BEGIN and one END marker"
else
fail "${f#"$REPO_ROOT/"} has $b BEGIN and $e END markers, expected 1 and 1"
MARKERS_OK=false
fi
done
if ! $MARKERS_OK; then
fail "skipping the byte-identity comparison — the marker pairs are not well-formed, so any extraction would measure the wrong span"
# (i) The library carries exactly one well-formed marker pair around real
# content. An unbalanced pair makes every extraction measure the wrong span, and
# an emptied-out block would still "load" while enforcing nothing. The resolver
# is ~1,060 lines; 100 is a floor low enough never to need maintenance and high
# enough that a gutted block cannot sneak past.
if [[ ! -f "$FACTORY_RESOLVER" ]]; then
fail "resolver library not found: ${FACTORY_RESOLVER#"$REPO_ROOT/"}"
else
HASHES=()
LINECOUNTS=()
for f in "$HOOK" "$FACTORY_RESOLVER"; do
out="$TMPDIR_T/block-$(echo "$f" | md5sum | cut -c1-8).txt"
sed -n "/^${BEGIN_MARKER}\$/,/^${END_MARKER}\$/p" "$f" > "$out"
HASHES+=("$(md5sum < "$out" | cut -d' ' -f1)")
LINECOUNTS+=("$(wc -l < "$out" | tr -d ' ')")
done
if [[ "${HASHES[0]}" == "${HASHES[1]}" ]]; then
pass "both copies hash to ${HASHES[0]} (${LINECOUNTS[0]} lines) — agreement by construction, not by coincidence"
b="$(grep -cFx "$BEGIN_MARKER" "$FACTORY_RESOLVER" || true)"
e="$(grep -cFx "$END_MARKER" "$FACTORY_RESOLVER" || true)"
if [[ "$b" == "1" && "$e" == "1" ]]; then
pass "${FACTORY_RESOLVER#"$REPO_ROOT/"} carries exactly one BEGIN and one END marker"
span="$(sed -n "/^${BEGIN_MARKER}\$/,/^${END_MARKER}\$/p" "$FACTORY_RESOLVER" | wc -l | tr -d ' ')"
if [[ "$span" -gt 100 ]]; then
pass "the resolver span is $span lines — real content, not an empty block"
else
fail "the resolver span is only $span lines — a gutted block would load and resolve nothing"
fi
else
fail "the shared resolver has DRIFTED: skill-size-check=${HASHES[0]} (${LINECOUNTS[0]} lines), factory-audit/scripts/lib-boundary-resolver.sh=${HASHES[1]} (${LINECOUNTS[1]} lines). Edit one copy, then paste it over the other."
fail "${FACTORY_RESOLVER#"$REPO_ROOT/"} has $b BEGIN and $e END markers, expected 1 and 1"
fi
# A block that has been emptied out would hash equal in both and pass the
# comparison above while enforcing nothing. The resolver is ~1,060 lines; 100
# is a floor low enough never to need maintenance and high enough that a
# gutted block cannot sneak past.
if [[ "${LINECOUNTS[0]}" -gt 100 ]]; then
pass "the extracted block is ${LINECOUNTS[0]} lines — the comparison is over real content, not an empty span"
fi
# (ii) The hook carries no copy of its own. A column-0 marker line in the hook
# is the shape the old embedded copy had, and the shape a paste-back would have.
if [[ ! -f "$HOOK" ]]; then
fail "hook not found: ${HOOK#"$REPO_ROOT/"}"
else
hb="$(grep -cFx "$BEGIN_MARKER" "$HOOK" || true)"
he="$(grep -cFx "$END_MARKER" "$HOOK" || true)"
if [[ "$hb" == "0" && "$he" == "0" ]]; then
pass "${HOOK#"$REPO_ROOT/"} carries no resolver marker lines — it has not grown its own copy back"
else
fail "the extracted shared block is only ${LINECOUNTS[0]} lines — two identical empty spans would compare equal and assert nothing"
fail "${HOOK#"$REPO_ROOT/"} carries $hb BEGIN and $he END marker lines — a second copy of the resolver is back in the hook"
fi
fi
# (iii) and (iv) run the hook from a scratch tree that mirrors the two paths it
# depends on, so the real library is never touched. The scratch hook is a copy
# of the real one; its library is either absent, gutted, or the real library
# with a sentinel planted inside the resolver block.
SSC_TREE="$TMPDIR_T/ssc-tree"
SSC_LIB_DIR="$SSC_TREE/plugins/kyberforge/.apm/skills/factory-audit/scripts"
mkdir -p "$SSC_TREE/scripts" "$SSC_LIB_DIR" "$TMPDIR_T/ssc-skill/probe-skill"
cp "$HOOK" "$SSC_TREE/scripts/skill-size-check.sh"
printf -- '---\nname: probe-skill\ndescription: Use when probing the resolver wiring.\nmetadata:\n version: "0.1.0"\n---\n\n## Step 1\n\nDo the thing.\n' \
> "$TMPDIR_T/ssc-skill/probe-skill/SKILL.md"
PROBE="$TMPDIR_T/ssc-skill/probe-skill/SKILL.md"
run_scratch_hook() {
local rc=0
SSC_OUT="$(bash "$SSC_TREE/scripts/skill-size-check.sh" "$PROBE" 2>&1)" || rc=$?
SSC_RC=$rc
}
# (iii) Fail closed: no library, then a library that defines nothing.
rm -f "$SSC_LIB_DIR/lib-boundary-resolver.sh"
run_scratch_hook
if [[ "$SSC_RC" -ne 0 && "$SSC_OUT" == *"boundary resolver library was not found"* ]]; then
pass "with the library missing, the hook exits $SSC_RC and names the missing library — not a vacuous pass"
else
fail "with the library missing, the hook exited $SSC_RC without naming it: $SSC_OUT"
fi
printf '# gutted\n' > "$SSC_LIB_DIR/lib-boundary-resolver.sh"
run_scratch_hook
if [[ "$SSC_RC" -ne 0 && "$SSC_OUT" == *"did not define the ADR-0020 boundary resolver"* ]]; then
pass "with a library that defines no resolver, the hook exits $SSC_RC and says so"
else
fail "with a gutted library, the hook exited $SSC_RC without saying so: $SSC_OUT"
fi
# (iv) The text the hook executes IS the library's. A sentinel print planted
# just after the BEGIN marker of a copied library must appear in the hook's
# output. Without this, a hook that sourced the library but ran some other
# program would pass (i)-(iii).
SENTINEL="ADR0020-RESOLVER-SENTINEL-$$"
if [[ -f "$FACTORY_RESOLVER" ]]; then
awk -v m="$BEGIN_MARKER" -v s="$SENTINEL" '{ print } $0 == m { print "print(\"" s "\")" }' \
"$FACTORY_RESOLVER" > "$SSC_LIB_DIR/lib-boundary-resolver.sh"
if [[ "$(grep -cF "$SENTINEL" "$SSC_LIB_DIR/lib-boundary-resolver.sh" || true)" -ne 1 ]]; then
fail "fixture check: the sentinel was not planted exactly once in the copied library — the case below would prove nothing"
else
run_scratch_hook
if [[ "$SSC_RC" -eq 0 && "$SSC_OUT" == *"$SENTINEL"* ]]; then
pass "a sentinel planted in the library's resolver block runs inside the hook — the hook executes the library's text"
else
fail "the hook did not run the library's resolver text (rc=$SSC_RC, sentinel absent from output): $SSC_OUT"
fi
fi
fi
# ---------------------------------------------------------------------------
# 1a. The resolver copies are the ONLY two, and validate.sh sources its one
# 1a. The resolver library is the ONLY authority, and validate.sh sources it
# ---------------------------------------------------------------------------
# Byte-identity between two named files says nothing about a THIRD copy, and
# nothing about whether factory-audit's copy is the one that runs. Assertion 1b
# Assertion 1 says nothing about a copy somewhere else in the tree, and nothing
# about whether validate.sh runs the library. Assertion 1b
# pins both of those for the Contributing-files parser; the resolver is the same
# defect class and gets the same two checks:
#
@@ -137,16 +176,14 @@ fi
# branches — asserted inside each arm of `case "$MODE" in`, not by counting
# source lines file-wide, because a count cannot see a branch. A library
# that is identical, unique and never sourced is a copy that has quietly
# been replaced by an inline one — and the byte-identity check above would
# stay green over it.
# been replaced by an inline one — and assertion 1 would stay green over it.
# b. Nothing has re-inlined it. The BEGIN marker and a def unique to the
# resolver (`_authoring_root`) appear in exactly the two authorities —
# scripts/skill-size-check.sh and lib-boundary-resolver.sh — and nowhere
# else under the tree. A mode library that grows a "just this once" copy
# would otherwise escape assertion 1 entirely, because 1 hashes only the
# two files it names.
# resolver (`_authoring_root`) appear in exactly one file,
# lib-boundary-resolver.sh, and nowhere else under the tree. A mode library
# or a root script that grows a "just this once" copy would otherwise
# escape assertion 1 entirely, because 1 inspects only the files it names.
echo ""
echo "--- the ADR-0020 resolver has exactly two authorities, and validate.sh sources factory-audit's ---"
echo "--- the ADR-0020 resolver has exactly one authority, and validate.sh sources it ---"
# Deployed and vendored trees are generated copies, not authorities: .claude/ is
# apm install output, apm_modules/ is resolved dependencies, build/ is release
@@ -303,18 +340,18 @@ PY
fi
fi
# (b) Exactly the two authorities, for both spellings of a copy.
EXPECTED_RESOLVERS="$(printf '%s\n' "$HOOK" "$FACTORY_RESOLVER" | sort)"
# (b) Exactly the one authority, for both spellings of a copy.
EXPECTED_RESOLVERS="$FACTORY_RESOLVER"
check_resolver_authorities() {
local label="$1" needle="$2"
local found
found="$(tree_scan "$needle")"
if [[ "$found" == "$EXPECTED_RESOLVERS" ]]; then
pass "$label appears in exactly the two resolver authorities and nowhere else"
pass "$label appears in exactly the one resolver authority and nowhere else"
elif [[ -z "$found" ]]; then
fail "$label was found in NO file at all — the scan is looking for the wrong text"
else
fail "$label appears in an unexpected set of files, so the resolver has been re-inlined or lost: $(echo "$found" | tr '\n' ' ')— expected exactly ${HOOK#"$REPO_ROOT/"} and ${FACTORY_RESOLVER#"$REPO_ROOT/"}"
fail "$label appears in an unexpected set of files, so the resolver has been re-inlined or lost: $(echo "$found" | tr '\n' ' ')— expected exactly ${FACTORY_RESOLVER#"$REPO_ROOT/"}"
fi
}
check_resolver_authorities "the resolver's BEGIN marker" "$BEGIN_MARKER"
@@ -325,7 +362,7 @@ check_resolver_authorities "a 'def _authoring_root' definition" "def _authoring_
# ---------------------------------------------------------------------------
# Same defect class, one directory over. parse_contributing_files() used to be
# embedded in both validate-provenance.sh copies for the same reason the resolver
# is embedded twice, and until this assertion existed the agent-audit copy's
# was once embedded in several scripts, and until this assertion existed the agent-audit copy's
# docstring merely CLAIMED it was "kept behaviourally identical to skill-audit's
# copy" — an invariant nothing checked, and the two did drift into different
# spellings of the bullet loop at 484357a. That drift happened to be

View File

@@ -1,8 +1,8 @@
#!/usr/bin/env bash
# Regression test for the two ways an ADR-0020 gate can be made to check NOTHING
# while still exiting 0. Both were live defects, both were silent, and both sit
# in the shared resolver block that all three scripts embed verbatim — so every
# case below runs against all three.
# in the shared resolver block that all three entry points run — one sourced
# copy since 2026-09-16 — so every case below runs against all three.
#
# 1. THE FRONTMATTER BLOCKER. The frontmatter matcher used to be `^---\n`. A
# UTF-8 BOM, a leading blank line, a trailing space after either marker, or