Two reproduced bugs in check-skill-version-bump:
- The origin/main-tip check fired even when the pushed skill was
byte-identical to main's tip, so a cherry-pick or backport failed a
push that ships nothing. The merge-base intersection ea119d8 added
covers that only when some base carries the content, which a
criss-cross history gives and a linear one does not. A new
same_subtree compares tree object ids, so the exemption holds
whatever route the history took.
- The failure line reported "baseline: none" when the skill was absent
at every merge-base but present at the tip, and the Fix: line then
named no version. The author writes the natural 1.0.0 and gets a
second blocked push. It now falls back to the tip's version.
ADR-0022 is not amended: the documented behaviour does not change, and
ea119d8 set the precedent by fixing the same failure class script-only.
1614bce verified that executables.allow grants are version-blind and
corrected ADR-0019, gates.md and apm.yml, but missed the gate script's
own header and its operator-facing FAIL message, which still told the
reader deployment was silently broken, and gates.md's hook summary,
which still called it a silent-failure guard. All three now match.
Also: README's offline guarantee carries the populated-apm_modules
condition gates.md and AGENTS.md already state; the scripts/ layout row
drops "sync" for the three deleted sync scripts; the check-rtk-prefix
README rationale names the 12 subdirectory READMEs that survive rather
than the skill-root ones this branch deleted; gates.md re-cites its
three head -1 sites by enclosing function per its own :238 rule; and
deploy-manifest drops a pointer to a provider-manifest.sh that has
never existed on main.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NwD8Egs5r4ndqeFLmhusX2
Removing the mattpocock-skills entry is a minor catalog change under
apm-workflow's marketplace policy, not a patch. Also describe Copilot
support as reached through apm rather than a native Copilot CLI
marketplace (ADR-0024), and rebuild marketplace.json with apm pack.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Why: .pre-commit-hooks.yaml and its release-tag gate served external
consumers that do not exist. No repo on the Gitea instance pins these
hooks, and the README names apm as the only supported install path. The
mechanism was also already failing: skill-size-check.sh changed after
v2.0.1 with no tag cut, and the gate cannot fire through Gitea's merge
button. (Simplification audit finding 36.)
Implementation Notes:
- Delete .pre-commit-hooks.yaml, scripts/check-release-needed.sh,
tests/test-check-release-needed.sh and tests/test-vale-hooks-consumer.sh,
and remove the check-release-needed pre-push hook. The repo: local
skill-size-check and vale-audit-prefilter-* hooks are unchanged.
- ADR-0014 is amended, not retired: its runtime decision to bundle Vale
inside factory-audit stands. The amendment keeps the entry[0]-only
constraint (LESSONS.md:101,105) in case the export returns. ADR-0025
gets a pointer.
- test-vale-wrap.sh: drop case 33 (the cross-manifest drift check) and
case 28's hook-scope half, which read the published manifest. Case 32
now also requires each hook to select every tracked file of its class,
which keeps case 33's one-plugin-narrowing guard, with a mutation test.
- test-skill-size-check.sh and test-adr0020-contract.sh now assert the
hook contract and verbose: true on .pre-commit-config.yaml only.
- gates.md: pre-push count goes from 9 to 8 authored hooks (11 to 10
reported), and the Release table, the External consumers section and
the two-manifest scope table are removed. README and script/test
comments no longer describe the export as live. The resolver comment
is edited identically in both copies.
- The v1.0.0/v2.0.0/v2.0.1 tags are left in place; they are inert.
ADR: 0014
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Why: the docs said to discard a refreshed apm.lock.yaml on a feature
branch because the refresh records main's commit, but the branch's own
lock records a (older) main commit too, and the SessionStart notice gave
the same "commit or discard" advice on every branch.
Implementation Notes:
- check-apm-current.sh picks fixed advice by branch: commit or discard
deliberately on the default branch (origin/HEAD, else main), discard and
reinstall on a feature branch; the branch name is never interpolated.
- README, AGENTS.md and ADR-0019 give the real reasons (no lock churn in
the branch diff, deployed tree matches the committed lock), the cost
(the session runs the older main) and that the next session start
refreshes again.
- ADR-0019's check-clean and stale-server claims restated to match apm's
source.
ADR: 0019
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
ADR-0022 and gates.md now state the merge-base baseline, the fail-closed
cases, the PyYAML requirement and the multi-ref push gap (shared with
check-release-needed); the new hook gets its own gates.md group. Both
ADR additions follow each file's amendment format. README and AGENTS.md
now say to discard a refreshed apm.lock.yaml on a feature branch, and
that an .apm/ edit is live only once it is on the remote's main.
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
718c79a deleted every per-plugin .claude-plugin/plugin.json, which
reclassified each package from a marketplace plugin to a plain apm
package. That silently broke MCP propagation: apm_cli/deps/plugin_parser.py
maps a plugin-root .mcp.json into .apm/.mcp.json, and that code path runs
only for marketplace plugins. With no manifest, apm never reads the file.
Reproduced on ref-pinned consumer clones: at the parent commit a consumer
receives the obsidian server, at HEAD it receives none, and on upgrade apm
prints "Removed stale MCP server 'obsidian' from .mcp.json". This repo
consumes its own plugins through apm (ADR-0018), so the tracked root
.mcp.json would have been rewritten to an empty server map on the next
lock re-resolve -- silent tool loss plus unexplained working-tree drift.
The server is removed entirely rather than relocated to .apm/. It was
already a standing question (SIMPLIFICATION-AUDIT finding 37, deferred on
2026-09-13 pending confirmation, now confirmed), and plugins/bin/apm.yml
declares dependencies.mcp: [] -- apm's supported mechanism was never used.
All seven .mcp.json files go; the root one is apm-generated output and is
now gitignored alongside the other install artifacts.
ADR-0011's deferred ".mcp.json wiring gap" is moot twice over -- the
install route it blocked no longer exists and neither does the file --
and ADR-0018 records why it lost its only worked example of MCP
propagation. apm.lock.yaml still carries the server; it clears on the
first apm update after this reaches the default branch.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YR2CjVumUbEGWcMikcoXBD
apm becomes the only supported install path. The flat mirror at each plugin
root existed solely so Claude Code's native `claude plugin install` could
convention-scan plugin content (ADR-0017). With no native consumers, it cost
~20,000 tracked lines plus ~2,100 lines of sync tooling and ~88s of every
push to guard content apm never reads — and its only automated gate,
`claude plugin validate --strict`, passes on a plugin with zero content, so
it could not detect the defect ADR-0017 was created to fix.
Removes the mirror (213 files), the six per-plugin manifest pairs,
sync-plugin-content.sh, its 1,289-line test, the orphaned
marketplace-plugins.sh, and the check-plugin-content-sync and
validate-plugins pre-push hooks. The root `marketplace:` block and
.claude-plugin/ catalogue stay: apm's own marketplace consumers read that
same file, so `<name>@holocron` short names keep working.
tests/run-bats.sh now excludes .claude/skills/. apm installs from .apm/,
which carries the tests/ dirs the mirror stripped, so deployed .bats files
would otherwise be discovered and double-run.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YR2CjVumUbEGWcMikcoXBD
apm-marketplace-check checked network reachability of remote marketplace
refs, but finding 35 already removed the only remote package entry, so
every marketplace.packages[] source is local and the hook is pure
overlap with apm-pack-check-clean. apm-audit-ci was re-examined and kept
as-is -- its pre-commit-config.yaml comment already carries a dated,
verified justification the audit had missed.
check-marketplace-mirror-sync guarded .github/plugin/marketplace.json
against drift from .claude-plugin/marketplace.json. Verified against
current GitHub Copilot CLI docs: Copilot's marketplace discovery already
falls back through .github/plugin/marketplace.json to
.claude-plugin/marketplace.json, which this repo generates anyway -- the
dedicated mirror bought a discovery-order preference, not a capability.
Deleted the mirror file, its sync script, its test, and the hook.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YR2CjVumUbEGWcMikcoXBD
README and AGENTS.md each fully restated the "edit .apm/, never the
mirror" rule and the apm.lock/SessionStart mechanism instead of linking
to their canonical sources (architecture.md, ADR-0019).
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
Six pre-push hooks were validating overlapping sets of the same
manifests. check-manifests (marketplace.json/plugin.json path checks)
is redundant with validate-plugins (claude plugin validate) and
apm-pack-check-clean, which already cover the same ground.
Deletes the check-manifests hook entry, scripts/check-manifests.sh
(282 lines), and tests/test-check-manifests.sh (771 lines).
scripts/lib/marketplace-plugins.sh is kept — it is still sourced by
sync-plugin-content.sh. Updates the now-stale check-manifests.sh
mentions and hook counts in README.md and docs/spec/gates.md.
The apm-audit-ci and apm-marketplace-check hooks named in the same
finding are left untouched — the audit flags them as needing a
separate decision.
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: the relocation into gates.md and architecture.md moved text faster than it
verified it, and a review found eight assertions that do not survive contact
with the repo.
- gates.md cited docs/research/examples/, which does not exist; the tree is at
plugins/kyberforge/docs/research/examples/. The same wrong string survives at
ADR-0013:98, where it originated, and is tracked separately.
- gates.md's description-budget arithmetic was built on 23,427, a figure
ADR-0020 measured at f9b919d and pins there, and compared a name-inclusive
total against a description-only ceiling. Dropped rather than re-measured,
following the precedent this branch set for CONTEXT.md's token figure: a
second live copy is a second thing to go stale.
- gates.md listed "every boundary-clause routing target must resolve" as a hard
FAIL. skill-size-check.sh emits unresolved targets as SUGGESTION by default,
promotes to ERROR only when terminal and either route-notated or corroborated
by a resolving target in the same sentence, and reports INFO DID NOT RUN when
no universe resolves. Verified with five fixtures; replaced with a
three-verdict table.
- gates.md and README called `pre-commit run --hook-stage pre-push --all-files`
the whole push gate. check-release-needed.sh exits 0 unless
PRE_COMMIT_REMOTE_BRANCH is refs/heads/main, which only the real git hook
exports -- its own header comment titles this a "Known gap".
- "repo-defined" was loose: the two meta hooks are also declared here, and what
separates them is repo: meta from repo: local.
- architecture.md claimed `when:` appears in two of 39 skill sources. Exactly
one SKILL.md carries it; the second hit is a META.md. The likely origin is
skill-implementation-workflow.md:233, which claims META.md sits alongside
every skill when one exists in the whole tree.
- architecture.md said core/AGENTS.md is "deployed beside" CLAUDE.md.
deploy-manifest.sh sends them to ~/.claude/ and ~/.agents/ respectively; the
relation is import, not adjacency.
- gates.md's see-also cited ADRs as bare stems where its neighbours use paths.
Implementation notes: architecture.md's closing pointer sent readers to the
published description "for what a consumer actually gets", which was true
against an enumeration and is not after ADR-0021. It now names the boundary
relationship and points at the skills tree and README for an inventory. README's
bin bullet gains caveman and zoom-out, since ADR-0021 leaves it the only place
an inventory lives.
Impact: 16/16 pre-push hooks pass. Deliberately not fixed here and tracked
separately: the 12 pre-commit-stage hooks gates.md does not document, its own
SentenceOpenerThereIs violation, and README's missing scripts/install.sh step.
Refs: #105
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TmFqzpuExLJv3m114XVE9w
Why: bin's marketplace description read "A place for things to be binned" — a
routing boundary sitting in the enumeration slot, phrased as a joke. It is what
a consumer sees in the marketplace listing for a plugin shipping eleven
substantive skills: research, write-docs, tdd, prototype, triage, diagnose,
improve-codebase-architecture, grill-me, grill-with-docs, zoom-out and caveman.
This is the same category error b0ef503 fixed for git and gitea, and the same
text bb9158d moved out of core's description in August — "triage, diagnosis,
architecture review" left core precisely because those skills live in bin, so
bin is where it should have landed.
Implementation notes: bin 1.1.3 -> 1.1.4, marketplace 0.4.3 -> 0.4.4, PATCH for
metadata under the per_package strategy. README's bin line mirrors the new
description. architecture.md's routing boundary is deliberately left as
"unsorted skills that have not earned a home yet" — that is still true, and it
answers a different question than the listing does.
Impact: consumer-facing. apm-pack-check-clean, check-plugin-content-sync,
check-marketplace-mirror-sync, validate-marketplace, validate-plugins and
apm-audit-ci all pass.
Why: both plugins' published descriptions named capabilities that do not exist
and omitted ones that do. `gitea` advertised wikis — no wiki skill exists and
`grep -ri wiki plugins/gitea/.apm/` is empty — while omitting gitea-branches and
gitea-files. `git` advertised pull requests, which are gitea's, while omitting
pc-author/pc-run, git-submodules and git-workflow. These are compiled into
plugin.json and both marketplace.json files, so they are published metadata, not
internal notes.
This is the same defect bb9158d fixed on `core` in August, so the fix follows
that precedent exactly: correct the package apm.yml and the root marketplace
entry together — fixing only the package manifest leaves it half-propagated,
since the root entry is what reaches the compiled marketplace.
Implementation notes:
- git 1.3.3 -> 1.3.4, gitea 1.3.4 -> 1.3.5, marketplace 0.4.2 -> 0.4.3, all
PATCH for metadata under the per_package strategy at apm.yml:76.
- Regenerated with `apm pack` for the two marketplace manifests and
`scripts/sync-plugin-content.sh --all` for the four per-plugin plugin.json
files; `apm pack` alone does not touch those. `scripts/sync-marketplace-mirror.sh`
refreshed the Copilot legacy mirror, which no apm profile targets.
- README's gitea line mirrors the new published description. The routing
boundaries in architecture.md deliberately do not, since they answer a
different question.
Impact: consumer-facing. Anyone reading the marketplace now sees what these
plugins ship. apm-pack-check-clean, check-plugin-content-sync,
check-marketplace-mirror-sync, validate-marketplace and apm-audit-ci all pass.
Why: the review found architecture.md's plugin table and README's plugin list
were both written by enumerating what happened to be in each plugin, so both
went stale immediately — README credited `git` with pull requests it has no
skill for, and both omitted `pc-author`/`pc-run`. bb9158d fixed this same class
on `core` in August and it recurred here, so the fix is to stop enumerating in
the place whose job is routing.
Implementation notes:
- architecture.md's table now states routing boundaries only, with a note saying
so and pointing at each plugin's published apm.yml description for the actual
inventory. Two boundaries are named explicitly: `core` vs `kyberforge` (kept),
and `git` vs `gitea` — wire protocol against a local clone versus the forge's
HTTP API, which is why git-branches and gitea-branches are not duplicates.
- The `git` row is widened to cover git hook tooling rather than moving
pc-author/pc-run elsewhere. pre-commit manages .git/hooks/, so the placement
was always right and the row was simply under-described.
- README's setup block had a comment where step 2's command should be. It now
carries `pre-commit install -t pre-commit -t commit-msg -t pre-push`, verified
against the three stages in .pre-commit-config.yaml and the three hooks in
.git/hooks/. pc-run is still named, as the option rather than the only path.
- README:11 said each plugin carries agents, hooks and MCP servers. Only
kyberforge ships hooks and only bin ships an MCP server.
- gates.md:30 claimed its hook list was in config order; it is grouped by
concern, which is the better layout, so the claim is corrected to match.
Impact: docs-only. gitea's published description is corrected separately, since
that regenerates consumer-facing manifests.
Why: AGENTS.md is preloaded into every session via CLAUDE.md's @AGENTS.md
import, so every word is a tax paid whether or not it is relevant. It had
grown to 3,371 words (~4,550 tokens), 75% of it a single "Setup and testing"
section that was mostly neither: roughly 1,600 words were gate forensics —
why a hook is shaped the way it is, what was tried and rejected, verification
numbers — and only ~390 words were genuine one-time setup.
Implementation notes: split three ways by audience rather than by heading.
One-time setup, prerequisites and test commands move to a new root README.md,
which did not previously exist. The gate forensics move to a new
docs/spec/gates.md, preserving every constant, hook ID, error string and
tried-and-rejected note (policy.fetch_failure_default, the removed fallback
frontmatter reader, extending skill-size-check's files: pattern to agents,
tidying .claude/settings.json out of the pretty-format-json exclude list) —
these exist to stop settled decisions being re-litigated, so none were
dropped. AGENTS.md keeps only what applies every session, at one clause of
rationale per rule. The .apm/-mirror section collapses to a pointer because
docs/spec/architecture.md already covers it in full. The hot ADR-0020 gate
counts stay in AGENTS.md deliberately: they block any skill edit today.
Also corrects docs/spec/architecture.md's stale claim of 19 ADRs; ADR-0020
landed without updating the count.
Impact: per-session preload drops from ~4,550 to ~1,116 tokens, a 75% cut.
agentsmd-audit passes: secrets clean, drift clean, 4 informational findings
for sections deliberately relocated to the README.