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

@@ -1,19 +1,18 @@
#!/usr/bin/env bash
# lib-boundary-resolver.sh — SOURCED, never executed.
#
# The ADR-0020 shared boundary resolver, as ONE copy for this skill. Both of
# validate.sh's modes compose it into the Python program they run, so the
# skill-mode and agent-mode check suites resolve boundary targets through the
# same code rather than through two copies that can drift apart.
# The ADR-0020 shared boundary resolver — the ONE copy in the repo. Both of
# validate.sh's modes compose it into the Python program they run, and so does
# the repo-root hook scripts/skill-size-check.sh, so the audit and the commit
# hook resolve boundary targets through the same code rather than through
# copies that can drift apart.
#
# The resolver is Python, and bash cannot source Python, so the block is held
# in a shell variable filled from a QUOTED here-doc: nothing inside it is
# expanded, substituted or rewritten, and the text between the two markers
# below is therefore byte-identical to the copy in scripts/skill-size-check.sh
# that tests/test-adr0020-contract.sh hashes. The markers stay on lines of
# their own, at column 0, exactly once each, so `sed -n '/^BEGIN$/,/^END$/p'`
# extracts the same span here as it does from the scripts the test already
# reads. Edit one copy, then paste it over the others.
# expanded, substituted or rewritten, and every consumer runs exactly the text
# between the two markers below. The markers stay on lines of their own, at
# column 0, exactly once each: tests/test-adr0020-contract.sh extracts the span
# with `sed -n '/^BEGIN$/,/^END$/p'`, and asserts no other file carries them.
#
# The here-doc is consumed by the `read` BUILTIN rather than by `$(cat <<...)`.
# This file is sourced by validate.sh before the mode-specific python3/PyYAML
@@ -26,7 +25,7 @@
# exactly ONE newline, never a run: blank lines at the end of a chunk are part
# of the program text the entry script reassembles, and stripping every
# trailing newline deleted them. The here-doc itself is unchanged: still
# QUOTED, still byte-identical between its markers.
# QUOTED, still verbatim between its markers.
#
# Self-containment (agentskills.io, skill-author/references/deployment-modes.md)
# binds BETWEEN skills, not within one: a cache-installed plugin copies each
@@ -34,21 +33,22 @@
# travels with the skill and is always readable. That is why this is sourced
# here and duplicated across skill boundaries elsewhere.
#
# Consumed by: validate.sh (both modes), via $KYBERFORGE_RESOLVER_PY.
# Consumed by: validate.sh (both modes) and scripts/skill-size-check.sh, via
# $KYBERFORGE_RESOLVER_PY. The root hook reaches into this plugin by path, which
# is safe only because it runs solely inside this repo — 4de5b6b retired the
# published hook manifest that once made it run elsewhere (ADR-0014).
# shellcheck shell=bash
# shellcheck disable=SC2034
IFS='' read -r -d '' KYBERFORGE_RESOLVER_PY <<'KYBERFORGE_ADR0020_RESOLVER_PY' || true
# ===== BEGIN ADR-0020 SHARED BOUNDARY RESOLVER =====
# ONE resolver, embedded VERBATIM in two scripts (ADR-0025 retired the third):
# scripts/skill-size-check.sh
# plugins/kyberforge/.apm/skills/factory-audit/scripts/lib-boundary-resolver.sh
# The block between these markers must stay byte-identical in both. It is copied
# rather than imported because a cache-installed plugin's scripts cannot read
# files outside their own plugin directory, and this repo-root hook is kept fit for
# a published hook manifest (retired; ADR-0014), where only entry[0] is rewritten --
# so no single file is reachable by both (the same constraint that duplicates the
# ADR-0020 constants). Edit one copy, then paste it over the other.
# ONE resolver, and this is its only copy. Sourced from this file by:
# plugins/kyberforge/.apm/skills/factory-audit/scripts/validate.sh (both modes)
# scripts/skill-size-check.sh (the repo-root commit hook)
# ADR-0025 retired the copies in the two pre-merge audit skills, and the
# 2026-09-16 change retired the copy embedded in the root hook, which had been
# kept only while that hook was also exported through a published hook manifest
# (retired by 4de5b6b; ADR-0014). Edit it here; there is nothing to paste over.
#
# Requires: glob, os, re, yaml (imported by the host script; PyYAML is a hard
# dependency, preflighted in bash before the interpreter starts).