Files
holocron/tests/test-adr0020-contract.sh
Claude Code AI - Gitea MCP 598a7c326a refactor(skills): retrofit the corpus to the ADR-0020 context contract (#129)
Retrofits all 39 skills to ADR-0020's description/body context contract, then fixes what six rounds of independent review found in that retrofit — including four ways the hot gate itself failed open.

Closes #99, #107, #108, #110, #111, #114, #115, #120.

## The retrofit (waves 1-5)

| | Start | Now |
|---|---|---|
| Description FAILs (>400 chars) | 26 | **0** |
| Body FAILs (>900 words, body-only) | 9 | **0** |
| Dangling routing targets | 2 | **0** |
| `Kyberforge.CompositionNote` | 10 | **0** |
| Preload tax | 21,005 chars | **~10,500** |

Under the 12,000-char success criterion. Per-wave detail is on #99.

## The review fixes

**The gate failed open four ways, three of them found after the retrofit shipped.** An unrecognised follower token made a dangling target vanish. A skill directory with no `SKILL.md` resolved as a valid target, so a commit could be green locally and red in a fresh clone — three existing fixtures were relying on that, one of which made the install-leak A/B pass vacuously. Then the free-standing `/name` sweep turned out to be gated on the sentence carrying a boundary marker, so route notation in any other sentence was invisible — not an ERROR, not a SUGGESTION, not an INFO — which left the documented "`/name` always blocks" promise false from a second direction. All four fixed and pinned.

**Two checks were silently not running.** `validate-provenance.sh` checks 7-8 were dead across nine skills. Waking them exposed a deeper problem: they assume `Research doc:` names a source index, but 30 of 121 entries point at topic content documents, so every new check-7 INFO was a false positive and check 8 was saved from a false-FAIL flood only by an *unannounced* skip. Checks 7/8 are now scoped to source indexes and every skip announces itself (#121).

**The retrofit's own anti-goal, four times.** ADR-0020 warns that a blunt gate gets satisfied by deleting content rather than relocating it. `diagnose` and `skill-audit` relocated prose and then read it unconditionally; `prototype` and `vale-config` deleted rules outright that survived nowhere. All four addressed.

## Verification

- `bash tests/run-tests.sh --strict` — 24 suites, 0 skipped, 0 failed
- `bash tests/run-bats.sh` — 325 tests, 0 failures
- `pre-commit run --all-files` — 17/17
- `pre-commit run --hook-stage pre-push --all-files` — 16/16, with `apm marketplace check` and `apm pack --check-clean` run against the remote, not skipped
- `scripts/skill-size-check.sh` over all 39 skills — rc 0, 0 ERROR/FAIL, SUGGESTION-only
- Preload tax measured at **10,498 chars**, max description 390 — both inside budget
- Every new test proven non-vacuous by a deliberate mutation of the behaviour it covers

**Per-commit sync, stated accurately:** the ten commits from the latest review round each pass `check-plugin-content-sync` in isolation, verified by checking each out in a detached worktree with a clean between. The earlier gitea window (`dfacf05..bedbd1d`, nine commits) does **not** — its mirror was regenerated in one batch at `bbc7300`. An earlier revision of this description claimed the property held for every commit; it does not, and a bisect through that window lands on a red commit. **Squash-merge** to collapse it, or accept that this range is not bisectable.

## Version bump

Six plugins and the catalog take a **patch**, not a minor. The branch is **89 commits — 40 `fix` / 30 `refactor` / 12 `docs` / 5 `chore` / 2 `test` — zero `feat`, zero `!`, zero `BREAKING CHANGE`** — and adds no skill, agent, command or hook. (Two earlier revisions of this section cited a stale histogram, most recently 78 commits; the figures above are measured at HEAD.) Both rules this repo ships (`forge/references/version-bump.md`, landing in this PR, and `git-commits/references/conventional-commits-spec.md`) make that a patch, and the catalog set is unchanged at 7 entries.

Not settled by that: four published files were removed from the installed tree, three moved, and `caveman` gained `disable-model-invocation`, retiring its old triggers. Under a strict reading those are major-class and currently ship under `refactor:` with no marker. Whether the deployed skill surface is a public contract is written down nowhere — worth deciding, but it outlives this PR.

## Deliberately not in scope

#112 (cherry-pick ownership, now resolved in favour of `git-commits`), #113 (`rtk git` normalisation), #116 (research fan-out), #101 (audit-skill merge), #122 (non-spec skill-root files), #123 (no PRD producer) stay open. #117 is the one worth reading: the contract's remedy is to move prose into `references/`, which is exactly where neither the size gate nor Vale looks — and the blind spot is wider than #117 currently records, since there is no root `.vale.ini` at all, so every ADR, `CONTEXT.md` and `README.md` is unlinted too.

That blind spot let this branch carry two `level: error` `Kyberforge.SentenceOpenerThereIs` violations into `references/` files it created — `provider-adapter-author/references/provider-matrix.md:31` and `agent-audit/references/finding-criteria.md:95`. Both are reworded in `afadaae`, confirmed by routing each file through the audit's own `vale-wrap.sh` (1 error each before, 0 after). Five further occurrences sit in `references/` files already on `main`; those are the pre-existing corpus and stay with #117, which is the real fix.

Also unfixed and not this PR's: `apm install` appends a duplicate `SessionStart` entry to `.claude/settings.json`, so a fresh clone cannot get pre-push green without an edit AGENTS.md warns against. Reproduces identically on `main`.

Co-authored-by: Defame1297 <gitea@rkdr.net>
Reviewed-on: https://git.dev.rkdr.net/Defame1297/holocron/pulls/129
Co-authored-by: Claude Code AI - Gitea MCP <claude@noreply.git.dev.rkdr.net>
Co-committed-by: Claude Code AI - Gitea MCP <claude@noreply.git.dev.rkdr.net>
2026-09-01 13:47:46 +00:00

398 lines
18 KiB
Bash
Executable File

#!/usr/bin/env bash
# Regression test for the STRUCTURAL claims the ADR-0020 gate family makes about
# 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 three 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.
# Nothing but this file asserts the three 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.
# 1b. The same claim, one directory over, for the Contributing-files parser
# embedded in both validate-provenance.sh copies. That one was worse: the
# agent-audit copy's docstring ASSERTED it was kept behaviourally identical
# to skill-audit's, and the two had already drifted.
# 2. Both interpreter preflights, in all three scripts. python3 and PyYAML are
# declared HARD dependencies precisely so a missing one cannot turn into a
# vacuous pass, and the two are checked separately so the message names the
# thing to install rather than the wrong one.
# 3. `verbose: true` on the skill-size-check hook. It is the ENTIRE delivery
# mechanism for the SUGGESTION tier: pre-commit prints nothing at all for a
# passing hook, and a SUGGESTION deliberately does not fail, so dropping
# one word from the config silences the tier ADR-0020 depends on while
# every test and every hook still reports green.
set -euo pipefail
REPO_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
HOOK="$REPO_ROOT/scripts/skill-size-check.sh"
SKILL_VALIDATE="$REPO_ROOT/plugins/kyberforge/.apm/skills/skill-audit/scripts/validate.sh"
AGENT_VALIDATE="$REPO_ROOT/plugins/kyberforge/.apm/skills/agent-audit/scripts/validate.sh"
PASS=0
FAIL=0
pass() { echo " PASS: $1"; PASS=$((PASS + 1)); }
fail() { echo " FAIL: $1"; FAIL=$((FAIL + 1)); }
TMPDIR_T="$(mktemp -d)"
trap 'rm -rf "$TMPDIR_T"' EXIT
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 all three scripts
# ---------------------------------------------------------------------------
echo ""
echo "--- the ADR-0020 shared resolver block is byte-identical in all three scripts ---"
# 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 all three were mangled the
# same way, so the shape is asserted before the contents.
MARKERS_OK=true
for f in "$HOOK" "$SKILL_VALIDATE" "$AGENT_VALIDATE"; 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"
else
HASHES=()
LINECOUNTS=()
for f in "$HOOK" "$SKILL_VALIDATE" "$AGENT_VALIDATE"; 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]}" && "${HASHES[1]}" == "${HASHES[2]}" ]]; then
pass "all three copies hash to ${HASHES[0]} (${LINECOUNTS[0]} lines) — agreement by construction, not by coincidence"
else
fail "the shared resolver has DRIFTED: skill-size-check=${HASHES[0]} (${LINECOUNTS[0]} lines), skill-audit=${HASHES[1]} (${LINECOUNTS[1]} lines), agent-audit=${HASHES[2]} (${LINECOUNTS[2]} lines). Edit one copy, then paste it over the other two."
fi
# A block that has been emptied out would hash equal in all three and pass the
# comparison above while enforcing nothing. The resolver is ~570 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"
else
fail "the extracted shared block is only ${LINECOUNTS[0]} lines — three identical empty spans would compare equal and assert nothing"
fi
fi
# ---------------------------------------------------------------------------
# 1b. The shared Contributing-files parser is byte-identical in both copies
# ---------------------------------------------------------------------------
# Same defect class, one directory over. parse_contributing_files() is embedded
# in both validate-provenance.sh copies for the same reason the resolver is
# embedded three times, 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 had already drifted into
# different spellings of the bullet loop. The parser decides whether checks 4,
# 5 and 8 run at all, so a one-sided edit disables a check in one script while
# every other test stays green.
echo ""
echo "--- the shared Contributing-files parser is byte-identical in both validate-provenance.sh copies ---"
CF_BEGIN='# ===== BEGIN SHARED CONTRIBUTING-FILES PARSER ====='
CF_END='# ===== END SHARED CONTRIBUTING-FILES PARSER ====='
SKILL_PROV="$REPO_ROOT/plugins/kyberforge/.apm/skills/skill-audit/scripts/validate-provenance.sh"
AGENT_PROV="$REPO_ROOT/plugins/kyberforge/.apm/skills/agent-audit/scripts/validate-provenance.sh"
CF_MARKERS_OK=true
for f in "$SKILL_PROV" "$AGENT_PROV"; do
if [[ ! -f "$f" ]]; then
fail "script not found: $f"
CF_MARKERS_OK=false
continue
fi
b="$(grep -cFx "$CF_BEGIN" "$f" || true)"
e="$(grep -cFx "$CF_END" "$f" || true)"
if [[ "$b" == "1" && "$e" == "1" ]]; then
pass "${f#"$REPO_ROOT/"} carries exactly one BEGIN and one END parser marker"
else
fail "${f#"$REPO_ROOT/"} has $b BEGIN and $e END parser markers, expected 1 and 1"
CF_MARKERS_OK=false
fi
done
if ! $CF_MARKERS_OK; then
fail "skipping the parser byte-identity comparison — the marker pairs are not well-formed, so any extraction would measure the wrong span"
else
CF_HASHES=()
CF_LINECOUNTS=()
for f in "$SKILL_PROV" "$AGENT_PROV"; do
out="$TMPDIR_T/cfblock-$(echo "$f" | md5sum | cut -c1-8).txt"
sed -n "/^${CF_BEGIN}\$/,/^${CF_END}\$/p" "$f" > "$out"
CF_HASHES+=("$(md5sum < "$out" | cut -d' ' -f1)")
CF_LINECOUNTS+=("$(wc -l < "$out" | tr -d ' ')")
done
if [[ "${CF_HASHES[0]}" == "${CF_HASHES[1]}" ]]; then
pass "both copies hash to ${CF_HASHES[0]} (${CF_LINECOUNTS[0]} lines) — agreement by construction, not by coincidence"
else
fail "the shared Contributing-files parser has DRIFTED: skill-audit=${CF_HASHES[0]} (${CF_LINECOUNTS[0]} lines), agent-audit=${CF_HASHES[1]} (${CF_LINECOUNTS[1]} lines). Edit one copy, then paste it over the other."
fi
# Two identical EMPTY spans would hash equal and assert nothing, exactly as
# for the resolver above. The parser block is ~93 lines; 40 is a floor low
# enough never to need maintenance and high enough that a gutted block — or
# one reduced to its docstring — cannot sneak past.
if [[ "${CF_LINECOUNTS[0]}" -gt 40 ]]; then
pass "the extracted parser block is ${CF_LINECOUNTS[0]} lines — the comparison is over real content, not an empty span"
else
fail "the extracted parser block is only ${CF_LINECOUNTS[0]} lines — two identical empty spans would compare equal and assert nothing"
fi
fi
# ---------------------------------------------------------------------------
# 2. Both interpreter preflights, in all three scripts
# ---------------------------------------------------------------------------
# The two are checked separately on purpose: `python3 -c 'import yaml'` fails
# identically whether python3 is missing or PyYAML is, and naming the wrong one
# sends the reader to install the wrong thing.
REAL_PYTHON="$(command -v python3)"
# Absolute path, deliberately. The no-python3 fixture below replaces PATH
# wholesale, so a bare `bash` (or `/usr/bin/env bash`) would be resolved against
# that stripped PATH and die with "No such file or directory" before the script
# under test ever starts -- a 127 that looks like the preflight firing.
BASH_BIN="$(command -v bash)"
# A PATH that genuinely has no python3 on it. Built by symlinking the handful of
# binaries the three scripts touch before their own preflight rather than by
# hiding python3 from a full PATH, because there is no portable way to subtract
# one entry from a directory. `bash` is invoked by absolute path below so the
# interpreter itself does not have to be on this PATH.
NOPY_BIN="$TMPDIR_T/nopython-bin"
mkdir -p "$NOPY_BIN"
for b in awk cat cut dirname basename grep sed pwd rm mkdir tr; do
src="$(command -v "$b" 2>/dev/null || true)"
[[ -n "$src" ]] && ln -sf "$src" "$NOPY_BIN/$b"
done
# A python3 that runs but cannot import yaml. A shim on PATH re-execs the real
# interpreter with a PYTHONPATH entry holding a `yaml` module that raises on
# import; PYTHONPATH precedes site-packages on sys.path, so it shadows a real
# PyYAML install without touching it.
SHADOW="$TMPDIR_T/shadow"
mkdir -p "$SHADOW"
printf 'raise ImportError("PyYAML deliberately unavailable in this fixture")\n' \
> "$SHADOW/yaml.py"
NOYAML_BIN="$TMPDIR_T/noyaml-bin"
mkdir -p "$NOYAML_BIN"
cat > "$NOYAML_BIN/python3" <<EOF
#!/bin/sh
PYTHONPATH="$SHADOW\${PYTHONPATH:+:\$PYTHONPATH}" exec "$REAL_PYTHON" "\$@"
EOF
chmod +x "$NOYAML_BIN/python3"
# Sanity-check the two fixtures themselves before trusting any verdict they
# produce. A shim that silently still imports yaml would make every PyYAML
# assertion below pass for the wrong reason.
if PATH="$NOYAML_BIN:$PATH" python3 -c 'import yaml' 2>/dev/null; then
fail "the no-PyYAML shim does not actually shadow PyYAML — every PyYAML assertion below would be vacuous"
else
pass "fixture check: the no-PyYAML shim makes 'import yaml' fail while python3 still runs"
fi
if PATH="$NOPY_BIN" command -v python3 > /dev/null 2>&1; then
fail "the no-python3 PATH still resolves python3 — every python3 assertion below would be vacuous"
else
pass "fixture check: the no-python3 PATH resolves no python3"
fi
# A minimal, entirely clean subject for each script. The preflight must fire
# before any measurement, so the subject's own content is irrelevant — which is
# exactly what makes a clean one the right choice: nothing else can produce the
# non-zero exit these cases assert.
SUBJECT_SKILL_DIR="$TMPDIR_T/subject/my-skill"
mkdir -p "$SUBJECT_SKILL_DIR"
cat > "$SUBJECT_SKILL_DIR/SKILL.md" <<'EOF'
---
name: my-skill
description: A short valid description. Do not use for anything else.
---
Do the thing.
EOF
SUBJECT_AGENT_ROOT="$TMPDIR_T/subject-agent"
mkdir -p "$SUBJECT_AGENT_ROOT/.apm/agents"
cat > "$SUBJECT_AGENT_ROOT/apm.yml" <<'EOF'
name: test-package
version: 0.1.0
type: skill
EOF
cat > "$SUBJECT_AGENT_ROOT/.apm/agents/my-agent.agent.md" <<'EOF'
---
name: my-agent
description: A short valid description. Do not use for anything else.
---
You are a test agent. When invoked, do the thing.
EOF
# probe_preflight <label> <env-kind: nopython|noyaml> <expect-needle> <cmd...>
probe_preflight() {
local label="$1" kind="$2" needle="$3"
shift 3
local out status=0
set +e
if [[ "$kind" == nopython ]]; then
out="$(env -i PATH="$NOPY_BIN" HOME="$HOME" "$BASH_BIN" "$@" 2>&1)"
else
out="$(env PATH="$NOYAML_BIN:$PATH" "$BASH_BIN" "$@" 2>&1)"
fi
status=$?
set -e
if [[ $status -eq 0 ]]; then
fail "$label exited 0 — a missing hard dependency became a vacuous pass (output: ${out:-<empty>})"
elif [[ "$out" != *"$needle"* ]]; then
# The needle is the DIAGNOSTIC ("python3 is required"), not the bare word.
# Deleting the preflight entirely would still produce a non-zero exit and a
# message mentioning python3 -- bash's own "python3: command not found" --
# so a bare-word needle would go green on a script with no preflight at all.
fail "$label exited $status but never produced the '$needle' diagnostic (output: ${out:-<empty>})"
elif [[ "$kind" == nopython && "$out" == *PyYAML* ]]; then
fail "$label reported PyYAML when python3 itself is missing — that sends the reader to install the wrong thing (output: $out)"
else
pass "$label"
fi
}
echo ""
echo "--- a PATH with no python3 is a hard failure in all three scripts, naming python3 ---"
probe_preflight "scripts/skill-size-check.sh reports missing python3" \
nopython "python3 is required" \
"$HOOK" "$SUBJECT_SKILL_DIR/SKILL.md"
probe_preflight "skill-audit/scripts/validate.sh reports missing python3" \
nopython "python3 is required" \
"$SKILL_VALIDATE" "$SUBJECT_SKILL_DIR"
probe_preflight "agent-audit/scripts/validate.sh reports missing python3" \
nopython "python3 is required" \
"$AGENT_VALIDATE" "$SUBJECT_AGENT_ROOT/.apm/agents/my-agent.agent.md"
echo ""
echo "--- a python3 that cannot import yaml is a hard failure in all three scripts, naming PyYAML ---"
probe_preflight "scripts/skill-size-check.sh reports missing PyYAML" \
noyaml "PyYAML is required" \
"$HOOK" "$SUBJECT_SKILL_DIR/SKILL.md"
probe_preflight "skill-audit/scripts/validate.sh reports missing PyYAML" \
noyaml "PyYAML is required" \
"$SKILL_VALIDATE" "$SUBJECT_SKILL_DIR"
probe_preflight "agent-audit/scripts/validate.sh reports missing PyYAML" \
noyaml "PyYAML is required" \
"$AGENT_VALIDATE" "$SUBJECT_AGENT_ROOT/.apm/agents/my-agent.agent.md"
# The control. Without it, "fails when the dependency is missing" is satisfied by
# a script that fails unconditionally, and the two cases above would be green on
# a gate that never runs at all.
echo ""
echo "--- control: with both dependencies present the same subjects pass ---"
for probe in "$HOOK:$SUBJECT_SKILL_DIR/SKILL.md" \
"$SKILL_VALIDATE:$SUBJECT_SKILL_DIR" \
"$AGENT_VALIDATE:$SUBJECT_AGENT_ROOT/.apm/agents/my-agent.agent.md"; do
script="${probe%%:*}"
arg="${probe#*:}"
set +e
ctl_out="$(bash "$script" "$arg" 2>&1)"
ctl_rc=$?
set -e
if [[ $ctl_rc -eq 0 ]]; then
pass "${script#"$REPO_ROOT/"} exits 0 on a clean subject with python3 and PyYAML available"
else
fail "${script#"$REPO_ROOT/"} failed a clean subject (exit $ctl_rc): $ctl_out"
fi
done
# ---------------------------------------------------------------------------
# 3. verbose: true on the skill-size-check hook, in BOTH manifests
# ---------------------------------------------------------------------------
# .pre-commit-config.yaml governs this repo; .pre-commit-hooks.yaml is what a
# CONSUMER repo gets when it points at this one. Dropping the flag from either
# silences the SUGGESTION tier for that audience alone, which is the hardest
# version of the defect to notice.
echo ""
echo "--- the skill-size-check hook declares verbose: true in both manifests ---"
VERBOSE_REPORT="$(python3 - "$REPO_ROOT" <<'PY'
import os
import sys
import yaml
root = sys.argv[1]
def emit(status, msg):
print("%s\t%s" % (status, msg))
# Repo config: nested repos[].hooks[].
path = os.path.join(root, '.pre-commit-config.yaml')
try:
with open(path, encoding='utf-8') as fh:
cfg = yaml.safe_load(fh) or {}
except Exception as exc:
emit('FAIL', '.pre-commit-config.yaml did not parse: %s' % exc)
cfg = {}
found = None
for repo in cfg.get('repos') or []:
for hook in (repo.get('hooks') or []):
if hook.get('id') == 'skill-size-check':
found = hook
if found is None:
emit('FAIL', '.pre-commit-config.yaml declares no hook with id skill-size-check')
elif found.get('verbose') is True:
emit('PASS', '.pre-commit-config.yaml: skill-size-check is verbose: true')
else:
emit('FAIL', '.pre-commit-config.yaml: skill-size-check has verbose=%r — '
'pre-commit prints nothing for a passing hook, so every '
'ADR-0020 SUGGESTION is swallowed' % (found.get('verbose'),))
# Consumer manifest: a flat list of hooks.
path = os.path.join(root, '.pre-commit-hooks.yaml')
try:
with open(path, encoding='utf-8') as fh:
hooks = yaml.safe_load(fh) or []
except Exception as exc:
emit('FAIL', '.pre-commit-hooks.yaml did not parse: %s' % exc)
hooks = []
found = None
for hook in hooks:
if isinstance(hook, dict) and hook.get('id') == 'kyberforge-skill-size-check':
found = hook
if found is None:
emit('FAIL', '.pre-commit-hooks.yaml declares no hook with id kyberforge-skill-size-check')
elif found.get('verbose') is True:
emit('PASS', '.pre-commit-hooks.yaml: kyberforge-skill-size-check is verbose: true')
else:
emit('FAIL', '.pre-commit-hooks.yaml: kyberforge-skill-size-check has verbose=%r — '
'a consumer repo would never see the SUGGESTION tier'
% (found.get('verbose'),))
PY
)"
while IFS=$'\t' read -r status msg; do
[[ -n "$status" ]] || continue
if [[ "$status" == PASS ]]; then
pass "$msg"
else
fail "$msg"
fi
done <<< "$VERBOSE_REPORT"
echo ""
echo "Results: $PASS passed, $FAIL failed"
[[ $FAIL -eq 0 ]]