A five-agent review of 718c79a and d2480b8 found no skill, agent or hook
regressions (39 skills before and after) and confirmed both hook removals
are genuinely moot -- verified against the tree, not taken on the commit's
word. It did find one functional regression (fixed separately) and this
documentation drift.
Counting errors, all from a git pathspec `*` crossing `/`:
- 17 .bats files shipped to consumers is really 10; 17 counted tracked
paths merely containing /tests/, one of them a template asset
- "roughly 88s off every push" is ~92.4s; 88 omitted validate-plugins
- "roughly 70% of each plugin remains live" holds only for kyberforge;
the real spread is 44.3% (bin) to 70.6%, now a table
- the pre-push enforcement row was half-corrected: 33 entries stood
unstruck (now 27) and 14 -> 11 switched counting basis mid-sentence
- the root .claude-plugin/plugin.json was described as "kept"; it has
never been tracked
gates.md said "Ten hooks" above a nine-row table (11 was decremented for
one removal, not two), and "both need the claude CLI" for one remaining
validator. Its pretty-format-json exclude rationale claimed six
alternations expanding to sixteen files in a passage headed "Mind which
number you are quoting" -- four alternations, two live files; the two
dead ones are dropped from the pattern. check-useless-excludes could not
catch this: it only flags an exclude matching nothing at all.
ADR-0024 cited ADR-0006 for a patch-bump rule it does not contain and
which ADR-0015 explicitly retired; stated apm's marketplace probe order
backwards (.claude-plugin/ is the last candidate, not the first, so the
earlier .github/plugin/ deletion only demoted resolution); undercounted
apm's skill-deploying targets as seven when there are fifteen; and never
recorded that validate-plugins was removed. The symlink hedge is resolved:
apm_cli/security/gate.py's ignore_non_content() drops symlinks silently on
deploy while apm_modules/ materialization dereferences them, so content
survives that far and vanishes at install. Accepted with no replacement
guard, per decision -- kyberforge/docs/hooks.md previously asserted a
guard that had been deleted with its script.
Four plugin READMEs still advertised `claude plugin install`; ADRs 0001,
0006, 0013, 0014, 0015 and 0019 described deleted machinery in the present
tense, 0019 most consequentially as the live justification for the
SessionStart hook's .apm/ path. CONTEXT.md's "apm package" entry forbade
"plugin" while using it in its own body, and "Output profile" lost the
antecedent for "one catalogue serves both".
run-tests.sh gains the .claude/skills/ exclusion run-bats.sh already had.
Latent today -- no test-*.sh lives under any .apm/skills/*/tests/ -- but
apm now deploys those directories, so one would be discovered twice.
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
Two related simplification-audit findings, bundled because they edit
some of the same skill-audit files and splitting would fragment
single-file diffs.
Finding 10: delete 48 per-skill/reference README.md files (they
restated SKILL.md in narrative form and no agent ever loads them) plus
2 scaffold templates. Drop the README criterion from skill-audit's
file-structure.md and finding-criteria.md, and the README-generation
step from skill-author's new-skill.sh; update new-skill.bats to match.
Plugin-root READMEs are kept intentionally, out of scope.
Finding 12: strip historical ADR-0020/ADR-0023 citations and
changelog-style narration from model-facing skill content across
kyberforge and git plugin skills. Delete skill-author's one-time
retrofit.md migration guide and its references. Some ADR-0023 tags
were not narration but check-rtk-prefix's required opt-out marker for
intentionally-bare git commands -- those were restored, not stripped.
Mirror re-synced and full pre-commit/pre-push suite verified green.
Refs: SIMPLIFICATION-AUDIT.md findings 10, 12
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YR2CjVumUbEGWcMikcoXBD
Retrofits all 39 skills to ADR-0020's description/body context contract, then fixes what six rounds of independent review found in that retrofit — including four ways the hot gate itself failed open.
Closes#99, #107, #108, #110, #111, #114, #115, #120.
## The retrofit (waves 1-5)
| | Start | Now |
|---|---|---|
| Description FAILs (>400 chars) | 26 | **0** |
| Body FAILs (>900 words, body-only) | 9 | **0** |
| Dangling routing targets | 2 | **0** |
| `Kyberforge.CompositionNote` | 10 | **0** |
| Preload tax | 21,005 chars | **~10,500** |
Under the 12,000-char success criterion. Per-wave detail is on #99.
## The review fixes
**The gate failed open four ways, three of them found after the retrofit shipped.** An unrecognised follower token made a dangling target vanish. A skill directory with no `SKILL.md` resolved as a valid target, so a commit could be green locally and red in a fresh clone — three existing fixtures were relying on that, one of which made the install-leak A/B pass vacuously. Then the free-standing `/name` sweep turned out to be gated on the sentence carrying a boundary marker, so route notation in any other sentence was invisible — not an ERROR, not a SUGGESTION, not an INFO — which left the documented "`/name` always blocks" promise false from a second direction. All four fixed and pinned.
**Two checks were silently not running.** `validate-provenance.sh` checks 7-8 were dead across nine skills. Waking them exposed a deeper problem: they assume `Research doc:` names a source index, but 30 of 121 entries point at topic content documents, so every new check-7 INFO was a false positive and check 8 was saved from a false-FAIL flood only by an *unannounced* skip. Checks 7/8 are now scoped to source indexes and every skip announces itself (#121).
**The retrofit's own anti-goal, four times.** ADR-0020 warns that a blunt gate gets satisfied by deleting content rather than relocating it. `diagnose` and `skill-audit` relocated prose and then read it unconditionally; `prototype` and `vale-config` deleted rules outright that survived nowhere. All four addressed.
## Verification
- `bash tests/run-tests.sh --strict` — 24 suites, 0 skipped, 0 failed
- `bash tests/run-bats.sh` — 325 tests, 0 failures
- `pre-commit run --all-files` — 17/17
- `pre-commit run --hook-stage pre-push --all-files` — 16/16, with `apm marketplace check` and `apm pack --check-clean` run against the remote, not skipped
- `scripts/skill-size-check.sh` over all 39 skills — rc 0, 0 ERROR/FAIL, SUGGESTION-only
- Preload tax measured at **10,498 chars**, max description 390 — both inside budget
- Every new test proven non-vacuous by a deliberate mutation of the behaviour it covers
**Per-commit sync, stated accurately:** the ten commits from the latest review round each pass `check-plugin-content-sync` in isolation, verified by checking each out in a detached worktree with a clean between. The earlier gitea window (`dfacf05..bedbd1d`, nine commits) does **not** — its mirror was regenerated in one batch at `bbc7300`. An earlier revision of this description claimed the property held for every commit; it does not, and a bisect through that window lands on a red commit. **Squash-merge** to collapse it, or accept that this range is not bisectable.
## Version bump
Six plugins and the catalog take a **patch**, not a minor. The branch is **89 commits — 40 `fix` / 30 `refactor` / 12 `docs` / 5 `chore` / 2 `test` — zero `feat`, zero `!`, zero `BREAKING CHANGE`** — and adds no skill, agent, command or hook. (Two earlier revisions of this section cited a stale histogram, most recently 78 commits; the figures above are measured at HEAD.) Both rules this repo ships (`forge/references/version-bump.md`, landing in this PR, and `git-commits/references/conventional-commits-spec.md`) make that a patch, and the catalog set is unchanged at 7 entries.
Not settled by that: four published files were removed from the installed tree, three moved, and `caveman` gained `disable-model-invocation`, retiring its old triggers. Under a strict reading those are major-class and currently ship under `refactor:` with no marker. Whether the deployed skill surface is a public contract is written down nowhere — worth deciding, but it outlives this PR.
## Deliberately not in scope
#112 (cherry-pick ownership, now resolved in favour of `git-commits`), #113 (`rtk git` normalisation), #116 (research fan-out), #101 (audit-skill merge), #122 (non-spec skill-root files), #123 (no PRD producer) stay open. #117 is the one worth reading: the contract's remedy is to move prose into `references/`, which is exactly where neither the size gate nor Vale looks — and the blind spot is wider than #117 currently records, since there is no root `.vale.ini` at all, so every ADR, `CONTEXT.md` and `README.md` is unlinted too.
That blind spot let this branch carry two `level: error` `Kyberforge.SentenceOpenerThereIs` violations into `references/` files it created — `provider-adapter-author/references/provider-matrix.md:31` and `agent-audit/references/finding-criteria.md:95`. Both are reworded in `afadaae`, confirmed by routing each file through the audit's own `vale-wrap.sh` (1 error each before, 0 after). Five further occurrences sit in `references/` files already on `main`; those are the pre-existing corpus and stay with #117, which is the real fix.
Also unfixed and not this PR's: `apm install` appends a duplicate `SessionStart` entry to `.claude/settings.json`, so a fresh clone cannot get pre-push green without an edit AGENTS.md warns against. Reproduces identically on `main`.
Co-authored-by: Defame1297 <gitea@rkdr.net>
Reviewed-on: https://git.dev.rkdr.net/Defame1297/holocron/pulls/129
Co-authored-by: Claude Code AI - Gitea MCP <claude@noreply.git.dev.rkdr.net>
Co-committed-by: Claude Code AI - Gitea MCP <claude@noreply.git.dev.rkdr.net>
Output of apm pack, sync-plugin-content.sh --all and
sync-marketplace-mirror.sh against this round's source changes. No file here
is hand-edited.
Carries the version bumps and marketplace owner.email into the compiled
manifests, the disallowedTools frontmatter and doc corrections into the flat
mirrors, and changes plugins/bin/.github/plugin/plugin.json's mcpServers from
the inlined server object to the ".mcp.json" pointer. That last file also
returns to 0644: the previous re-injection wrote it through mktemp and carried
0600 across, which no gate could see because the mode check did not cover
.github/plugin/ and git tracks only the exec bit.
.agents/plugins/marketplace.json is unchanged and that is correct -- apm's
codex profile carries neither version nor owner keys, so nothing in this round
reaches it.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01X7GvKuJfy2WrdBmUttV4DT
AGENTS.md told an offline agent to push with SKIP=apm-marketplace-check and
asserted that hook was "the only one whose failure mode is 'no network'".
Running all 12 pre-push hooks under a network namespace shows two fail, for
one shared cause: apm-pack-check-clean resolves the same remote entry. An
exact pin does not remove the ls-remote, so both hooks are named now.
AGENTS.md also said everything in a plugin root except .apm/ is generated.
Plugin roots carry hand-authored README.md, docs/, bin/, sources.md and
.mcp.json, so an agent would hunt for an .apm/ source that does not exist or
refuse the edit. The rule is positional: immunity belongs to the plugin root,
and anything inside a mirrored directory is still rm -rf'd.
ADR-0017 said apm strips a hooks field. The real loop is (agents, skills,
commands, instructions) -- hooks absent, instructions never mentioned -- and
it can never fire, because synthesize_plugin_json_from_apm_yml only emits the
eight identity fields. The decision stands; the mechanism was overstated. Its
mcpServers amendment is rewritten for the pointer payload and now records the
real reason: inlining bypassed apm's credential sanitizer.
ADR-0015's owner.email and version-pin passages are corrected against the apm
source, and ADR-0016 gains the disallowedTools amendment. agent-audit's
allowlist is data, so it gains disallowedTools too -- the ADR and the
validator that enforces it had come apart.
architecture.md described a root CLAUDE.md that imports two files (it imports
one, plus an RTK block) and pointed at an ADR index that does not exist.
Seven skill READMEs listed tests/ files the mirror strips, promising installed
users files their install lacks; those rows are marked source-only, with the
depth-4 template tests explicitly called out as surviving. And
plugins/kyberforge/hooks/README.md, deleted during the conversion and
preserved nowhere, is restored to a path the mirror does not own -- verified
by running a sync against a scratch copy.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01X7GvKuJfy2WrdBmUttV4DT
Documentation asserted things the code no longer did, which is how several of this
round's defects survived three review passes.
- `docs/spec/architecture.md` still described the pre-APM model — "each plugin has a
`plugin.json` manifest" — when no such file exists. AGENTS.md routes agents here
for the current structure, so it was live drift, not archive. Also cited ADR-0012
where ADR-0003 is meant.
- AGENTS.md never said `.apm/` is the sole hand-edited source, while the generated
mirror is byte-identical with no marker — an agent reading only AGENTS.md would
edit the mirror and find out at pre-push. It also omitted that `apm` and `jq` are
now required to push, unlike the `vale` bullet that exists for exactly that reason,
and understated the pre-push gate. `apm marketplace check` hard-fails offline and
`--offline` does not help, so the `SKIP=` escape hatch is documented rather than
new machinery built.
- ADR-0017 carried hardcoded line numbers that drifted twice within one PR, and said
`mcpServers` reinjection was real-syncs-only where the script's own header says
both modes. Line numbers dropped rather than corrected.
- ADR-0015 asserted issue #90 was closed. It is open. Reworded to defer to the
issue's own state rather than depend on someone remembering to close it.
- ADR-0014 said both audit skills pass `--config`; both SKILL.md files say pass none,
and passing one fails with E100. The ADR was wrong.
- ADR-0007 cross-referenced an archived ADR-0011 whose number was later reused.
Disambiguated with a note — renumbering a published ADR rewrites an immutable
record and breaks every existing citation.
- CONTEXT.md claimed `lint-runner` is report-only via a missing `Edit` tool.
Plugin-scope APM agents cannot express `tools:` (ADR-0016), so it is report-only by
instruction now; ADR-0016 accepted that regression but CONTEXT.md was not updated.
- Recorded two undocumented consequences of the conversion: `displayName` dropped
from all six compiled manifests and `owner.email` from the marketplace, and
`mattpocock-skills` silently version-pinned and manually maintained.
- Wrote the version-bump policy issue #90 specified but never delivered. Its previous
carrier, `marketplace-author`, was deleted in this PR, leaving the per-package rule
documented nowhere.
- README fixes: kyberforge listed skills belonging to the `git` plugin and a deleted
hooks path, `bin` advertised an agents directory it never had, and the docs index
listed files that do not exist.
Refs: #90
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01X7GvKuJfy2WrdBmUttV4DT
`core`'s description and keywords described `bin`'s skills — triage, diagnosis,
architecture review — while `core` ships the three agentsmd/provider-adapter skills.
Pre-existing text, but this PR compiles it into `plugin.json` and both
`marketplace.json` files, so it became published metadata. The root `apm.yml` entry
carried the same stale text and is the one that actually reaches the marketplace
manifests, so fixing only the package manifest left it half-propagated.
Version bumps follow the per_package policy this PR documents, each landing in both
the package `apm.yml` and the root entry: kyberforge 1.4.0 -> 1.4.1 for the
reference-doc changes, core 1.1.0 -> 1.1.1 for the metadata, catalog 0.3.2 -> 0.3.3
as a marketplace-block-only edit.
The compiled diff is larger than those four changes because the manifests committed
at HEAD were not apm output: `json.dumps(indent=2, sort_keys=True)` reproduces all
three byte-for-byte, i.e. `pretty-format-json --autofix` had rewritten them before
the exclusion existed. Regenerating realigns them to genuine `apm pack` output;
the remainder of the diff is key ordering and UTF-8 escaping. `core`'s keyword
change is a fifth semantic change, invisible at marketplace level because
`marketplace.json` carries no keywords field.
Refs: #90
ADR: 0015
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01X7GvKuJfy2WrdBmUttV4DT
--check's throwaway pack copy seeded .claude-plugin/plugin.json and
.github/plugin/plugin.json from the real plugin dir, then packed without
--force -- apm pack silently skips regenerating a plugin.json that already
exists, so the diff always compared the copy against itself and never caught
drift in the compiled name/version/description/mcpServers. --force is now
always passed; in check mode it forces regeneration inside the throwaway copy
only, which sync_plugin_manifest() then diffs against the real committed
manifest.
sync_hooks_json() returned early whenever .apm/hooks/ was missing, without
checking whether a stale hooks.json was still sitting at the plugin root from
a prior sync -- unlike sync_dir(), which already detects that kind of orphaned
mirrored output. It now mirrors sync_dir()'s shape: flagged as drift in
--check, removed on a real sync.
Running the corrected --check --all against this repo's own plugins surfaced
3 real orphans: plugins/{git,gitea,core}/hooks.json, empty stubs added in
4edaaac only to satisfy an old plugin.json pointer-field check that no longer
exists (their compiled plugin.json has never had a hooks field, and none of
the three ever had .apm/hooks/). Removed as part of this fix since they're
exactly the drift the corrected check now catches -- leaving them would break
the sync-plugin-content pre-push gate on this branch.
Also extracts two shared helpers into scripts/lib/, sourced by this script and
others so a future bug fix doesn't need hand-applying three times:
- marketplace-plugins.sh: walks marketplace.json for local plugin dirs (this
script's --all branch and check-manifests.sh had near-identical copies)
- batch-run.sh: the bounded-batch concurrent job runner (this script,
tests/run-tests.sh, and tests/run-bats.sh each hand-rolled the same
core-count-capped wait loop independently)
Extended tests/test-sync-plugin-content.sh with coverage for both drift cases
(plugin.json version-bump drift, orphaned-hooks.json drift), including that a
re-sync clears each.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01X7GvKuJfy2WrdBmUttV4DT
validate-secrets.sh checked the placeholder allowlist regex against the
whole line before running any secret-pattern regex. An unrelated
placeholder-looking token anywhere on the line (e.g. "example" or
"your-token-here" in a trailing comment) suppressed detection of a real
credential earlier on the same line. Scope the allowlist check to the
matched secret-candidate substring only, which the per-match re-check
already did downstream but the whole-line pre-check short-circuited
before it ever ran.
Extend validate-secrets.bats with a case proving a real AWS-style key is
still caught when a placeholder token sits elsewhere on the line.
Regenerate the flat-mirror copy at
plugins/core/skills/agentsmd-audit/scripts/validate-secrets.sh via
scripts/sync-plugin-content.sh --all per ADR-0016.
bin and kyberforge had empty keywords: [], and no plugin set homepage or
repository despite the schema supporting both and the repo having one
canonical Gitea remote -- flagged by an apm-docs-vs-repo audit following
issue #90's plugin->apm conversion.
Compiled .claude-plugin/plugin.json and .github/plugin/plugin.json were
regenerated via scripts/sync-plugin-content.sh to carry the new fields.
Refs: #90
apm pack silently skips regenerating plugin.json when it already exists
("already exists; skipping plugin.json generation. Re-run with --force to
overwrite it."). sync-plugin-content.sh never passed --force, so
.claude-plugin/plugin.json and .github/plugin/plugin.json were written
once on first sync and never refreshed again -- a name/version/description
edit in a plugin's apm.yml would silently stop propagating, with no gate
catching it (check-manifests.sh only checks presence; check-plugin-content-
sync explicitly excludes plugin.json; apm-pack-check-clean is scoped to
marketplace.json only).
Pass --force on real-mode syncs only (--check must not mutate the plugin
root, so plugin.json staleness there stays a known, undetected gap until
the next real sync -- documented in the script's header comment).
Regenerating surfaced a second, unrelated bug: apm's writer and
pretty-format-json's --autofix disagree on JSON key order (semantic vs.
alphabetical), so every real sync would otherwise flip plugin.json's key
order right back after a commit re-alphabetized it. Excluded the four
apm-generated manifests (plugin.json x2, marketplace.json x2) from
pretty-format-json -- their exact formatting is dictated by apm's own
writer as compiled output, not this repo's generic JSON prettifier.
Verified idempotent: a second real sync after this fix produces zero
further diff.
Refs: #90
Uncomment marketplace.outputs.codex in apm.yml. Codex output requires
every package to declare a category -- added one per package (Developer
Tools / Utilities / Version Control / Productivity, matching each
plugin's actual scope).
Re-running apm pack after enabling codex auto-synced 'codex' into each
local package's own targets: list (apm.yml, per plugin) -- packages
belonging to a marketplace that publishes a codex output need to declare
codex as a supported compile target for that to be coherent. Verified
idempotent: a second apm pack --check-clean run makes no further changes.
.claude-plugin/marketplace.json regenerated (now carries category per
plugin); .agents/plugins/marketplace.json is the new codex artifact.
Refs: #90
Claude Code's (and Copilot's) native plugin installer has zero awareness of
.apm/ nesting -- it convention-scans only flat skills/, agents/, commands/,
hooks.json at each plugin's root. Confirmed via strings on the installed
claude binary and live installs of git@holocron/gitea@holocron/kyberforge@
holocron, all reporting Skills(0) Agents(0) Hooks(0) post ADR-0015's apm
conversion. Root cause (apm_cli/core/plugin_manifest.py): apm's plugin.json
compiler deliberately strips skills/agents/commands keys, assuming the host
already auto-discovers those convention directories -- it has no model of
.apm/ being host-visible at all. Separately, apm's own bundle exporter
(apm_cli/bundle/plugin_exporter.py, behind `apm pack --format plugin`)
implements the correct .apm/ -> flat mapping, but only ever targeted
build/<name>-<version>/, a path nothing in marketplace.json's source: points
at.
scripts/sync-plugin-content.sh wraps that bundle exporter and copies its
agents/, skills/, commands/, instructions/, extensions/, and merged
hooks.json back into each plugin's own root as a second tracked
compiled-output category -- same governance status as
.claude-plugin/plugin.json: generated from .apm/, never hand-edited. tests/
subdirectories are excluded from the mirror (dev fixtures, not host-visible
runtime content; several hardcode a relative repo-root walk-up sized for the
.apm/-nested depth, which breaks when duplicated one level shallower).
Applied for real across all 6 plugins and verified two ways: `claude plugin
validate --strict` passes on every real plugin directory, and a live
`claude --plugin-dir <path> -p "list skills/agents"` behavioral test
confirms content is now actually discovered.
Also, from the same issue #90 review round:
- scripts/check-manifests.sh pointed at each plugin's root-level plugin.json
(checking skills/hooks/mcpServers/agents pointer fields) -- that file was a
stale near-duplicate of .claude-plugin/plugin.json nothing else read or
wrote, now deleted across all 6 plugins. check-manifests.sh is rewritten to
validate .claude-plugin/plugin.json instead, and drops the pointer-field
checks entirely (nothing to check -- those fields are correctly absent by
design). Content-presence drift is now check-plugin-content-sync's job, a
new pre-push hook wired in .pre-commit-config.yaml.
docs/adr/0017 records the root cause and decision in full, including two
rejected alternatives (patching plugin.json's path fields directly -- apm's
compiler strips them on every run; pointing marketplace.json at apm pack's
build/ output -- a version-suffixed non-source directory nothing can install
from without an extra build step). ADR-0015 and CONTEXT.md are updated to
point at it.
Refs: #90
Why:
ADR-0015 established that Microsoft APM (apm.yml + .apm/) should replace
this repo's hand-authored plugin.json/marketplace.json model, with those
files becoming compiled output of `apm pack` instead of files edited by
hand via the (now-retired) plugin-author/marketplace-author skills.
Issue #90 was the deferred execution of that decision, gated on #88
(apm tooling) and #89 (apm-native agent-author/skill-author routing).
Implementation notes:
- All six plugins (bin, core, git, gitea, kyberforge, lint) now carry
apm.yml + .apm/{skills,agents,hooks} as their authoring source. Skills
moved with a plain git mv (content-identical across targets). Agents
were re-authored, not moved: per ADR-0016, .apm/agents/*.agent.md
compiles verbatim to both Claude and Copilot, so plugin-scope agents
now carry only name/description/model/source_keys -- no tools: field,
no Claude-only knobs (isolation, maxTurns, effort, memory,
permissionMode).
- Root apm.yml registers all 7 marketplace packages (6 local plus
mattpocock-skills as a remote entry) under versioning: per_package,
matching this repo's existing independent-plugin-versioning practice.
- .claude-plugin/marketplace.json and every plugin's plugin.json are now
apm-pack-compiled output, verified against the prior hand-maintained
content: same names/descriptions/versions/licenses/authors, only
cosmetic serialization differences (JSON key order, owner email vs.
url, Unicode escaping).
- plugin-author and marketplace-author are retired now that apm-based
authoring fully replaces their job; kyberforge bumped 1.3.1 -> 1.4.0
for that removal, and the root marketplace catalog bumped
0.3.1 -> 0.3.2 to match, per the version-bump convention now
documented in apm-workflow's reference docs instead of a dedicated
script (apm has no native version-bump automation).
- Fixed hardcoded pre-.apm/ path assumptions across
.pre-commit-config.yaml, .pre-commit-hooks.yaml,
scripts/check-scope-walkup-sync.sh, scripts/sync-vale-styles.sh,
scripts/check-vale-style-sync.sh, six plugins' root plugin.json
(stale skills/hooks/agents pointer fields that check-manifests.sh
validates), and several tests/*.bats and tests/*.sh fixtures --
including a bats REPO_ROOT relative-path depth bug (10 files, one
extra .apm/ directory level to walk up) and a vale probe-path
isolation regression introduced mid-fix.
- Corrected empirically-wrong assumptions surfaced this session in
apm-workflow/apm-install's own reference docs: `apm marketplace
package add` does not accept local paths (only owner/repo remote
shorthand -- local packages are registered by editing apm.yml's
marketplace.packages[] directly); `apm compile` is a consumer-side
AGENTS.md/CLAUDE.md generator, not the plugin.json producer, and
hard-fails on skill/agent-only packages without --clean; `apm plugin
init <name>` nests a stray subdirectory when run with a positional
name arg from inside a same-named directory; no native Copilot
marketplace output profile exists; .mcp.json is merged into the
compiled plugin.json content-aware and target-scoped, with no
dependencies.mcp entry needed for simple passthrough; pipx is the
correct pip fallback on externally-managed Python environments.
- Renamed agent-author's copilot.agent.md template asset to
copilot.agent.md.template so apm compile's recursive *.agent.md glob
stops misparsing the placeholder template as a real agent primitive.
Impact:
plugin.json and marketplace.json are compiled artifacts from here on --
editing them by hand is no longer the workflow; edit apm.yml/.apm/ and
run apm pack. CONTEXT.md's Plugin/Plugin marketplace glossary entries
reflect this. ADR-0001 is marked superseded, ADR-0006 moot, and
ADR-0010 updated for the new .apm/agents/ path (project/user scope
unaffected, per ADR-0016). Full local verification: claude plugin
validate --strict on all 6 plugins, apm audit --ci, apm marketplace
check, check-manifests.sh, and the full test suite (165/165 bats,
13/13 shell scripts) all pass clean.
Fixes: #90
Refs: #88, #89
ADR: 0015
ADR: 0016
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Ub96PyaSRD9BHPktotj1pC
PR review feedback: Step 3 gave no concrete guidance on what good
AGENTS.md content looks like, and the skill had no substantive
references file (only provenance bookkeeping in sources.md), unlike
sibling kyberforge skills. Adds section-by-section content guidance,
the worked example, and monorepo precedence rules synthesized from
the agentsmd research corpus.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
core gained three skills for the first time (agentsmd-author,
agentsmd-audit, provider-adapter-author). Minor bump reflects new
capability rather than a fix. Also declares the missing `skills`
path in the Copilot manifest so Copilot CLI discovers them
(CC auto-discovers from the plugin root; Copilot requires explicit
declaration per ADR-0016 convention).
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Creates/updates a target repo's AGENTS.md by exploring real repo
conventions, supports nested monorepo placement, closes out via
agentsmd-audit, and composes into provider-adapter-author for
provider-file reconciliation. Completes the three-skill trio from
ADR-0012.
Converts a target repo's provider-specific instruction file (CLAUDE.md,
.cursor/rules, copilot-instructions.md, etc.) into a thin adapter over
AGENTS.md, mirroring this repo's own two-tier CLAUDE.md pattern
(ADR-0002/0003). Self-validates via a bundled deterministic script
(scripts/validate-adapter.sh) rather than a separate paired audit skill.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Independent clean-context audit recheck flagged that README.md's file
table omitted scripts/README.md and tests/README.md despite both
existing on disk, inconsistent with sibling kyberforge skills.
The previous commit only landed the research-folder rename — a multi-path
git add silently failed and left CONTEXT.md, ADR-0012, and the actual skill
files unstaged. This lands them: the agentsmd-audit skill itself (three
deterministic validators for secrets, structure, and drift against a target
repo's AGENTS.md), its bats test suite, provenance record, and the
CONTEXT.md/ADR entries documenting why this lives in core rather than
kyberforge.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Audits a target repo's AGENTS.md file(s) for embedded secrets, structural
completeness against the agents.md common-sections checklist, and drift
(referenced commands/paths that no longer resolve). First active skill in
the core plugin — kyberforge is scoped to marketplace-factory meta-tooling,
not generic target-repo documentation (see ADR-0012). Moves the agentsmd
research corpus from plugins/kyberforge to plugins/core to match.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
scripts/install.sh hardcoded $REPO_ROOT/.git/hooks, which breaks under any
git worktree checkout (.git is a file there, not a directory) — this is
what blocks every worktree-based agent from pushing cleanly. Resolve the
hooks directory via `git rev-parse --git-path hooks` instead, normalizing
to an absolute path since git returns it relative to the queried repo root
for plain checkouts but absolute for worktrees.
Also drops `agents`/`skills` fields from plugins/bin, plugins/core, and
plugins/gitea plugin.json where the referenced directories don't exist on
main yet (bin never had an agents/ dir; core and gitea's real skill/agent
content is still pending merge from an in-flight branch) — these were
failing scripts/check-manifests.sh and blocking pushes for unrelated work.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FNJWdVvdgvZCHi1hZGqgVQ
check-manifests.sh requires all paths declared in plugin.json to exist.
Adds empty hook and MCP server stubs matching kyberforge convention.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>