The clean provenance bill was an artifact. Checks 7 and 8 assume `Research doc:`
names a source index whose H2s are slugs, but 30 of 121 corpus entries point at
topic content documents whose H2s are topics. Those 30 produced every new check-7
INFO — all false positives. Check 8 aimed at the same documents, which carry no
`Status:` line at all, would have emitted a large false-FAIL flood; the only thing
preventing it was an unannounced `rd_status != extracted` skip. So "0 new FAILs"
rested on exactly the fail-open class this branch exists to remove, and naively
fixing the skip would have turned the branch red.
Checks 7/8 now run only when the research doc's basename is `sources.md`, and every
other case emits a visible INFO naming the slug. The dangling-path INFO stays ahead
of the basename gate, because a path that does not resolve is rot whatever it is
named. `parse_status` accepts the bullet form and a trailing note after the
backticked value, so a status it cannot read no longer reads as "nothing to check".
Corpus: 36 INFOs of which 30 were false, to 56 of which none are. FAIL stays 0, and
no Status line flipped to `extracted` under the new parser, so no FAIL was
suppressed by luck.
Also closed, each a silent pass: a nonexistent directory, a directory with no
SKILL.md, and extra arguments now exit 2; a UTF-8 BOM no longer defeats frontmatter
parsing; the bare `except Exception: return False` that turned an unreadable file
into a clean pass is gone, with all reads pinned to UTF-8; check 3 walks nested
`references/` subdirectories; `FILL IN:` at end of line no longer escapes checks 1
and 6; duplicate `## slug` blocks and repeated `Research doc:` lines are announced
rather than half-read.
The agent-audit copy carried all of the above unfixed and is now ported, minus the
four fixes that are genuinely N/A at agent scope — it reads a plugin-root
`sources.md` and has no checks 7/8 and no `references/` tree. Its silent exit 0 for
a file outside plugin scope is preserved deliberately: that is a verdict about a
valid file, not a skip, and `check-scope-walkup-sync.sh` pins it. Every exit-2 gate
therefore decides from the argument alone, before the walk-up runs.
`validation-scripts.md` said flatly that silence from the validator is a pass, not
a skip. That sentence is what made a typo'd path dangerous, and both copies are
corrected here. The matching SKILL.md exit-code guidance lands with the audit
rubric change, which touches the same files.
Tests: skill-audit 45 to 65, agent-audit 24 to 43, every new case proven by mutation.
Refs: #111, #118, #121
skill-audit loaded roughly 4,268 words of rubric on every run, most of it
criteria for findings a clean skill never triggers. The auditing guidance moves
into finding-criteria.md, read only when a finding is actually raised, cutting
a clean audit to about 999 words. The named-skill exemption is replaced with
properties, so the rubric stops carrying a list that ages the moment a skill
is renamed.
apm-workflow's `type:` trap sat in one flow while biting several, so it is
promoted to a common gate reachable from all of them; its claim to be
self-contained was untrue once it started routing to apm-install. skill-author's
contract had drifted from body-discipline.md and is realigned, and agent-audit's
field inventory is brought in line with the same split.
configure.md said apm.yml's `type:` field "constrains what .apm/ may
contain" and that changing it later "does not retroactively validate what
is already on disk" — both implying a validation step that does not exist.
Read against the installed apm-cli 0.28.0: PackageContentType controls how
a package is processed during install/compile, apm_package.py only
enum-checks the declared string, and validate_apm_package() branches on the
structural type derived from files on disk, never on the declared field.
There is no content-vs-type mismatch check anywhere.
The hazard is therefore the opposite of what the wording primed for:
silent omission. A package declaring type: instructions while shipping
.apm/skills/ installs no skill and compiles AGENTS.md only, exits 0, and
reports success having shipped none of its primitives. The rule is now to
verify deployed output rather than the exit code. apm-orchestrate carried
the same wording as a Hard Rule and is corrected in step; its separate
defects stay with #120.
Also refreshes the exemplar figures this branch had re-staled. 264a5db set
them to 3,222 words of references; 6cb47f8 then added 63 words and
invalidated them, and the correction above adds more. Re-measured after
all edits: body 237 and whole-file 304 both still hold, references total
3,416. body-discipline.md's "roughly 3,200" moves with it.
ADR-0020 is deliberately untouched — it self-pins its citations to
f9b919d — as is the git-commits negative example pinned to 5e23250.
Refs #99
body-discipline.md and contract.md both cite apm-workflow by hard number
as the reference dispatch shape, and instruct authors to calibrate against
its body word count. The retrofit in 1e4aab5 moved every one of those
figures: body 421 -> 237, whole-file 554 -> 304, references 3,006 ->
3,222. Measured, not estimated.
The quoted closing line was stale too -- it now carries a continuation
clause, so it is quoted with an ellipsis rather than as a full sentence.
These are positive exemplars in live guidance, so they are refreshed
rather than pinned. That is the opposite treatment from the git-commits
negative example in the same file, which is pinned to 5e23250 precisely
so it keeps describing the pre-retrofit state. ADR-0020 carries the same
figures and is deliberately left alone: it self-pins every citation to
base commit f9b919d, and refreshing it would destroy the record of what
the decision was taken against.
Refs #99
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MWb5RQgCL1ye7cGp2RPb2u
body-discipline.md required every reference load to use the literal
'If X, read references/file.md' form and called anything else a generic
pointer. ADR-0020's own cited dispatch exemplar, apm-workflow, uses a
bare table plus one closing line, so an author could not satisfy both --
and the rule reliably produced duplication in exactly the bodies the
contract exists to keep short.
Resolves#109 with its option 1: a table row already pairs a condition
with a target, so where a body dispatches, the table is the wiring. The
literal form is what a body needs when it loads a reference without a
table.
Two corrections to the issue as filed. There is no Vale conflict --
PaddingPhrase.yml only matches 'see references/ for more info' and never
fired on the exemplar, so this is a one-file prose fix and no rule
changes. And gitea-workflow carried the predicted duplication: a
three-row table restated underneath as three conditionals. Removed, body
227 -> 148 words.
Closes#109
The body-discipline rubric cited git-commits as it stood before the ADR-0020
retrofit -- twelve Gotchas, 387/1102 words, line numbers :31-:52. Every
figure was correct for that version and none survives in the current file,
so the example is now anchored to commit 5e23250 and marked not to be
refreshed against HEAD.
More than staleness: row four called the secrets Gotcha a paraphrase FAIL
because step 2 restated it. Wave 2 followed that reasoning, deleted the
always-loaded copy, and left the amend branch able to commit a credential
unchecked -- dispatch loads exactly one flow file. The paraphrase rule now
carries its missing precondition: delete a restating Gotcha only when the
surviving copy is reachable from every branch that needs it, and relocate
a multi-branch safety gate into the body rather than dropping it.
Diffing each retrofitted SKILL.md against its replacement references/ files found
rules that existed on main and now existed nowhere — relocated in intent, deleted in
fact. A trim that loses a rule is not progressive disclosure, it is data loss with a
smaller word count.
Three had no survivor. The least-privilege guidance for `tools` kept its mechanics
and lost the "restrict to what the agent needs" half, so the remaining text read as
encouragement to omit the field. The improve flow lost its regression check, so
nothing compared the closing audit against the pre-edit state and a PASS quietly
becoming a SUGGESTION went unnoticed — restored on both halves of the author pair,
since agent-author had dropped its equivalent too. And agent bodies lost "would the
agent get this wrong without it?", which mattered more than it looks: ADR-0020
deliberately sets no body word gate for agents, three of the four already sit
between 933 and 1,199 words, and the delegation check only fires on procedure a
skill already owns. That heuristic was the only brake left.
Two more were reachable only from the wrong scope. agent-author tells the reader to
load only the file for the resolved scope, but the mcp__ glob syntax for
disallowedTools and the five tools no subagent ever receives had both landed in
project-user-scope.md. disallowedTools is the ONLY permitted fence at plugin/APM
scope, so the scope that needs the syntax most could not reach it, and a plugin-scope
run could write a body telling the agent to ask the user a question.
Two documents were actively wrong rather than merely thin. agent-audit told auditors
that validate.sh resolves boundary targets for skills only; it runs at both scopes,
so the auditor was hand-resolving what the script had already decided and could
contradict it. And skill-audit routed to its script-troubleshooting reference
whenever validate.sh "fails" — but it exits 1 on ordinary content FAILs, the normal
outcome for the whole #99 population, so 1,302 words loaded on nearly every audit.
A context-budget regression inside the skill that enforces the context budget.
Finally, two illustrations taught the shape the gate ERRORs on, unfenced, while an
adjacent rubric called it a hard ERROR.
LESSONS.md records the reference-chain depth rule flipping from "one level deep" to
"two hops, never three". ADR-0020 is silent on it and the reversal rode entirely on
the diff; the looser rule is what mandatory dispatch requires.
Refs: #99
ADR: 0020
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015W3iwF9ncfRZddGBxsMCYi
Six defects, each one a place where two files that an author reads in the same
sitting told them different things — or where the trim dropped a rule and nothing
noticed because no gate covers prose.
**"Use proactively" contradicted itself across the pair.** All three agent
templates said to add it where the runtime should delegate unprompted, while
`agent-audit`'s `KyberforgeCopilot.ProactivePhrase` rule grades it a hard FAIL in
any `*.agent.md` — which is the Copilot half of every project/user pair *and* the
vendor-neutral plugin-scope file, since that compiles to a real Copilot agent
downstream. Following the template produced a file the repo's own gate rejects.
The phrase is now permitted in exactly one place, the Claude Code `.md`, and
`references/contract.md` carries the per-file table plus the consequence authors
ask about next: a pair whose CC half has it and whose Copilot half does not is
correct, because `agent-audit` checks that both halves describe the same job, not
that they match word for word.
**The output-schema rule contradicted itself inside one file.** `contract.md`
said any content only one branch reaches moves to `references/`, and then offered
an "Output format template" body pattern with no qualification. Stated once now,
so it is not re-litigated: an output schema stays in the body only when every flow
produces it and it is roughly 50 words or less. No third option.
**Gotchas tiers disagreed with the script.** `validate.sh` emits the entry count
through `suggest()` and exits 0, while `skill-author` and `skill-audit` both
called more than five entries a FAIL. Whether a given gotcha earns its place is
judgment, so the prose moves to the script's tier rather than the reverse. The
paraphrase rule stays a FAIL and is explicitly marked as the auditor's call — no
script detects it.
**The dispatch exemplar was cited at the wrong number.** `apm-workflow`'s body is
421 words; 554 is its whole-file count. Both `contract.md` and `body-discipline.md`
cited 554 while describing a body budget, so an author calibrating against the
exemplar overshot by ~30% — the exact whole-file/body-only conflation those two
sections exist to warn against, reproduced inside the warning.
**"Error handling" came back as a required body element.** It was one of four and
is the one that gets dropped, and dropping it is not neutral: an agent handed
malformed input with no instruction invents a recovery, and a subagent's invented
recovery is invisible to its caller until the output is wrong. Restored in
`agent-audit`'s rubric as a SUGGESTION, in `agent-author`'s contract and both
scope checklists as a required element, and as an `## Errors` section in all three
templates.
**`skill-author` Step 4 gains the one check the audit misses.** An empty body
reports `PASS SKILL.md body word count 0` — a word gate cannot tell "concise"
from "absent". Step 4 now hand-checks for a non-empty section, and its commit
verification is conditioned on actually being inside a git worktree, which a skill
under `~/.claude/skills/` is not.
Also here: absolute repo paths removed from `skill-author`'s SKILL.md and
contract.md in favour of naming the skill (`zoom-out`'s description is quoted
inline instead of pointed at), the boundary-target universe documented to match
the resolver, a two-hops-from-SKILL.md limit on reference chains, and
`new-agent.sh`'s next-steps output naming the description budget and the
deliberate absence of an agent body gate.
Refs: ADR-0020
The ADR-0020 body trim took `skill-audit` from 2,623 body words to a dispatch
shape, and two things went out with it that were not padding.
The manual structural fallback was one. Its replacement was a single sentence
telling the auditor to report an INFO when `validate.sh` cannot run — so with no
`python3` or no PyYAML, `skill-audit` reported the gap honestly and then audited
nothing structural at all. Every ADR-0020 measurement, the whole-file ceilings,
the name-to-directory match, the `references/` pointer check and the script
hygiene checks silently left the audit. A skill's whole Structure dimension
hanging on one optional interpreter is the same vacuous-pass shape the gate
scripts were just fixed for, one layer up.
The `E100 Runtime error ... does not exist` diagnostic was the other. That exit
code means an explicit relative `--config` was passed to `vale-wrap.sh` while
vale itself was installed and working; without the note, Step 1's fallback reads
exit 2 as "vale unavailable" and downgrades the description, body-discipline and
patterns dimensions to full LLM judgment for a config error it could have fixed.
That misreading is already recorded in CONTEXT.md as the reason both audit skills
stopped passing `--config` at all.
Both are restored in `references/validation-scripts.md`, loaded only when a Step 1
script fails — so the body pays nothing for them on a clean run, which is what the
dispatch pattern is for. The file also carries the by-hand boundary-target
procedure and the three ways to misread the result, including that
`INFO ... DID NOT RUN` is not a pass.
`references/file-structure.md` gains the one sanctioned spelling for a cross-skill
reference. The possessive form (``skill-audit's references/validation-scripts.md``)
is the only spelling both rules accept: a full repo path is what that section
already forbids, and a bare `references/<file>.md` is now a hard ERROR from the
ADR-0020 pointer check, which requires the file to exist in the skill's *own*
directory. Without the rule the two constraints look mutually exclusive.
Refs: ADR-0020
Skill name+description pairs are preloaded into every session, costing
~6,200 tokens across 39 skills before any skill is invoked. The authoring
rules mandated that growth: skill-author:104 and description-quality.md:21
both required padding, while skill-author:102 (the deflating rule) had no
FAIL condition behind it.
Gates (blocking, no baseline file):
- description 250 chars SUGGESTION / 400 FAIL, measured on the folded
YAML value
- body-only 600 words SUGGESTION / 900 FAIL, independent of the unchanged
whole-file 2770-word / 500-line spec backstop
- every boundary-clause routing target must resolve to a real skill or
agent; catches skill-improve, neuledge-context and gitea-labels
- agents take the description gates but deliberately no body gate; a test
pins that absence
Vale: DescriptionOpener widened to ^This\b, new CompositionNote rule
banning architecture notes from descriptions. 10 hits, 0 false positives.
Kyberforge's own four skills retrofitted: descriptions 3,364 -> 938 chars
(-72%), bodies 8,306 -> 2,487 words (-70%), all via the apm-workflow
dispatch pattern. Fixes the skill-improve dangling route and the
agent-author misroute to manual review.
Also fixes a pre-existing false positive where any line-initial 'read '
was flagged as interactive input, which had already caused two scripts to
be rewritten around it.
Refs: ADR-0020
Add Research doc: pointer to all 7 agentskillsio entries so the new
validate-provenance.sh upstream checks can resolve the research source.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Records the upstream agentskills.io sources that informed skill-audit,
continuing the research → docs → skill provenance chain.
- New references/sources.md with 7 extracted sources attributed to skill files;
agentskills-llms-txt demoted to discovery-only comment per skill-author precedent
- source_keys frontmatter added to SKILL.md (5 slugs), references/body-discipline.md
(agentskills-spec, agentskills-best-practices), and references/description-quality.md
(agentskills-spec, agentskills-optimizing-descriptions)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Add references/description-quality.md and references/body-discipline.md to
skill-audit — condensed, rubric-focused extracts from the agentskills.io
specification docs. Both files are loaded conditionally via progressive
disclosure triggers added to Step 3 (Description and Body discipline
dimensions), so the agent consults the spec source when a finding is
borderline rather than relying solely on inline heuristics developed
during the skill-write authoring cycle.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>