`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
Four repo gates reported success in states they exist to reject.
`check-vale-style-sync.sh` passed while a Kyberforge lint rule was silenced. The
check matched a blocklist of severity values, but Vale's semantic is an allowlist:
anything that is not exactly YES/error/warning/suggestion disables the rule. So
`= false`, `= 0`, `= garbage`, an empty value and — worst — a lowercase `= yes` all
killed enforcement while reading as "enabled" to a human. Inverted to an allowlist.
Two sibling holes: dropping `KyberforgeCopilot` from `BasedOnStyles` unloaded the
Copilot-only check silently, and narrowing a section glob to a location made Vale
lint zero files, which is the "0 files, hook Passed" failure the script's own
comment says it exists to catch.
`sync-marketplace-mirror.sh --check` failed open when its source was missing, while
its sibling correctly errored in the same state.
`check-scope-walkup-sync.sh` wrote to hardcoded `/tmp/fN.out` paths and read one
back, making it non-reentrant — a concurrent instance can flip a verdict, and this
branch made the test runner concurrent. Now per-run `mktemp -d`.
`check-manifests.sh` had no disk-to-marketplace pass, so a plugin directory absent
from `marketplace.json` passed every gate while the `validate-plugins` hook globbed
it. The "listed" match is restricted to remote-source entry names; matching any
entry name let a genuine orphan through on a name coincidence.
`run-bats.sh` reported an empty TAP stream as `0 tests, 0 failures`, exit 0 — a
total harness failure reading as a pass.
The test-side changes are the larger half, because the guards were the real problem.
`test-sync-marketplace-mirror.sh` could overwrite the live tracked mirror under an
inherited GIT_DIR, which is precisely the git-hook context it runs in. The bash-3.2
scan hand-maintained its file list, omitting the new shared runner, and had no rule
for `wait -n` or `nproc` — the two hazards the previous review round found live. It
now derives 43 files across three globs with per-glob floors. Several assertions
were decoration: the concurrency checks caught the reentrancy defect 0 times in 10,
the leak fix was green either way, and two manifest fixtures passed with the code
they claimed to cover deleted. Every assertion now has a revert it provably fails
against.
Refs: #90
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01X7GvKuJfy2WrdBmUttV4DT
The mirror's `tests/` exclusion was depth-agnostic, so it deleted
`skill-author/assets/templates/tests/` — a template the skill scaffolds FROM —
alongside the depth-2 dev fixtures it was meant to drop. Since ADR-0017 makes the
mirror the installed content, the shipped scaffolder was broken: the mirror copy of
`new-skill.sh` exited 2 on `sed: can't read .../tests/README.md`, leaving a
half-written skill, while the byte-identical `.apm/` copy exited 0. `--check` was
green about it.
Check mode was restructured rather than patched because `diff -x` matches a basename
at any depth and cannot express the depth-2 scoping the fix needs — the two modes
could not be made to agree by construction. Check mode now runs the real `sync_dir`
into a throwaway root and diffs with no exclusions, leaving the exclusion rule and
the hooks destination each in exactly one place.
Also fixed here, all previously invisible to `--check`:
- Merged hooks were written to `<plugin>/hooks.json`, which Claude Code does not
convention-scan, while ADR-0017 itself quoted `hooks/hooks.json` as the contract.
Moved, with the legacy path cleaned up as stale. No `hooks` pointer is added to
`plugin.json`, so this does not reopen the option ADR-0017 rejected.
- Only the first drift per plugin was reported: `diff | sed` returns 1 under
`pipefail`, and `set -e` killed the subshell before the remaining checks and
before `FAIL=1`.
- File-mode and symlink drift were invisible, so `--check` and a real sync
disagreed; a find-based type/mode manifest now covers both.
The tests pinned almost none of this — the stale-skill wipe, the check-mode stale
branch, three `MIRROR_DIRS` entries and the hooks newline normalization could each
be deleted with the suite still green. All are now mutation-tested.
Refs: #90
ADR: 0017
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01X7GvKuJfy2WrdBmUttV4DT
`check-plugin-content-sync` was the only local pre-push hook scoped with `files:`
instead of `always_run`, and its regex missed both compiled `plugin.json` paths —
exactly the manifests the script had been taught to diff. A commit tampering only
with one of those passed the entire pre-push suite at exit 0.
A regex fix alone could not close it: pre-commit drops deleted paths from the file
list, so removing a mirror directory skipped the hook too. The `hooks\.json`
alternative was also already dead, matching only the pre-move flat path. Dropping
`files:` for `always_run: true` closes all three and makes the hook uniform with its
three siblings; the check costs ~2-4s.
Also excludes `.github/plugin/marketplace.json` from `pretty-format-json`. This was
not hypothetical: `json.dumps(indent=2, sort_keys=True)` reproduces the manifests
committed at HEAD byte-for-byte, so `--autofix` had already rewritten compiled apm
output once. Without the exclusion it would do so again on the next commit,
silently breaking the byte-identical mirror invariant.
Landing first, ahead of the regenerated manifests, so that cannot happen.
Refs: #90
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01X7GvKuJfy2WrdBmUttV4DT
Review findings #5 and #7 on PR #95 flagged two bash-3.2-incompatible
patterns despite the surrounding scripts claiming 3.2 safety:
- tests/run-bats.sh used `mapfile` (bash 4.0+), which fails immediately
under macOS's stock bash 3.2 before any batching logic runs. Replaced
with the `while read` loop already established in tests/run-tests.sh,
and guarded the two downstream `${TEST_FILES[@]}` expansions with
`${arr[@]+"${arr[@]}"}` to match that file's convention.
- `trap 'rm -rf "${CLEANUP_DIRS[@]}"' EXIT` was unguarded in
tests/test-sync-marketplace-mirror.sh and
tests/test-sync-plugin-content.sh: under `set -u`, if `mktemp -d`
fails before the array is populated, the trap itself throws an
unbound-variable error that masks the real test failure. A repo-wide
grep for the same pattern turned up a third, unreviewed instance in
tests/test-check-release-needed.sh. Fixed all three with the guarded
idiom already used elsewhere in the repo.
Extended the existing bash-3.2-hazard static check (test 16 in
tests/test-vale-wrap.sh) to scan all four fixed files going forward,
so a regression of either pattern fails the suite instead of only
surfacing on a real bash 3.2 host.
Refs: PR #95
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01X7GvKuJfy2WrdBmUttV4DT
Skills/hooks/mcpServers/agents pointer-field validation in plugin.json was
fully delegated to sync-plugin-content.sh --check, but that script explicitly
skips any plugin directory lacking .apm/ (it has nothing to compile there).
A plugin with no .apm/ and a hand-authored plugin.json whose pointer field
points at a missing path was therefore left uncovered by either check --
currently latent since every plugin in this repo has .apm/, but a real gap
for the first non-apm plugin added.
Restores a fallback validation path here for exactly that case (no .apm/
directory), reusing the pre-delegation logic this script used to run
unconditionally. apm-native plugins keep relying on the delegated check so
the two never duplicate (or disagree) on the same manifest.
Also switches the marketplace.json walk to the shared
scripts/lib/marketplace-plugins.sh helper introduced alongside
sync-plugin-content.sh's matching --all branch, replacing the
near-identical hand-duplicated loop this script's own header comment
already flagged as a duplication risk.
Adds fixtures: a non-apm plugin with a broken skills pointer (caught), a
non-apm plugin with a valid pointer (no false positive), and an apm-native
plugin with a broken pointer (left to the delegated check, not
double-validated here).
Co-Authored-By: Claude Sonnet 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
hook_file_regexes() unioned the `files:` regex from .pre-commit-hooks.yaml
and .pre-commit-config.yaml before checking whether a probe path is in
scope of a kyberforge vale-audit-prefilter hook. That union let a probe
matching only the old, looser .pre-commit-hooks.yaml pattern pass even
after .pre-commit-config.yaml's copy of the same hook had been narrowed
(e.g. to require a `.agent.md` suffix) -- silently masking exactly the
kind of hook-rescoping drift this check exists to catch. Per ADR-0014
the two manifests are meant to exercise the same resolution path an
external consumer's hook would, so this divergence is real drift, not
noise.
hook_file_regexes() now takes the manifest path explicitly and caches
per (skill, manifest) pair instead of per skill, so each manifest's
regex set can be inspected on its own. The probe-validation loop
computes in_hooks/in_config independently via a new matches_any_regex()
helper. Probes carry a new third heredoc field, `shared` or
`hooks-only`: `shared` probes (a file shape genuinely covered by both
manifests, e.g. plugins/demo/.apm/agents/demo.agent.md) must agree
between the two or the check now fails with a drift error;
`hooks-only` probes (a Copilot .agent.md living outside this repo's
own plugins/.apm/ layout, and the legacy bare-`.md`-under-agents/ shape
kept only to exercise a distinct .vale.ini glob section in isolation)
are exempt, since .pre-commit-hooks.yaml is deliberately broader there
by design. The original "matches no regex in either manifest"
staleness check is unchanged.
Added case 11b to tests/test-check-vale-style-sync.sh: narrows a
fixture's local config regex further while leaving
.pre-commit-hooks.yaml untouched, and asserts the check now flags it.
Confirmed red against the pre-fix script before applying the fix.
Refs: #95
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.
The pre-push hook ran a full `apm pack` + directory diff across every
plugin on every push (always_run: true), even for changes that could
never affect the sync (e.g. a README edit). Add a files: regex scoped
to what scripts/sync-plugin-content.sh actually consumes -- each
plugin's apm.yml, .mcp.json (read by reinject_mcp_servers), .apm/**
(the sync source), the flat mirror output dirs themselves (agents/,
skills/, commands/, instructions/, extensions/, hooks.json -- so a
direct edit to compiled output is still caught as drift), the
marketplace.json --all reads to build the plugin list, and the sync
script itself -- and drop always_run now that files: covers every
input path.
plugin-author was deleted by this PR (ADR-0015, apm-workflow is the
successor) but still appeared as a live example in the skill-composition
list. The Gitea-canonical-issue-tracker line also cited ADR-0017 (the
unrelated plugin-content-mirror ADR added by this PR) instead of ADR-0007.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01X7GvKuJfy2WrdBmUttV4DT
force_flag and plugin_dirs expanded unguarded under set -euo pipefail,
tripping "unbound variable" on bash 3.2 (macOS) whenever the array is
empty -- which is the default --check invocation wired into the
pre-commit drift gate. Apply the same ${arr[@]+"${arr[@]}"} guard
already used for seen_names in this file.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01X7GvKuJfy2WrdBmUttV4DT
Running ADR-0017's cited live behavioral test literally from this
repo's root gives a contaminated signal: this repo's own project-level
.claude/settings.json enables all 6 holocron plugins, so Claude Code
loads all of them rather than isolating kyberforge's discoverability.
Documents the neutral-cwd + absolute --plugin-dir reproduction needed
to isolate the signal.
Its sibling sync-plugin-content.sh has thorough coverage
(tests/test-sync-plugin-content.sh) but this drift guard -- wired into
pre-commit as check-marketplace-mirror-sync, keeping
.claude-plugin/marketplace.json and .github/plugin/marketplace.json in
sync -- had none. A silent regression here would let the two
marketplace manifests drift without any test catching it.
Covers: missing-source no-op in both real-sync and --check modes,
drift detection when the mirror is missing or stale, real sync
producing a byte-identical mirror, --check clean immediately after
sync, drift from an edited source cleared by re-sync, an
already-in-sync mirror reporting no drift, usage-error rejection of
unrecognized/extra arguments, and idempotency of repeated syncs.
The Vale-audit-prefilter glossary entry still quoted the pre-.apm/-
migration file-scoping regexes (^plugins/[^/]+/skills/...,
^plugins/[^/]+/agents/...). The actual pre-commit hooks require .apm/
in the path and .agent.md for agent files; updated the glossary to
quote the live patterns from .pre-commit-config.yaml verbatim,
including a third stale occurrence in the skill-size-check description
that the PR #95 review missed.
Refs: #95
run-bats.sh's new bounded parallel dispatcher used nproc/wait -n,
which are bash 4.3+/GNU-only and silently drop the concurrency cap on
macOS's stock bash 3.2 (the wait -n error is swallowed by `|| true`).
Its sibling tests/run-tests.sh, changed in the same PR and explicitly
bash-3.2-safe, already solves this with getconf + a batched wait.
Ported that same pattern here for consistency and to actually meet the
compatibility goal.
Refs: #95
PR #95's review of the issue #90 apm-conversion work found several
defects in scripts/sync-plugin-content.sh and the gate wired to it:
- --check claimed never to mutate the plugin root, but apm pack still
wrote .claude-plugin/plugin.json and .github/plugin/plugin.json into
the real plugin_dir on first-time creation. --check now packs a
throwaway copy instead.
- check-plugin-content-sync hardcoded the six plugin directories
instead of deriving them the way check-manifests.sh already does.
Added an --all flag that parses .claude-plugin/marketplace.json, and
simplified the pre-commit hook to use it.
- A missing plugin_dir and one that legitimately has no .apm/ yet both
reported SKIP/success; a missing directory now FAILs.
- The dispatch loop backgrounded every plugin with no concurrency cap,
unlike the JOBS-bounded pattern this same PR added to
tests/run-bats.sh and tests/run-tests.sh. Added the same
bash-3.2-safe getconf + batched-wait cap here for consistency.
- Per-plugin scratch/log/status files were keyed only by basename, with
no collision guard across arguments; added a fail-fast check.
- sync_hooks_json()'s trailing-newline normalization was duplicated
between its --check and write branches; factored into one helper.
- tests/test-sync-plugin-content.sh set two competing `trap ... EXIT`
statements, so the first (cleaning up $FIXTURE) was silently
replaced by the second and its tmp dir leaked every run. Adopted the
track()/CLEANUP_DIRS pattern already used in
tests/test-check-release-needed.sh.
Separately: apm's Copilot-ecosystem plugin.json builder unconditionally
strips mcpServers, citing (in its own docstring) that the field is out
of schema for Copilot -- a claim this repo's own researched Copilot
plugin schema docs contradict. reinject_mcp_servers() narrowly restores
it from the plugin's .mcp.json on real syncs only, regenerating
plugins/bin/.github/plugin/plugin.json (the only plugin that currently
declares any MCP servers). Documented as an amendment to ADR-0017,
since it's a deliberate, narrow exception to that ADR's rejection of
patching apm's compiled output -- apm's premise for stripping
skills/agents/commands/hooks pointers is still accurate; its premise
for stripping mcpServers is not.
All 12 assertions in tests/test-sync-plugin-content.sh pass
individually, plus 5 new regression tests added for this round; the
full bats and shell-script suites are green; shellcheck is clean.
Refs: #95
ADR: 0017
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.yml only declares claude and codex marketplace output profiles --
codex writes a differently-shaped file to .agents/plugins/marketplace.json,
so nothing regenerates the legacy Copilot CLI path at
.github/plugin/marketplace.json. It was hand-synced once during the #90
conversion and had drifted (missing the category field added when codex
output was enabled) with no gate to catch it.
scripts/sync-marketplace-mirror.sh keeps it byte-identical to the compiled
.claude-plugin/marketplace.json, wired as a pre-push check so it can't go
stale silently again.
Refs: #90
Both runners spawned one process at a time and let it finish before starting
the next, so wall time scaled with file count even though each file's own
work (bats: mostly repeated apm/git subprocess startup; the vale-heavy
shell scripts: repeated vale binary startup) rarely used a full core.
run-bats.sh now backgrounds one `bats` invocation per file, bounded by core
count, buffering each file's TAP output so concurrent streams can't
interleave, then flushes in stable sorted order once every job finishes.
run-tests.sh does the same for test-*.sh in fixed-size batches (plain `wait`,
not `wait -n`, to stay on the bash-3.2-safe path test-vale-wrap.sh already
enforces for this file) -- verified beforehand that every test-*.sh keeps its
fixtures under its own mktemp dir rather than mutating the live repo tree, so
running them concurrently is safe.
Confirmed correct on both the pass and fail paths (a deliberately failing
bats test and a scratch TEST_DIR with pass/fail/skip-77 scripts) before
measuring: full bats suite 26-30s -> 7.5s, tests/run-tests.sh 68s -> ~45-50s,
full pre-push gate 2m40s -> 1m12s.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01X7GvKuJfy2WrdBmUttV4DT
skill-frontmatter's files: pattern matched any SKILL.md, so it ran twice per
sync -- once on the .apm/ source and again on the compiled flat-mirror copy.
Scoped it to .apm/skills/ like its sibling hooks (skill-size-check,
vale-audit-prefilter-skill), which already only check the source.
sync-plugin-content.sh ran `apm pack` once per plugin, serially -- each
invocation is dominated by fixed CLI startup cost rather than per-plugin
work, so 6 plugins paid that cost 6 times over (~3.1s). Backgrounds the
per-plugin work instead, buffering each plugin's output so concurrent
DRIFT/FAIL messages can't interleave, then flushes in stable order after
`wait` (~1.3s, confirmed idempotent on a real sync).
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01X7GvKuJfy2WrdBmUttV4DT
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
Validated the plugin-content-mirror fix (issue #90) against apm's own
packing/CI documentation and source: no apm-native mechanism replaces the
mirror script (apm's bundler treats .apm/ and root convention dirs as
mutually exclusive, by design), but the investigation surfaced a real,
separate gap -- this repo ran zero apm-native audit/check commands in CI,
relying entirely on custom scripts and Claude Code's own client-side
validator.
Add three pre-push hooks matching apm's documented producer CI pattern:
- apm marketplace check: validates every marketplace.packages[] entry
resolves, including live network reachability for remote refs -- a
blind spot check-manifests.sh explicitly skips (local sources only).
- apm audit --ci: apm's own lockfile/policy/hidden-content integrity gate.
- apm pack --check-versions --check-clean: closes issue #90's deferred
item 3 (a check-clean-equivalent gate) using apm's native flag instead
of bespoke drift logic, verifying .claude-plugin/marketplace.json still
matches what apm.yml + .apm/ would currently generate.
All three are network-tolerant and whole-repo in scope, so they belong at
pre-push alongside check-manifests/check-plugin-content-sync/
validate-plugins -- not pre-commit, which stays fast/offline/per-file.
Documented the packing/bundling/releasing/CI findings in
docs/research/docs/microsoft-apm/releasing.md (new) and extended
testing-and-validation.md with the apm-action wrapper and its documented
CI patterns, sourced from Context7 and cross-checked against the
installed apm-cli 0.28.0 package directly.
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
The apm conversion (5e23250) moved skills/agents to plugins/<name>/.apm/
and deleted plugin-author/marketplace-author, but a review against issue
#90's Definition of Done found several stale pre-conversion references
left behind by straight git-mv's, plus one real content gap:
- ADR-0014 still documented the old flat vale-prefilter paths in 6
places, despite ADR-0015 claiming it had been updated.
- ADR-0015 also overclaimed: it said ADR-0014 had both a skills/ and an
agents/ path regex updated, but ADR-0014 never had an agents/ path
regex to begin with (its one "agents" mention is a Vale glob-section
identifier, not a filesystem path). Corrected the wording.
- CONTEXT.md's Skill glossary entry, its Vale-prefilter section, and its
plugin-author/marketplace-author forward-pointer all still described
the pre-conversion layout or a "pending issue #90" state that has since
landed.
- LESSONS.md and two skill test READMEs pointed at skill-author/
skill-audit/agent-author paths without the .apm/ segment.
- apm-workflow/references/marketplace.md cited plugin-author/SKILL.md's
Gotchas for the Claude Code reserved plugin-name-prefix list, but that
list was never actually carried into apm-workflow during the
conversion despite ADR-0015 claiming it was. Recovered the list from
git history and inlined it directly rather than leaving a dangling
citation.
- agent-author/references/deployment-modes.md had an example
contradicting its own stated .apm/agents/ convention two lines above.
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
field-inventory.md's apm-agent-allowlist and validate.sh's runtime
check already included source_keys as a 4th allowed field, and the
apm-agent.md template already instructed authors to add it for
provenance tracking — but SKILL.md (x2), README.md, ADR-0016, and
deployment-modes.md still described the allowlist as name/description/
model, "nothing else". The template itself even contradicted its own
source_keys guidance with a header claiming "ONLY the three fields
below — full stop" directly above it.
Updates all six locations to document source_keys as the intentional
4th field, resolving the contradiction.
validate.sh's detect_scope() and validate-provenance.sh's
find_plugin_root() disagreed with new-agent.sh's already-correct,
documented walk-up semantics on three points, each causing validate.sh
to false-FAIL a legitimately-scaffolded project-scope agent pair:
- a marker-less directory walked up into $HOME (no .git/apm.yml of its
own) was classified as user scope instead of project scope
- the .git-boundary branch returned the walked-to .git location instead
of the conventional scope root, breaking any <root> that is a
subdirectory of a larger git-tracked tree (monorepo package dirs)
- the new conventional-root arithmetic introduced to fix the above two
cases had no guard against non-conventional/hand-placed file paths,
which could point it at the wrong ancestor
Also adds scripts/check-scope-walkup-sync.sh, a behavioral drift-guard
(per ADR-0014's no-cross-skill-path precedent) that cross-checks the
four independently hand-ported walk-up implementations (validate.sh,
validate-provenance.sh, new-agent.sh, new-skill.sh) against real
fixture scaffolds, wired into .pre-commit-config.yaml at pre-push so
future drift between the ports is caught automatically.
Verified via bash tests/run-tests.sh (13/13) and targeted before/after
reproduction of each bug this closes.
A fresh /code-review of the APM-native authoring retarget (PR #93) found
several correctness bugs beyond the ones already fixed on this branch:
- new-agent.sh silently walked a marker-less subdirectory under $HOME up
to user scope, contradicting its own usage text ("user scope is checked
directly, no walk-up") and risking scaffolding into shared global
~/.claude or ~/.copilot directories instead of the intended local path.
- The hand-copied apm.yml type: manifest detector in new-agent.sh and
new-skill.sh accepted mismatched quotes (e.g. `type: "skill'`) that
validate.sh's regex correctly rejects, and silently dropped a final
apm.yml line lacking a trailing newline — causing the scaffolder and
validator to disagree on scope for identical input.
- Plugin-scope agent frontmatter could still contain the apm-agent.md
template's HTML comments at ship time with no audit signal, yet
apm compile copies frontmatter verbatim and <!-- --> breaks YAML
parsing on both downstream harnesses.
- ADR-0016 asserted agent-audit already implements a SUGGESTION heuristic
for tool-restriction-needing plugin-scope agents; it doesn't.
- agent-audit/README.md still described the old plugin-pair model this
PR replaced with a single-file allowlist model.
- validate.sh's project/user-scope CC-only/Copilot-only field checks and
counterpart-missing check lost their only test coverage when the old
plugin-pair fixture was deleted.
Also replaces an echo-into-sed two-value parse (4 forks per call) with a
single space-separated echo + read in both scaffolders.
Regression tests added for every fix above, including one for a bug this
pass introduced and the test suite caught: an initial two-line
echo + `read` attempt silently dropped the second value, since `read`
consumes only one line regardless of embedded newlines.
Full suite: 158 bats tests, 39 shell-script tests, 12/12 summary
categories, 0 failures.
Refs: #89, #93
Post-implementation review of PR #93 (issue #89's apm.yml-native retargeting
of skill-author/skill-audit/agent-author/agent-audit) found four confirmed
defects across the four scripts' apm.yml `type:` walk-up logic:
- field-inventory.md's apm-agent-allowlist was missing `source_keys`,
contradicting agent-author/SKILL.md's own instruction (Step 5 checklist)
to allow it at plugin/APM scope — a correctly-authored file with
source_keys failed validate.sh.
- validate.sh's APM_TYPE_RE and validate-provenance.sh's TYPE_RE disagreed:
the former tolerated a quoted `type: "skill"` value, the latter didn't,
despite agent-audit/SKILL.md explicitly documenting that
validate-provenance.sh walks up "the same way validate.sh does". Both
also used `\b` word-boundary matching, which false-matches a malformed
value like `type: prompts-only` on the `prompts` prefix. Unified both
regexes to be quote-tolerant and require an exact value.
- All four scripts' `.git` project-boundary check used isdir()/[[ -d ]],
which misses git worktrees where `.git` is a regular file (`gitdir: ...`)
rather than a directory. Switched to exists()/[[ -e ]].
- new-agent.sh and new-skill.sh had the same quote-intolerance as above via
inline `grep -qE` calls (new-skill.sh's also had the `\b` false-match
bug); replaced both with a shared-shape `is_apm_package_manifest` bash
helper matching the Python regex's semantics.
Four other findings from the same review turned out not to be bugs: a
bare `plugin.json` no longer signaling plugin scope is documented,
intentional behavior (agent-audit/SKILL.md:30, agent-author/SKILL.md:87),
deferred to issue #90's real plugin.json-to-apm.yml conversion — not
something this fix should reverse.
Verified via direct reproduction of each defect plus the full test suite:
147/147 bats tests, 39/39 shell-script tests, 12/12 summary categories.
Refs: #89
The file's body cites APM's Python source directly (apm_cli/models/validation.py,
apm_cli/primitives/models.py) to support its "no dedicated Agent validation"
claim, but source_keys only listed context7-microsoft-apm — the apm-github-repo
source_key was missing, and sources.md's apm-github-repo entry didn't list this
file under Contributing files either. The three sibling schema files from the
same commit (prompt/instructions/hooks) all cited both sources correctly.
Found via post-implementation review of issue #89.
detect_scope() had the same bug class fixed in new-agent.sh (099bdec):
it checked for a .git directory before checking whether it had reached
$HOME, so a dotfiles-managed home directory (yadm, chezmoi bare-repo,
etc.) made validate.sh misresolve to project scope, deriving the
counterpart as ~/.github/agents/<name>.agent.md instead of the correct
~/.copilot/agents/<name>.agent.md and failing with a false "counterpart
file not found". Check the $HOME boundary before the .git check, same
fix shape as 099bdec.
Found via post-implementation review of issue #89.
check_file()'s is_plugin_scope param and its plugin-silently-ignored
field check were unreachable dead code left over from the issue #89
restructure: plugin/APM scope now exits via check_apm_agent_file()
before check_file() is ever called, so is_plugin was always False.
Remove the param, its branch, the unused plugin_ignored_fields parse,
the now-stale field-inventory.md section, and the SKILL.md mention.
Found via post-implementation review of issue #89.
new-agent.sh's walk-up checked for a .git directory before checking
whether it had reached $HOME, so a dotfiles-managed home directory
(e.g. `~/.git` from yadm or a bare-repo checkout) made `new-agent.sh
<name> ~` silently resolve to project scope instead of user scope,
writing .github/agents/ instead of ~/.copilot/agents/. Check the
$HOME boundary before the .git check so it can no longer be shadowed.
Found via post-implementation review of issue #89.
skill-audit's cross-plugin path check now also recognizes .apm/skills/
<other-skill>/ as a breaking reference shape, alongside the existing
plugins/<plugin>/skills/<other-skill>/ pattern — the APM-native equivalent
of the same cache-isolation problem.
forge.md's Step 4 plugin-version-bump detection switches from "any
directory containing a plugin.json" to the same apm.yml+type: walk-up
used elsewhere in this batch, and hands off to apm-workflow (bump the
single apm.yml version) instead of the deprecated plugin-author.
Refs: #89
Validates the new single-file .apm/agents/<name>.agent.md shape agent-author
now produces at plugin/APM scope: frontmatter allowlist (name/description/
model only, from a new apm-agent-allowlist entry in field-inventory.md),
no counterpart derivation, and Pair Consistency dropped from that scope's
report entirely (nothing to pair by design). Adds a plugin/APM-scope-only
SUGGESTION when an agent's description/body implies a tool restriction or
Claude-only behavior the vendor-neutral frontmatter can no longer express
(ADR-0016).
Scope detection in both validate.sh and validate-provenance.sh switches
from a flat plugin.json/.claude-plugin/plugin.json check to a walk-up for
the nearest ancestor apm.yml with a top-level type: field, skipping
type:-less marketplace-only manifests — full switch, no dual-mode fallback
to the old plugin.json signal. validate-provenance.sh's walk-up was fixed
to match validate.sh's (it still used the old plugin.json check, and its
counterpart-merge logic was rewritten to read a single file's source_keys
instead of merging a CC+Copilot pair, since plugin/APM scope has no
counterpart). Project/user scope validation is unchanged in both scripts.
Refs: #89
Plugin scope now authors a single vendor-neutral .apm/agents/<name>.agent.md
file (name/description/model/body only) instead of a Claude Code + Copilot
CLI pair, per ADR-0016: apm compile has no per-target field integrator, so
tools: and all Claude-only fields (isolation/maxTurns/effort/memory/
permissionMode) are omitted entirely rather than shipping a value that's
guaranteed wrong on one harness. Scope detection switches from a flat
plugin.json check to the same apm.yml+type: walk-up agent-audit's
validate.sh already uses. Project scope and user scope are unaffected —
both keep the existing dual-file pair model.
Refs: #89
Skills now migrate from hand-authored plugin.json to Microsoft APM
(apm.yml + .apm/) per ADR-0015/issue #89. skill-author's write location
switches accordingly: walk up from the destination to the nearest
ancestor apm.yml declaring a type: field (skipping type:-less
marketplace-only manifests) and scaffold into <package-root>/.apm/skills/
<name>/. Standalone authoring (no apm.yml/.git anywhere, e.g.
~/.agents/skills/) is unchanged — only the old plugin.json-based path is
replaced, not standalone mode.
Refs: #89
#89's grilling session surfaced a real upstream gap: APM's agent
primitive has no per-target compile integrator, so tools: and every
Claude-only field (isolation, maxTurns, effort, memory, permissionMode)
would verbatim-copy into Copilot's file with an incompatible or
unrecognized shape. Omitting them entirely is the only option that
never ships a wrong value to a real harness.
Scoped to plugin-scope agent-author only — project/user scope keep
today's Claude+Copilot pair model unchanged. Partially supersedes
ADR-0005 and ADR-0008's plugin-scope clauses (addenda appended to
both); ADR-0009's field-inventory.md mechanism is unaffected, only
its plugin-scope content shape changes.
Refs: #89
skill-author/agent-author's #89 retarget needs to know exactly how each
.apm/ primitive compiles to Claude Code and Copilot CLI output. The
existing microsoft-apm corpus only had a full schema for skills and one
minimal example for agents, and nothing for prompts/instructions/hooks.
Deepened via APM's own Python source (not just docs) where prose was
thin. Key finding for #89: agents have no per-target integrator, so
apm compile does a naive verbatim copy to both Claude and Copilot,
unlike prompts/instructions/hooks which each get real per-target
reconstruction. That means the agent primitive's `tools:` field can't
express both harnesses' incompatible vocabularies at once — a real
upstream gap, not something we can schema our way around.
Two review passes on PR #91 (correctness + simplification) found the
prior fixup commit (d6fd9b6) left the deprecation notice it added
half-applied, cited a nonexistent ADR, and introduced/compounded a
few duplicate restatements of the same facts across apm-workflow's
skill files and apm-orchestrate's Hard rules.
- plugin-author/SKILL.md cited ADR-0016 (doesn't exist) instead of
ADR-0006 for the version-parity invariant.
- The deprecation banner on plugin-author/marketplace-author wasn't
reflected in their routing `description:` frontmatter, forge's
routing table, either kyberforge README skill table, or CONTEXT.md
— now propagated consistently; routing behavior is unchanged since
issue #90 (repo-wide apm conversion) hasn't landed yet.
- apm-orchestrate's `configure` operation only covered scaffolding a
new package, not editing an existing apm.yml (e.g. removing a
marketplace.packages[] entry, which has no dedicated `apm` CLI verb
and is just a manifest edit). Added `edit-config`, intentionally
ungated — a local file edit isn't the same class of irreversible
action as `apm publish`, and normal review already gates the commit.
- Deduplicated facts restated 3-4x across SKILL.md/references/ADR
boundaries (audit semantics, reserved name prefixes, marketplace-add
direction, registries precondition), and trimmed configure.md's
~55-line apm.yml schema dump — copied near-verbatim from the
research doc — down to a compact key list with a pointer, matching
install.md's existing terse cross-referencing style.
Deliberately left as-is: apm-orchestrate's parallel/sequential
fan-out logic stays inline rather than extracted to a shared
reference, since neither git-orchestrate nor gitea-orchestrate need
it yet — premature extraction for a single caller.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0186ERbyACLRuRxPRnqwpa4m
Addresses 7 verified findings from the /code-review pass on PR #91's
APM-conversion changes:
- apm-workflow's description omitted install triggers, misrouting
"install my apm dependencies" requests to apm-install instead
- `apm marketplace check` was listed under both the marketplace and
audit dispatch rows, breaking the "one reference file per action"
contract; kept under marketplace.md, its real usage-sequence home
- ADR-0015 claimed apm-workflow docs are generic/repo-agnostic while
marketplace.md cited this repo's AGENTS.md and git-commits/
git-remotes skills by name; stripped the holocron-specific
citations (marketplace.md, compile.md) so the claim now holds
- plugin-author/marketplace-author are superseded per ADR-0015 with
deletion deferred to #90; added deprecation notices pointing to
the replacing apm-workflow dispatch actions
- apm-orchestrate/gitea-orchestrate/git-orchestrate all instruct
dispatch "via Skill" without granting the Skill tool; added it to
all three
- apm-orchestrate mandated strictly sequential fan-out across
independent packages with no shared state; relaxed to permit
parallel dispatch, matching this repo's own subagent-parallelization
guidance
Refs: #91
A grill-me session determined plugin-author/marketplace-author are fully
superseded by the landed apm-install/apm-workflow/apm-orchestrate artifacts
and will be deleted (not adapted) under issue #90, narrowing issue #89 to
skill-author/agent-author only; forge stays untouched. ADR-0015 is amended
to record this correction plus a three-way content-migration audit.
Claude Code platform-constraint facts that lived only in
plugin-author/marketplace-author (reserved name prefixes, the ADR-0010
agents/ stray-.md validator gotcha, claude plugin validate as a terminal
check, and a removal/re-pack confirm-before-proceeding note) remain true
post-conversion and are migrated into apm-workflow's reference docs via
kyberforge:skill-author, which also updated the skill's README and
sources.md provenance records. Dual-manifest-specific conventions (ADR-0006
parity, CC/Copilot field-placement split) do not carry forward since they
no longer apply.
ADR: 0015
Re-review (comment 24) of fix commit e16c3dc found six new issues, mostly
introduced by that fix commit itself: a dangling reference to a Hard Rule
bullet the same commit deleted (apm-orchestrate.md/.agent.md Process step
2 still named "secret indirection"), and an ADR-0015 Decision bullet that
claimed "this ADR does not update CONTEXT.md" while the same commit had
just added a forward-pointer sentence to CONTEXT.md's Plugin/Plugin
marketplace entries. Both reworded to match what actually happened.
apm-install's APM_INSTALL_DIR escape-hatch example dropped the curl pipe
entirely (`APM_INSTALL_DIR=... sh` with nothing piped into it) — fixed in
both apm-install/SKILL.md and the installation.md research doc, verified
against the upstream Microsoft APM docs via Context7.
Neither apm-workflow nor apm-orchestrate routed to plain `apm install
[PACKAGE_REF]`, the CLI command that actually resolves/fetches
dependencies declared in apm.yml — apm-install only bootstraps the apm
binary/runtime, not per-package deps. Added a 5th "install" dispatch
action to apm-workflow (new references/install.md, SKILL.md table row,
README usage/files sync, sources.md provenance entry) and a matching
"install" operation group on apm-orchestrate so it can route there.
configure.md's apm.yml schema block was also missing the "legacy singular
`target:` CSV form is still accepted" caveat its sibling research doc
documents for the same field — added for consistency.
The sixth finding (paired .md/.agent.md Output-contract disagreement) was
checked against git-orchestrate and gitea-orchestrate's existing pairs and
found to match established repo convention (JSON schema in .md, prose
summary without the enum in .agent.md) — left unchanged as a false
positive rather than "fixed."
kyberforge bumped 1.3.0 -> 1.3.1 via agent-author's normal improve flow.
Refs: #91
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0186ERbyACLRuRxPRnqwpa4m
ADR-0015's Decision bullet stated ADR-0001 was superseded as present
fact while Consequences framed it as conditional on issue #90 —
reworded Decision to match, and added a Supersedes header (repo
convention per ADR-0007/ADR-0010) so the pending change is visible
from the title. Also fixed a forward-note that misattributed the
pre-existing .agent.md convention (ADR-0005/0010) to the APM move,
and reworded the rejected-plugins/apm/ rationale to acknowledge the
shipped skills are actually generic rather than repo-specific.
CONTEXT.md's Plugin/Plugin-marketplace glossary entries got a
forward-pointer to ADR-0015 so a session-start read surfaces the
pending manifest-authoring change.
apm-orchestrate.md/.agent.md: removed a Hard Rule requiring ${VAR}
secret indirection that can never fire (the orchestrator has no
Edit/Write tool and never touches apm.yml — apm-workflow's SKILL.md
already owns this rule where content is actually written); removed
compile-manifest-check, an operation with no backing apm CLI command
anywhere in the research docs; added explicit CLI mappings for
add-package/add-marketplace so an executing agent can't invert them.
marketplace.md's release steps shelled out to raw git add/commit/
tag/push — replaced with a pointer to the git-commits/git-remotes
skills per AGENTS.md's "prefer plugin skills over raw shell" rule.
compile.md now states audit runs after compile/pack (audit scans
deployed output, not the source apm.yml) — answers a reviewer
question that had no documented answer.
Refs: #91
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0186ERbyACLRuRxPRnqwpa4m
Deterministic counterpart to apm-workflow for subagent dispatch,
mirroring git-orchestrate/gitea-orchestrate. Scoped to
configure/marketplace/compile/audit, with fan-out across multiple
packages for the future multi-plugin conversion; apm-install has no
orchestrator counterpart since it's a one-time machine bootstrap.
Bumps kyberforge 1.2.8 -> 1.3.0 (new agent, first in the plugin).