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

@@ -410,14 +410,17 @@ skills. They are copied rather than imported because a cache-installed plugin's
files outside their own plugin directory. `tests/test-skill-size-check.sh` asserts the copies agree,
so drift fails CI rather than silently letting an audit bless a skill the commit hook then rejects.
**The shared boundary resolver is now two copies, not three** (ADR-0025). `scripts/skill-size-check.sh`
still carries it embedded between `BEGIN`/`END ADR-0020 SHARED BOUNDARY RESOLVER` markers; the two
plugin copies that used to sit inside `skill-audit`'s and `agent-audit`'s `validate.sh` collapsed
into the single `factory-audit/scripts/lib-boundary-resolver.sh`, sourced by that skill's scripts.
The two remaining copies must still stay byte-identical — a plugin script cannot source the root
one, which is the constraint that forces a copy to exist at all.
**The shared boundary resolver is one copy** (ADR-0025, then 2026-09-16). It lives between the
`BEGIN`/`END ADR-0020 SHARED BOUNDARY RESOLVER` markers in `factory-audit/scripts/lib-boundary-resolver.sh`.
ADR-0025 collapsed the two copies inside `skill-audit`'s and `agent-audit`'s `validate.sh` into that
file. `scripts/skill-size-check.sh` kept an embedded, byte-identical third copy while it was also
exported through `.pre-commit-hooks.yaml`, whose consumers could not reach a file inside the plugin.
`4de5b6b` retired that export (ADR-0014), so the hook now sources the library by path and fails closed
if the library is missing or defines no resolver.
`tests/test-adr0020-contract.sh` pins that arrangement, and one of its assertions was green on a
`tests/test-adr0020-contract.sh` pins that arrangement: the library carries the only marker pair,
the hook carries none, the hook fails closed without the library, and a sentinel planted in a copied
library proves the hook executes the library's text. One of its assertions was green on a
defect it named. "`validate.sh` sources the resolver in **both mode branches**" was implemented as a
file-wide `grep -Ec … -ge 2`, which cannot see a branch at all: delete the `agent)` arm's source line
and duplicate the `skill)` arm's, and the file-wide count is still 2 and the assertion still passes,
@@ -425,7 +428,8 @@ with the agent path running no resolver or some other one. It is now a **per-arm
each arm of `validate.sh`'s `case "$MODE" in` block must carry exactly one `source` line inside its
own body, and the file must carry exactly those two — with a mutation self-test that performs that
exact count-preserving edit on a copy and requires the check to fail on it. The suite went 25 → 28
cases.
cases. It stood at 27 before the 2026-09-16 change and 29 after it, which replaced the two-copy
hash and its line-count floor with the six one-copy assertions above.
### `python3` and PyYAML are hard requirements