docs(spec): correct the gate and hook descriptions that did not reproduce
Four claims in the spec and the hook config stated as fact what the tools
do not do:
- gates.md:485 said factory-audit's validate.sh "holds its own copy of"
the ADR-0020 constants. gates.md:411-413, twenty lines earlier, said it
carries none of them and named the mode libraries. The libraries are
right: lib-checks-skill.sh:313-316 and lib-checks-agent.sh:164-165.
architecture.md repeated the same error.
- gates.md stated the case count for test-adr0020-contract.sh as "29 at
HEAD", explicitly presented as measured. Running it prints 44; 384756b
added the hook-wiring assertions after the text was written.
- gates.md:913 and :916 described "Both audit skills'" behaviour in the
present tense, three and six lines above :919 saying factory-audit's is
the only copy left.
- The apm-audit-ci block named manifest-parse as a check, said the hook
does not scan for hidden Unicode, and called root lockfile-exists
vacuous. apm 0.28.0 runs ten checks, content-integrity does scan for
hidden Unicode, and there is no manifest-parse row.
Also: the version-bump gate's baseline is documented as the single
merge-base it is not -- it resolves every base with merge-base --all,
intersects the changed-skill sets, exempts a tree-identical skill, and
emits a third sha-suffixed failure form. The gate's own header documents
this correctly; the spec did not. Behaviour is unchanged.
The "none of them need the network" line added on this branch cited a
README section that says the opposite for a fresh clone, and the
check-vale-style-sync rationale said 6 of 17 assertions diffed the Vale
copies where ADR-0025 says 2 diffed and 4 more only located them.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NwD8Egs5r4ndqeFLmhusX2
This commit is contained in:
@@ -27,7 +27,7 @@ Skills are **not** deployed by `install.sh`. They are distributed as plugins and
|
||||
|
||||
Skills, agents, MCP servers, and hooks are distributed as self-contained plugin units under `plugins/`, installed independently via `apm install`, here and in any consuming repo (ADR-0018). Each unit is an **apm package**: `plugins/<name>/apm.yml` plus a hand-authored `plugins/<name>/.apm/{skills,agents,hooks,commands,instructions,extensions}/` tree (ADR-0015). There is no per-plugin `plugin.json` at all — apm reads `apm.yml`, and the repo's one generated manifest, `.claude-plugin/marketplace.json`, is compiled from that source.
|
||||
|
||||
Self-contained is a hard constraint, not a description: a file reference inside `.apm/skills/<name>/` may not reach outside that skill's own directory, and there is no cross-skill sharing mechanism to reach for instead. That is why the Vale styles ship inside the one skill that uses them, `factory-audit/assets/vale/` (ADR-0014, ADR-0025), and why ADR-0020's constants are copied into two validators — the plugin's `validate.sh` and the repo's `scripts/skill-size-check.sh` — rather than sourced from one. The constraint used to be explained by Claude Code's plugin cache-install copying a plugin to a cache; that is no longer the reason and never was the only one. It is stated independently for APM package mode by the agentskills.io spec (`plugins/kyberforge/.apm/skills/skill-author/references/deployment-modes.md`), which is why ADR-0024 consequence 6 pins it as a negative result: ending native install did not relax it, and it is not to be re-litigated on the assumption that it did.
|
||||
Self-contained is a hard constraint, not a description: a file reference inside `.apm/skills/<name>/` may not reach outside that skill's own directory, and there is no cross-skill sharing mechanism to reach for instead. That is why the Vale styles ship inside the one skill that uses them, `factory-audit/assets/vale/` (ADR-0014, ADR-0025), and why ADR-0020's constants are copied rather than sourced from one place: `scripts/skill-size-check.sh` carries them, and so do `factory-audit`'s mode libraries — `scripts/lib-checks-skill.sh:313-316` all four, `scripts/lib-checks-agent.sh:164-165` the two description ones. The plugin's `validate.sh` carries none of them; it sources the library its mode selects. The constraint used to be explained by Claude Code's plugin cache-install copying a plugin to a cache; that is no longer the reason and never was the only one. It is stated independently for APM package mode by the agentskills.io spec (`plugins/kyberforge/.apm/skills/skill-author/references/deployment-modes.md`), which is why ADR-0024 consequence 6 pins it as a negative result: ending native install did not relax it, and it is not to be re-litigated on the assumption that it did.
|
||||
|
||||
Which apm package a new skill belongs in follows from what each one is scoped to. The boundary that matters most in practice is `core` vs `kyberforge`: `core` is the home for cross-cutting, repo-agnostic utility skills that a consumer would want against *their* repo, while `kyberforge` is meta-tooling for the holocron marketplace itself. A skill that authors a target repo's `AGENTS.md` is `core`; a skill that audits a `SKILL.md` against this marketplace's contract is `kyberforge`.
|
||||
|
||||
|
||||
@@ -98,19 +98,32 @@ ADR-0022 makes `metadata.version` mandatory and says a skill change carries a bu
|
||||
|
||||
- **It runs on every push and under a manual `pre-commit run --hook-stage pre-push`.** It does not
|
||||
read `PRE_COMMIT_REMOTE_BRANCH`, so the manual rehearsal really checks it. The pushed commit is `PRE_COMMIT_TO_REF`, or `HEAD` when that is unset.
|
||||
- **"Changed" is measured from the merge-base of the pushed commit with `origin/main`** (local
|
||||
`main` if `origin/main` does not resolve). Readers install from `main`, so "changed" means
|
||||
changed against the `main` the branch started from. The remote branch tip is not the baseline:
|
||||
a second push would excuse an unbumped change the first push already carried.
|
||||
- **A changed skill's version must beat two baselines**: its version at that merge-base *and* its
|
||||
- **"Changed" is measured from the merge-bases of the pushed commit with `origin/main`** (local
|
||||
`main` if `origin/main` does not resolve), resolved with **`git merge-base --all`** — all of
|
||||
them, not the single one git would otherwise pick. Readers install from `main`, so "changed"
|
||||
means changed against the `main` the branch started from. The remote branch tip is not the
|
||||
baseline: a second push would excuse an unbumped change the first push already carried.
|
||||
- **With more than one base, the changed-skill sets are intersected.** A criss-cross history —
|
||||
`main` merges a branch while that branch merges a `main` commit — has two merge-bases, and which
|
||||
one a bare `git merge-base` prints is an implementation detail, so picking one made the verdict a
|
||||
coin flip: a skill already identical to `main` was reported `(not above merge-base)` whenever the
|
||||
losing base was chosen. A skill therefore counts as changed only when it differs from **every**
|
||||
base; differing from none of them, or from only some, means a base already carries the pushed
|
||||
content. A skill that does count as changed must then beat the version at every base it exists
|
||||
at. Both directions are conservative: the intersection cannot exempt a skill that changed since
|
||||
all of `main`'s reachable history, and requiring every base keeps the ratchet.
|
||||
- **A changed skill's version must beat two baselines**: its version at each merge-base *and* its
|
||||
version at the tip of the same `main` ref (ADR-0022's second 2026-09-16 amendment). The tip
|
||||
check stops two branches that make the same bump (`1.0.0` → `1.0.1`) with different content from
|
||||
both landing, since the identical version lines merge without a conflict. A skill absent at the
|
||||
tip is held to the merge-base alone, and so is one whose directory at the pushed commit is the
|
||||
*same tree object* as at the tip: it ships exactly what main ships, whatever route the history
|
||||
took there — a criss-cross merge, a cherry-pick, a backport — so there is nothing for a bump to
|
||||
announce. When `main` has not moved, the two baselines are the same commit. Each
|
||||
failure line names the baseline it missed: `(not above merge-base)` or
|
||||
tip is held to the merge-bases alone, and so is one whose directory at the pushed commit is the
|
||||
**same tree object** as at the tip — compared as object ids, because a tree id *is* the content
|
||||
whatever route the history took to it. That skill ships exactly what `main` ships, so there is
|
||||
nothing for a bump to announce. The intersection does not already cover it: it exempts only when
|
||||
some base carries the content, which a criss-cross history gives and a cherry-pick of a fix
|
||||
`main` already has does not. When `main` has not moved, the tip is itself a base and the skill is
|
||||
checked once. Each failure line names the baseline it missed: `(not above merge-base)`,
|
||||
`(not above merge-base <sha>)` when there is more than one base to tell apart, or
|
||||
`(not above origin/main tip)`. The tip is `origin/main` as last fetched.
|
||||
- **It fails closed when it has no trustworthy baseline:** neither `origin/main` nor `main`
|
||||
resolves; there is no merge-base (shallow clone, unrelated history); or only local `main`
|
||||
@@ -428,7 +441,8 @@ if the library is missing or defines no resolver.
|
||||
|
||||
`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
|
||||
library proves the hook executes the library's text, and a later block pins every repo-authored
|
||||
pre-commit hook's `entry` and `stages`. 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,
|
||||
@@ -436,11 +450,14 @@ 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's case count
|
||||
runs **28 → 27 → 29**, and is **29** at HEAD: 28 at `620f20b` (the ADR-0025 merge), 27 after
|
||||
`4de5b6b` retired the `.pre-commit-hooks.yaml` export, and 29 after `ef27c97` replaced the two-copy
|
||||
hash and its line-count floor with the six one-copy assertions above. There are two 2026-09-16
|
||||
runs **28 → 27 → 29 → 44**, and is **44** at HEAD: 28 at `620f20b` (the ADR-0025 merge), 27 after
|
||||
`4de5b6b` retired the `.pre-commit-hooks.yaml` export, 29 after `ef27c97` replaced the two-copy
|
||||
hash and its line-count floor with the six one-copy assertions above, and 44 after `384756b` added
|
||||
the hook-wiring block. An earlier revision of this section stopped the chain at 29 and called that
|
||||
the figure at HEAD; it was written before `384756b`. There are two 2026-09-16
|
||||
changes here, not one, which is what an earlier revision of this section conflated. Each figure is
|
||||
`bash tests/test-adr0020-contract.sh` run in a worktree at that commit, reading its `Results:` line.
|
||||
`bash tests/test-adr0020-contract.sh` run at that commit — in a worktree for the historical ones —
|
||||
reading its `Results:` line.
|
||||
An earlier revision also opened the chain at 25; that predates the branch squash, no reachable
|
||||
commit reproduces it, and it is dropped as unverifiable rather than carried.
|
||||
|
||||
@@ -482,8 +499,10 @@ against synthetic `mktemp` fixtures — it had never run against the agent files
|
||||
how ADR-0016 could be amended to bless a `disallowedTools` frontmatter field while `validate.sh`'s
|
||||
allowlist still rejected it: spec and enforcer disagreed and every gate stayed green.
|
||||
|
||||
Agents take the ADR-0020 **description** gates (`factory-audit`'s `validate.sh` holds its own copy of
|
||||
those two constants) and, deliberately, **no body word gate**. A skill body is loaded into the
|
||||
Agents take the ADR-0020 **description** gates and, deliberately, **no body word gate**. The two
|
||||
description constants `factory-audit` applies to an agent live in `scripts/lib-checks-agent.sh:164-165`;
|
||||
an earlier revision of this line put them in its `validate.sh`, which carries none of them (see
|
||||
[Duplicated constants](#duplicated-constants)). A skill body is loaded into the
|
||||
caller's context and competes with the live conversation; an agent body becomes the system prompt of
|
||||
a *fresh* context. The rationale for the 900-word FAIL does not transfer. A bats test pins that
|
||||
absence for the agent path of `factory-audit`'s validator — adding a body gate there contradicts the
|
||||
@@ -910,11 +929,12 @@ An explicit `--config` from any other caller still wins, in all three argv forms
|
||||
`--config=/abs`, `--config=rel`), and a relative one resolves against the caller's cwd — matching
|
||||
bare `vale`, not the repo root.
|
||||
|
||||
Both audit skills' Step 1 passes no `--config` either. Step 1 resolves the script relative to the
|
||||
`factory-audit`'s Step 1 passes no `--config` either. Step 1 resolves the script relative to the
|
||||
skill's own directory so the call works from an installed plugin cache; a relative `--config`
|
||||
alongside it would resolve against the cwd instead, yielding `E100 Runtime error … does not exist`
|
||||
and exit 2 — which both skills' fallback misreads as "vale unavailable" and silently downgrades to
|
||||
full LLM judgment.
|
||||
and exit 2 — which the skill's fallback misreads as "vale unavailable" and silently downgrades to
|
||||
full LLM judgment. An earlier revision wrote this paragraph in the plural, for the `skill-audit` /
|
||||
`agent-audit` pair ADR-0025 merged; there is one Step 1 now.
|
||||
|
||||
`tests/test-vale-wrap.sh` regression-tests this against `factory-audit`'s copy — the only one left.
|
||||
Its fixtures are all `SKILL.md`-shaped, and that copy's `.vale.ini` carries the matching glob section
|
||||
|
||||
Reference in New Issue
Block a user