Seven ADRs described code that no longer exists or behaviour the gates do
not have. Where the wrong text came from main it carries a dated
correction; where this branch introduced it, it is fixed in place, because
main never published it and there is no record to preserve.
Fixed in place, branch-introduced:
- ADR-0021's 2026-09-14 correction asserted apm audit --ci "was never a
drift gate at all". It is one: it replays the install and diffs. The
claim contradicted this branch's own AGENTS.md and gates.md.
- ADR-0015 said unconditionally that no pre-push hook needs the network.
The guarantee holds only once apm install has populated apm_modules/.
- ADR-0014's 2026-09-16 correction said restoring .pre-commit-hooks.yaml
would ship a hook that fails for every consumer, because their checkout
has no lib-boundary-resolver.sh. pre-commit clones the whole hook repo
and skill-size-check.sh resolves the library from BASH_SOURCE, so the
hook would work.
- ADR-0019's "twelve hooks pass under unshare -rn" matched neither HEAD
(8) nor main (14), and stated the offline guarantee unconditionally.
Corrected, inherited from main:
- ADR-0022 and ADR-0013 named skill-frontmatter's pre-commit hook as the
enforcer of mandatory metadata.version. That hook was deleted on this
branch; the check lives in skill-size-check.sh.
- ADR-0022 enumerated the tip rule's carve-outs as a closed list and
described a single merge-base. The gate also exempts a tree-identical
skill and intersects every base from merge-base --all, and emits a third
failure form. 8cfd54f said the documented behaviour did not change; it
did. The gate is correct and is unchanged -- the record was not.
- ADR-0020's Decision still routed description overflow to README.md, its
ADR-0025 amendment pointed the mirrored constants at validate.sh, which
holds none, and its Enforcement table still named the two deleted
validate.sh paths.
- ADR-0015's Status claimed every plugin's plugin.json is pack output;
none exist.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NwD8Egs5r4ndqeFLmhusX2
Group 3 of the validated PR #135 review fixes. Every figure and commit
citation below was re-verified at HEAD before being written.
ADR and architecture:
- #7 ADR-0025 cited 61b0b9c, which no published branch reaches. Repointed
to 620f20b (identical parent tree, reachable from the PR branch), with a
note that neither is reachable from origin/main. The parser-drift
paragraph now credits 598a7c3 (the reachable PR #129 squash) and keeps
484357a only as a pre-squash parenthetical.
- #8 architecture.md dropped the pointer at the LESSONS.md entry this
branch deleted.
- #9 architecture.md's ADR entry points now name ADR-0015 (the one
compiler) and ADR-0024, and list ADR-0024 as superseding ADR-0017.
- #10 ADR-0024 section 4 rewritten: the standing patch-bump rule is
apm-workflow's configure.md, not ADR-0006's, and this change does not
trigger it. ADR-0015:93 carries a correction for the misattribution.
- N5 ADR-0021 gained a Correction note for the deleted
scripts/check-manifests.sh (e647f14).
gates.md:
- #11a the four ADR-0020 constants live in lib-checks-skill.sh:313-316 and
lib-checks-agent.sh:164-165, not in validate.sh.
- #11b the pretty-format-json exclude is two alternations expanding to
three tracked files, including .claude/apm-hooks.json.
- #11c the ADR-0020 contract suite runs 28 -> 27 -> 29 (620f20b,
4de5b6b, ef27c97), 29 at HEAD; the unverifiable 25 is dropped.
- #11d the boundary resolver is one copy since ef27c97.
- #12 apm-audit-ci documents the 10 root checks and the 1 plugin check
apm 0.28.0 actually runs, that content-integrity IS the hidden-Unicode
scan, that manifest-parse is not a named check, and that the hook needs
a completed apm install. The offline claim is qualified accordingly.
- N9 gates.md:142-146 verified to still match the hook description.
AGENTS.md:
- #12 the no-network session rule is qualified to a populated
apm_modules/.
Audit note:
- A1 hook counts corrected to 27/9 -> 26/8 -> 27/9 -> 26/8 (26 and 8 at
HEAD) and the dangling pointer dropped.
- A2 skill-size-check.sh is 509 lines with the resolver sourced, not 1,522
embedded; citations repointed to skill-size-check.sh:323-335 and
lib-checks-skill.sh:235-283 (fail() at :265 and :280), and that library
is 627 lines.
- A3 consumers receive 15 test files across 5 skills; 16 tracked test
paths repo-wide.
- A4 the "do not run apm update on this branch" instruction is marked
superseded, with the branch-aware guidance in its place.
- Finding 31's "true orphans" claim corrected for HOTL and Sycophancy,
both still used in core/ai-constitution.md.
Same class, found during group 2:
- skill-author's deployment-modes.md no longer points at .mcp.json
configs (deleted in c96ca9c); metadata.version 1.0.2 -> 1.0.3.
- git-orchestrate's context contract clarifies that
user_config_overrides is caller-supplied session state, not a config
read. The field name is unchanged.
- B3 root apm.yml's executables.allow comment: grants are version-blind
in apm 0.28.0, so the #2.0.0 suffix is cosmetic to apm and a bump does
not break the hook; the suffix stays because
check-executables-allow-sync.sh requires it.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NwD8Egs5r4ndqeFLmhusX2
Why: for a local-path marketplace entry, apm treats a root description: as
an override of the package's own apm.yml, reported only at verbose level,
so the "two copies stay identical" rule had no enforcement. Same fix as
2def060 made for version:.
Implementation Notes:
- All six root copies matched their package apm.yml before removal; the
compiled marketplace.json descriptions are unchanged.
- apm-workflow references now scope the "omit it" advice to local-path
entries: on a remote entry, version: is the semver range that selects
the tag, and version: or ref: is required.
Impact: ADR-0021 amended; the package apm.yml is the single source.
ADR: 0021
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Why
The two audit skills carried 1,724 lines of byte-identical duplication: the ADR-0020 boundary
resolver (1,061), vale-wrap.sh (526), the Vale style rules (44) and the Contributing-files parser
(93). Nothing shared them — they were held in sync by a 413-line pre-push gate and its 797-line
test suite. Sync-by-gate had already failed once: at 484357a the two parser copies drifted into
different spellings of the bullet loop while a docstring asserted they were identical. That drift
was behaviour-neutral and was re-unified by hand at 598a7c3, so the copies were identical at merge
time — but nothing had caught it, and the next drift need not be neutral.
Implementation Notes
Self-containment binds BETWEEN skills, not within one. The agentskills.io spec forbids reaching
across skill directories, which is why two separate skills needed embedded copies; two files inside
ONE skill may source a third. That is the whole reason the merge removes duplication rather than
relocating it.
The union of both bodies measured 1,532 words against BODY_MAX_WORDS=900, and only 211 of those
words were shared, so SKILL.md is a dispatch body. Step 0 resolves the flow from the target path
before any validation, and its table mirrors validate.sh's detection exactly: a directory holding
SKILL.md or a SKILL.md file (skill); a *.agent.md, or a .md directly under an agents/ directory
(agent); anything else stops without running a validator. Steps 1-3 live in
references/skill-flow.md and references/agent-flow.md, and gotchas that apply to one flow live in
that flow's file, since it is loaded on every invocation anyway. If validate.sh reports on the
other artifact type, the body restarts at Step 0.
Named factory-audit rather than forge-audit because forge is a live skill, and a family prefix that
matches a live sibling reads as ownership rather than membership.
The description carries one arrow per boundary target, because ADR-0020 resolves only the first
target after an arrow. It drops the quoted "audit this skill"-style phrases, which restated
"audited" in a second register (ADR-0020's duplicate-register rule). 241 characters, Gotchas 16%
of the body: no size SUGGESTIONs.
The boundary resolver stays embedded in two files rather than imported: a cache-installed plugin
cannot read outside its own directory, and the repo-root hook resolves via .pre-commit-hooks.yaml
where entry[0] is the only token pre-commit rewrites, so no single file is reachable by both.
tests/test-adr0020-contract.sh hashes both copies for byte-identity, and asserts validate.sh sources
the resolver and that no third copy exists.
The entry scripts classify the target from its resolved parent directory, so a bare agent filename
typed inside agents/ works; resolve SCRIPT_DIR CDPATH-safely; and exit 2 when a lib-*.sh is
missing, rather than dying with exit 1, the tier the flows relay as real findings.
The provenance run functions stash their findings code in KYBERFORGE_PROV_RC and
return 0, so validate-provenance.sh calls them UNTESTED. Testing a function's
status (`f || RC=$?`) disables errexit for its entire body, and no subshell or
`set -e` inside can re-arm it once the call sits in a condition context
(measured, both spellings). Their error paths use `exit`, which is unaffected
either way; this keeps errexit armed for anything added later.
Case 0's readability guard reads the file instead of asking `[[ -r ]]`. `-r` is
access(2), which answers yes for uid 0 even on a mode-000 file, and this repo's
dev environment is root -- so the guard could never fire where it exists to fire.
A read attempt is also the stricter question, catching EIO. This is the reasoning
scripts/check-vale-style-sync.sh carried before this commit deleted it; the
hazard did not go with it.
All three entry scripts are CDPATH-safe, vale-wrap.sh included: both of its cd sites are cleared,
the --config resolution and the directory-mirror walk, where an exported CDPATH would otherwise
print a decoy path into the -print0 stream and build the mirror from the decoy's files. The two
remaining bare cd calls take absolute paths, which CDPATH is never consulted for.
Impact
BREAKING: skill-audit and agent-audit no longer exist as invocable skills. kyberforge goes to
2.0.0 (catalog 0.4.7).
Check logic is unchanged: differential runs of the old and new validators across every skill and
agent produced byte-identical stdout, stderr and exit codes, and the reconstructed Python payloads
differ only in comments and the references/field-inventory.md -> agent-field-inventory.md rename.
One doctrine governs the tiers: exit 0 is audited and clean, exit 1 is audited with findings OR a
target present but unreadable, exit 2 is that nothing was audited at all. Edge paths DID change,
deliberately (full table in ADR-0025):
- a missing target exits 2 (never ran), not 1, under its own "does not exist" message; detection is
by path shape, so a shape-matching path that is simply absent used to reach the validator and come
back as a FAIL against a file that never existed;
- an unshaped target exits 2 under the generic "matches neither" message, and a directory with no
SKILL.md under a third, distinct one -- three exit-2 messages, not one;
- a dangling symlink or a symlink loop stays exit 1: it is present but broken, which is a finding
about the artifact rather than a usage error;
- a SKILL.md file path is audited as its skill directory instead of refused;
- a .md agent outside an agents/ directory is refused rather than audited;
- a missing script library, a missing python3, a missing PyYAML, and no argument at all each exit 2.
validate-provenance.sh already exited 2 for the last two; validate.sh now matches it.
.pre-commit-hooks.yaml is a published contract consumed by external repos. Both hook IDs and both
files: regexes are unchanged; only entry: and description: moved.
scripts/check-vale-style-sync.sh (413), scripts/sync-vale-styles.sh (21),
tests/test-check-vale-style-sync.sh (797) and agent-audit/scripts/README.md (47) are deleted. The
checker made 17 assertions: 6 compared the two Vale copies and are moot; 10 are rehomed into
tests/test-vale-wrap.sh (case 0, cases 28-31, and the suite's Vale-absent skip); and the
cross-manifest files: agreement check, which selected hooks by entry: and so could not survive both
hooks sharing one, is ported as case 33 pairing hooks by id:. Cases 28, 30 and 33 carry mutation
self-tests; narrowing the local skill prefilter to 6 of 38 SKILL.md files now fails the suite.
Skills go 39 to 38. Pre-push goes 9 repo-authored hooks to 8.
ADR: 0025
BREAKING-CHANGE: the skill-audit and agent-audit skills are removed. Both flows are served by
factory-audit, which auto-detects whether it was handed a skill directory or an agent file.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YR2CjVumUbEGWcMikcoXBD
A five-agent review of today's seven commits found no executable
regressions and no dangling references, but a set of documents still
asserting, in present tense, machinery that ADR-0024 and its commits
removed. This corrects them in place, keeping the original text as the
historical record wherever the repo's amendment convention applies.
LESSONS.md: the 2026-06-21 entry prescribed a `claude plugin validate`
sweep that now fails on every plugin, so it is marked superseded with
the surviving gates named. The 2026-08-09 entry gained a recurrence
note: today's manifest deletion broke apm's MCP propagation exactly as
that lesson describes, and its prescribed repo-local grep could not
have caught it, because `plugin_parser.py` ships in the apm toolchain
installed outside this repository.
ADR-0019, ADR-0011 and ADR-0021: amendments extended to passages the
earlier correction passes stepped over -- a dead native-consumer guard,
Consequences bullets still calling for a `plugins/gitea/.mcp.json` that
must not be recreated, and a drift-gate list naming a deleted script.
ADR-0021's list is down to one gate, not two: `apm audit --ci` never
read `description` and was never a drift gate.
architecture.md and enrichments.md: the self-containment constraint is
restated on its live source, the agentskills.io APM package-mode spec,
rather than on Claude Code's plugin cache-install, which ADR-0024
consequence 6 pins as a superseded rationale. releasing.md's pointer to
the deleted sync script is rewritten as history.
tests/run-bats.sh and scripts/lib/batch-run.sh: comment-only. The
`.claude/skills/` exclusion comment claimed a duplication that is not
live yet; apm does not strip `tests/`, and the deployed tree is empty
of them only because the lockfile still resolves the six dependencies
to a pre-ADR-0024 commit carrying the flat mirror. The exclusion is
correct but forward-looking, and now says so.
SIMPLIFICATION-AUDIT.md: reconciled against what the commits actually
did. Two closed findings recorded conclusions that ADR-0024 reversed
hours later; findings 1, 3, 31 and 35 carried prescriptions voided the
same day; finding 28 is now recorded as having moved backwards, with
docs/adr/ measured at +336 lines over the day. The section 1 headline
table is re-measured at a6434e0 and labelled with its basis. The
ADR-0012 contradiction between finding 2b and section 8 is resolved in
2b's favour after reading the ADR: only finding 24 is governed by it.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YR2CjVumUbEGWcMikcoXBD
A six-agent review of today's commits found three documentation defects
that the apm-only change left behind. All three are records that describe
deleted machinery in the present tense; no code or gate is affected.
ADR-0021 was the one ADR that ADR-0024 invalidates without carrying an
amendment note -- 0001, 0006, 0011, 0013, 0014, 0015, 0017, 0018 and 0019
all got one. Its Context section still compiles a plugin description into
four generated files and its Consequences section still names eight, but
718c79a deleted the per-plugin manifest pairs and 0dffff3 deleted the
.github/plugin/marketplace.json mirror. One target survives. The decision
itself is untouched: the note marks the counts historical rather than
rewriting them, since the staleness hazard that motivated the ADR is
exactly what shrinking the blast radius does not fix.
That note also lands the one ADR-0021 has promised since it was written:
its Context section said "see the note below" about the codex profile's
removal and no such note has ever existed in the file.
SIMPLIFICATION-AUDIT.md's section 8 still asked whether Copilot reads the
legacy mirror path. Finding 2c answered that at 11:35 and 0dffff3 acted on
it; two later passes over section 8 (d2480b8, 061bb3d) each checked off a
different question and stepped over this one. Closed with the answer that
already shipped: Copilot's discovery falls through to .claude-plugin/,
so what the deletion cost is discovery-order preference, not consumability.
ADR-0020 cited plugins/bin/skills/zoom-out/SKILL.md:4 as end-to-end
verification evidence. That path is mirror, deleted by ADR-0024. The .apm/
source and the deployed copy both still carry the flag and the pass-through
still holds, so the citation is narrowed to the two live paths rather than
the finding being withdrawn.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YR2CjVumUbEGWcMikcoXBD
The Context section still described .agents/plugins/marketplace.json
(apm's codex profile) as an existing, unaffected generated file. It
was removed today in 568ca74; point to the removal instead of leaving
the text describing a file that no longer exists.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YR2CjVumUbEGWcMikcoXBD
Neither has a real consumer: Codex is not a supported target, and
mattpocock-skills was the sole remote marketplace.packages[] entry
forcing apm-marketplace-check and apm-pack-check-clean to git
ls-remote on every push. Removing both drops .agents/plugins/marketplace.json
(the codex output artifact) and makes every pre-push hook resolve
fully offline. Updates README, AGENTS.md, gates.md, architecture.md,
and ADR-0015/ADR-0021 to match.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YR2CjVumUbEGWcMikcoXBD
Why: two blind verifiers re-ran the five preceding commits and found four
defects of the same class this branch exists to close -- a confidently stated
measured claim that does not survive re-measurement -- this time inside the
fixes themselves.
- AGENTS.md:41 still carried both phrasings c68e864 reports having corrected.
`grep -rn repo-defined` returned exactly one hit repo-wide: that line, in the
file every session preloads. 4d336bb edited the line directly above it.
- ADR-0021 asserted twice, in the section justifying that no gate is added,
that the ADR-0020 validators "never open an apm.yml". All three open and
yaml.safe_load it (skill-size-check.sh:342, both validate.sh). The conclusion
survives -- none reads the description: key, and their globs are SKILL.md and
*.agent.md only -- but the stated mechanism is falsified by one grep.
- architecture.md said the ADR directory holds 20 numbered ADRs; c7ba3d2 made
it 21, and c68e864 audited that file for exactly this class of stale count.
The number is dropped rather than corrected: `ls docs/adr/` is already the
index, so a count in prose is a second thing to maintain.
- gates.md's new three-verdict table said `-> name` promotes an unresolved
target to ERROR. Reproduced with fixtures: NAME_HYPH (skill-size-check.sh:543)
requires a hyphen, so `-> gitea-prs` is checked and `-> triage` is not
extracted at all, and the unicode arrow is never recognised. The SUGGESTION
text advises that spelling, so taking its advice can silence the finding. The
gap is now documented as a defect; nothing covers it, since the one arrow case
in test-adr0020-targets.sh happens to use a hyphenated target.
Implementation notes:
- AGENTS.md:48's coverage claim is shrunk rather than chased. Restoring six
glossary entries did not make it true: 12 more sampled terms are undefined,
three of them (trigger/capability/boundary clause) used inside CONTEXT.md
itself. It now says CONTEXT.md is the glossary and is not exhaustive.
- CONTEXT.md's output profile and near-miss entries are corrected against their
sources. The first stated a false exclusion -- .github/plugin/plugin.json IS
apm-generated; only the marketplace mirror has no profile. The second
inverted its source's referent: description-quality.md defines a near-miss as
a query, not a sibling skill.
- The strict-mode message named jq, which no suite guards on (`command -v jq`
appears nowhere in tests/), while omitting python3/PyYAML, which three do.
- README's git and gitea bullets now name git-workflow and gitea-workflow.
ADR-0021 leaves README the only inventory and architecture.md now points at
it, so the two bullets that were short had to be completed.
- ADR-0018's 2026-08-14 correction is marked superseded in place. It asserted
machine state in the present tense that its own 2026-08-17 note retracts.
- ADR-0021's remaining errors: six files -> four (measured from de84d1b), the
wiki description's length 114 -> 96 chars, the codex self-contradiction, the
cost argument overstating bumps already owed for any skill addition, and two
claims about files this branch went on to edit.
- The "15 of 17 suites" figure is restored where I had removed it: it is a dated
record of one incident, not a live count, and four sites now describe it the
same way.
Impact: 16/16 pre-push hooks pass, suite 24 passed 0 skipped 0 failed. No
behaviour change; every edit is prose or a comment.
Refs: #105
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TmFqzpuExLJv3m114XVE9w
Why: enumerating a plugin's skills in its published description has now failed
three times in four days, the third time inside the correction for the second.
bb9158d (Aug 14) fixed core's description, which described bin's skills. That
failure was wrong content, not an incomplete list -- a syntactically perfect
four-item enumeration that belonged to a different plugin -- so enumerating
harder could not have caught it, and bb9158d's own fix replaced the enumeration
with a domain boundary. 65bac15 and de84d1b then cited that precedent while
doing the opposite, and the re-enumeration is itself incomplete: caveman and
zoom-out are unnamed in bin, git-workflow is unnamed in git despite 65bac15's
message claiming it was added, and gitea-workflow is unnamed in gitea. 23 of 27
skills named at the third attempt.
Nothing checks any of this. check-manifests.sh does not contain the string
"description"; apm audit --ci, apm pack --check-clean and
sync-plugin-content.sh --check all compare compiled output against apm.yml, so
their job is to propagate whatever it says byte-for-byte. The wiki claim passed
all fourteen pre-push hooks every day it was published. Enumeration also makes a
marketplace release the consequence of a directory appearing.
Implementation notes:
- ADR-0021 records the decision, the three failures, and why no gate is added:
the check is one-directional, cannot see an invented capability, and a
correspondence map would relocate the same per-skill maintenance one file over.
- bin 1.1.4 -> 1.1.5, git 1.3.4 -> 1.3.5, gitea 1.3.5 -> 1.3.6,
marketplace.version 0.4.4 -> 0.4.5, all PATCH under per_package.
- apm.yml's top-level version: is restored to lockstep at 0.4.5. It has moved
with marketplace.version in all eleven commits that ever touched the file
until this branch left it at 0.4.2, and apm.yml's own comment records it as
live and consumed by apm audit.
- git's boundary names the pre-commit hooks explicitly: pc-author and pc-run are
not git operations, and a boundary drawn narrower than the contents is the
same defect one level up.
- Regenerated with apm pack for the marketplace manifests and
sync-plugin-content.sh --all for the per-plugin ones; both mirrors verified
byte-identical.
Impact: consumer-facing. 16/16 pre-push hooks pass, suite green, generated diff
is 26 insertions and 26 deletions across 8 files with no incidental churn.
Refs: #105
ADR: 0021
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TmFqzpuExLJv3m114XVE9w