a8cd5e8 was squashed into 598a7c3 (#129) and is reachable from no
branch, so the comments now cite the commit that exists on main.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Correct the hashes left by the branch rewrite (467bbd7 -> 620f20b,
4059cb4 -> ffcbed6), annotate a8cd5e8 and c59e4bf as reachable only
through the 598a7c3 squash, strike the case 33 claims that 4de5b6b
made stale, and re-measure the section 1 table, the gates.md length and
the ADR share at baa2f5d. Add section 12 for the final seven-reviewer
round. Move the record under docs/notes/, alongside the repo's other
closed decision records, and update ADR-0024's pointer to it.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
improve.md still required a retrofit before extending but lost the
procedure with retrofit.md. Restore the ordered cuts inline, and fix
the stale hook name and plugin-mode wording in skill-author's tests and
deployment-modes reference.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The exception pointed at a Gotcha that does not exist. State the rule
(machine-parsed output or an interactive editor runs bare git, with the
reason inline) and point at rewrite-history.md for interactive rebase.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
ADR-0014 gains a dated correction: skill-size-check now sources the
boundary resolver from kyberforge (ef27c97), so restoring the external
hook contract needs it made self-contained first. ADR-0017's status
reflects its supersession, architecture.md and gates.md carry the
current duplication counts and reason, gates.md defines vacuous green
inline, and the gitleaks lesson is marked historical.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
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>
Both runners excluded */.claude/worktrees/* by absolute path, which
filtered out every test when the repo itself is a Claude worktree.
Search from inside the root so only nested worktrees are skipped.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Skills no longer carry a README.md, so the size advice in
skill-size-check and factory-audit's validator now names a references/
file instead.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
read_version required --- at byte 0 while skill-size-check accepts
leading blank lines, so a file one gate passed the other reported as
unversioned, and an unversioned merge-base side let an unbumped change
through. Match FRONTMATTER_RE, add case 39, and describe the main-tip
check and fail-closed cases in the hook entry.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
apm recognises its own settings.json hook entries only through the
.claude/apm-hooks.json sidecar. With the sidecar gitignored, apm install
in a fresh clone keeps the committed SessionStart entry as user-owned and
appends a duplicate, so apm audit --ci reports drift and the apm-audit-ci
pre-push hook fails. Reproduced on main and this branch with apm 0.28.0.
Commit the sidecar in apm's exact serialisation, exclude it from
pretty-format-json alongside settings.json, and record the correction
in ADR-0019.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-16 15:26:56 +00:00
28 changed files with 263 additions and 69 deletions
"description":"AI development skills for Claude Code and GitHub Copilot CLI — factory, design, implement, review, and cross-cutting workflows.",
"description":"AI development skills for Claude Code, and for GitHub Copilot through apm — factory, design, implement, review, and cross-cutting workflows.",
"version":"0.4.7",
"version":"0.5.0",
"owner":{
"owner":{
"name":"Defame1297",
"name":"Defame1297",
"email":"defame1297@rkdr.net",
"email":"defame1297@rkdr.net",
@@ -10,7 +10,7 @@
"plugins":[
"plugins":[
{
{
"name":"kyberforge",
"name":"kyberforge",
"description":"Skills and agents for creating, maintaining, and managing a Claude Code / Copilot CLI plugin marketplace.",
"description":"Skills and agents for creating, maintaining, and managing an apm plugin marketplace for Claude Code and GitHub Copilot.",
description:Onevery push, fail if a skill directory changed (tests/ excluded) since the merge-base with main without its SKILL.md metadata.version rising (ADR-0022)
description:Onevery push, fail if a skill directory changed (tests/ excluded) since the merge-base with main without its SKILL.md metadata.version rising above both that merge-base's and main's tip's (ADR-0022)
entry:bash scripts/check-skill-version-bump.sh
entry:bash scripts/check-skill-version-bump.sh
language:system
language:system
stages:[pre-push]
stages:[pre-push]
pass_filenames:false
pass_filenames:false
always_run:true
always_run:true
# Baseline is the merge-base with origin/main (falling back to main),
# "Changed" is measured from the merge-base with origin/main (falling
# not the remote branch tip: readers install from main. Fails closed
# back to main), not the remote branch tip: readers install from main.
# when no main ref resolves. Merges through Gitea's merge button run no
# The version must also beat main's tip, so two branches making the same
# local hook, so they bypass this.
# bump cannot both land. Fails closed when no main ref resolves, when
# there is no merge-base, or when only local main resolves and already
# contains the pushed commit. Merges through Gitea's merge button run no
# local hook, so they bypass this. See docs/spec/gates.md.
@@ -40,9 +40,9 @@ A research sub-agent reported "Process goes in SKILL.md, context in reference fi
`claude plugin validate --strict` was left out of the standard plugin audit sweep and only discovered when the user flagged the gap. It catches warnings (missing `version` fields, stray non-agent `.md` files) that will fail CI once strict mode is enforced. Fix: run it on every plugin path and marketplace manifest as a named audit step.
`claude plugin validate --strict` was left out of the standard plugin audit sweep and only discovered when the user flagged the gap. It catches warnings (missing `version` fields, stray non-agent `.md` files) that will fail CI once strict mode is enforced. Fix: run it on every plugin path and marketplace manifest as a named audit step.
## 2026-06-21 — Source and deployed gitleaks configs can silently diverge
## 2026-06-21 — Source and deployed gitleaks configs can silently diverge (historical)
`scripts/gitleaks.toml` (source) and `.gitleaks.toml` (deployed, hook-read) drifted after someone edited the deployed copy directly; rerunning `setup-gitleaks.sh` would have overwritten it, silently deleting the allowlist. Fix: treat the source as sole truth, never hand-edit the deployed copy, and update both together in the same commit.
Superseded — `5b8b6f5` removed `scripts/gitleaks.toml` and `setup-gitleaks.sh`, so `.gitleaks.toml` is now the only copy and there is nothing to diverge from. Kept for the general pattern, which applies to any source/deployed pair: `scripts/gitleaks.toml` (source) and `.gitleaks.toml` (deployed, hook-read) drifted after someone edited the deployed copy directly; rerunning `setup-gitleaks.sh` would have overwritten it, silently deleting the allowlist. Fix: treat the source as sole truth, never hand-edit the deployed copy, and update both together in the same commit.
## 2026-06-21 — `shellcheck` without `-x` blocks pre-commit on scripts using `source` (historical)
## 2026-06-21 — `shellcheck` without `-x` blocks pre-commit on scripts using `source` (historical)
@@ -17,7 +17,7 @@ Content ships as six installable plugins, each an apm (Agent Package Manager) pa
The six plugins:
The six plugins:
- **kyberforge** — skills and agents for creating, maintaining, and managing a Claude Code / Copilot CLI plugin marketplace
- **kyberforge** — skills and agents for creating, maintaining, and managing an apm plugin marketplace for Claude Code (and GitHub Copilot through apm)
- **git** — conventional commits, branches, history, submodules, worktrees, remotes, pre-commit hook authoring and running (`pc-author` / `pc-run`), and an interactive router (`git-workflow`)
- **git** — conventional commits, branches, history, submodules, worktrees, remotes, pre-commit hook authoring and running (`pc-author` / `pc-run`), and an interactive router (`git-workflow`)
- **gitea** — issues, pull requests, labels, milestones, releases, branches, files, and an interactive router (`gitea-workflow`)
- **gitea** — issues, pull requests, labels, milestones, releases, branches, files, and an interactive router (`gitea-workflow`)
- **core** — authoring and auditing a repo's `AGENTS.md` and the provider adapter files that defer to it
- **core** — authoring and auditing a repo's `AGENTS.md` and the provider adapter files that defer to it
description:AI development skills for Claude Code and GitHub Copilot CLI — factory, design, implement, review, and cross-cutting workflows.
description:AI development skills for Claude Code, and for GitHub Copilot through apm — factory, design, implement, review, and cross-cutting workflows.
license:MIT
license:MIT
# Consumer side: this repo installs its own published plugins from the holocron
# Consumer side: this repo installs its own published plugins from the holocron
@@ -51,8 +51,8 @@ marketplace:
# compiled marketplace.json when set explicitly here (an override) — the
# compiled marketplace.json when set explicitly here (an override) — the
# top-level apm.yml description:/version: above are NOT inherited into the
# top-level apm.yml description:/version: above are NOT inherited into the
# compiled output despite being used elsewhere (e.g. by `apm audit`).
# compiled output despite being used elsewhere (e.g. by `apm audit`).
description:AI development skills for Claude Code and GitHub Copilot CLI — factory, design, implement, review, and cross-cutting workflows.
description:AI development skills for Claude Code, and for GitHub Copilot through apm — factory, design, implement, review, and cross-cutting workflows.
@@ -19,7 +19,7 @@ for the parent decision. It resolves the one question ADR-0015's own execution f
did not block on: whether Claude Code's installer can actually load content out of `.apm/`. It
did not block on: whether Claude Code's installer can actually load content out of `.apm/`. It
could not.
could not.
**Status: executed (2026-08-13, issue #90).**`scripts/sync-plugin-content.sh` has been run
**Status: superseded by ADR-0024** (originally executed 2026-08-13, issue #90; the text below is the record of that execution).`scripts/sync-plugin-content.sh` has been run
against all 6 plugins; flat `agents/`, `skills/`, `commands/` (etc., wherever `.apm/` populates
against all 6 plugins; flat `agents/`, `skills/`, `commands/` (etc., wherever `.apm/` populates
them), and a merged hooks file now exist at each plugin root as tracked, generated files. The
them), and a merged hooks file now exist at each plugin root as tracked, generated files. The
merged hooks file lands at `hooks/hooks.json`, not at the plugin root itself — see the second
merged hooks file lands at `hooks/hooks.json`, not at the plugin root itself — see the second
@@ -23,7 +23,7 @@ Counting convention: line counts are hand-edited `.apm/` source unless marked "i
>
>
> > **Re-measured (2026-09-14, at `a6434e0`):** the right-hand column originally read 31,473 / 6,050 / 3,471 / 2,360 / 923 / 2,083 = 46,360 and was labelled "Today" against "the current working tree". It did not reconcile to its own commit's tree — at `061bb3d`, where it was written, the six plugins measured 31,435 / 6,048 / 3,474 / 2,358 / 926 / 2,087 = 46,328 — and "the current working tree" is a basis that goes stale silently. Re-counted at `a6434e0` and the column now names its SHA. The baseline column is confirmed exact against `9eb8bc7`. Commits after `061bb3d` (`c96ca9c`, which deleted the six plugin-root `.mcp.json` files) account for most of the remaining drift.
> > **Re-measured (2026-09-14, at `a6434e0`):** the right-hand column originally read 31,473 / 6,050 / 3,471 / 2,360 / 923 / 2,083 = 46,360 and was labelled "Today" against "the current working tree". It did not reconcile to its own commit's tree — at `061bb3d`, where it was written, the six plugins measured 31,435 / 6,048 / 3,474 / 2,358 / 926 / 2,087 = 46,328 — and "the current working tree" is a basis that goes stale silently. Re-counted at `a6434e0` and the column now names its SHA. The baseline column is confirmed exact against `9eb8bc7`. Commits after `061bb3d` (`c96ca9c`, which deleted the six plugin-root `.mcp.json` files) account for most of the remaining drift.
> **Re-derived (2026-09-16, at HEAD on `docs/simplification-audit`):** the 2026-09-15 notes recording finding 14's merge (`467bbd7`, ADR-0025) and the pipefail fix (`4059cb4`) were written without correcting the headlines they annotate, so this pass re-counted every figure those two commits could have moved and corrected each in place above and below. Everything re-measured here came from a command run at HEAD — `git ls-files`, `wc -l`, `grep -c`, and `bash tests/run-tests.sh --strict` — never from an earlier note. What moved: finding 2 (two surviving sync gates → one), the `.pre-commit-config.yaml` hook counts (27/9 → 26/8, then back to 27/9 — see the correction at the end of this note), the skill census (39 → 38 and everything derived from it), finding 11's validator and `sources.md` figures, finding 16's whole numeric basis, and the stale `skill-audit/`, `agent-audit/` and `formatting-and-scripts.md` paths in findings 18, 19 and 33. §1's three rows re-measured: ~~**469**~~ → **471** tracked files (~~465~~ → 467 regular plus the 4 submodule gitlinks) / ~~**74,594**~~ → **75,441** lines (pinned to `c07ca07`; see the note below); `plugins/`~~**46,106** (62%)~~ → **46,127** (61%); the 38 `SKILL.md` bodies **2,409** (5.2% of plugin lines); enforcement ~~**20 `tests/test-*.sh` totalling 10,189 lines**~~ → **21 `tests/test-*.sh` totalling 10,608 lines**, the two runners **502** (`run-tests.sh` 283 + `run-bats.sh` 219), and `scripts/`~~**2,901**~~ → **3,139**; kyberforge's validator scripts and their bats tests ~~**5,861**~~ → **5,876** + **6,015** (the merge deduplicated scripts and left the test corpus larger, not smaller — `git ls-files 'plugins/kyberforge/.apm/skills/*/scripts/*.sh'` and `.../tests/*.bats`). `run-tests.sh --strict` reports ~~**20 passed, 0 skipped, 0 failed**~~ → **21 passed, 0 skipped, 0 failed**.
> **Re-derived (2026-09-16, at HEAD on `docs/simplification-audit`):** the 2026-09-15 notes recording finding 14's merge (~~`467bbd7`~~ → `620f20b`, ADR-0025) and the pipefail fix (~~`4059cb4`~~ → `ffcbed6`) were written without correcting the headlines they annotate, so this pass re-counted every figure those two commits could have moved and corrected each in place above and below. Everything re-measured here came from a command run at HEAD — `git ls-files`, `wc -l`, `grep -c`, and `bash tests/run-tests.sh --strict` — never from an earlier note. What moved: finding 2 (two surviving sync gates → one), the `.pre-commit-config.yaml` hook counts (27/9 → 26/8, then back to 27/9 — see the correction at the end of this note), the skill census (39 → 38 and everything derived from it), finding 11's validator and `sources.md` figures, finding 16's whole numeric basis, and the stale `skill-audit/`, `agent-audit/` and `formatting-and-scripts.md` paths in findings 18, 19 and 33. §1's three rows re-measured: ~~**469**~~ → **471** tracked files (~~465~~ → 467 regular plus the 4 submodule gitlinks) / ~~**74,594**~~ → **75,441** lines (pinned to `c07ca07`; see the note below); `plugins/`~~**46,106** (62%)~~ → **46,127** (61%); the 38 `SKILL.md` bodies **2,409** (5.2% of plugin lines); enforcement ~~**20 `tests/test-*.sh` totalling 10,189 lines**~~ → ~~**21 `tests/test-*.sh` totalling 10,608 lines**~~ → **19 totalling 10,088** at `baa2f5d`, the two runners **502** (`run-tests.sh` 283 + `run-bats.sh` 219), and `scripts/`~~**2,901**~~ → **3,139**; kyberforge's validator scripts and their bats tests ~~**5,861**~~ → **5,876** + **6,015** (the merge deduplicated scripts and left the test corpus larger, not smaller — `git ls-files 'plugins/kyberforge/.apm/skills/*/scripts/*.sh'` and `.../tests/*.bats`). `run-tests.sh --strict` reports ~~**20 passed, 0 skipped, 0 failed**~~ →~~**21 passed, 0 skipped, 0 failed**~~ →**19 passed, 0 skipped, 0 failed** at `baa2f5d` (`4de5b6b` deleted two suites).
>
>
> > **Re-measured (2026-09-16, at `c07ca07`):** commit `8451169` added `check-skill-version-bump` — a pre-push hook, `scripts/check-skill-version-bump.sh` (238 lines) and `tests/test-skill-version-bump.sh` (410) — after the figures above were taken, so each was one short. `.pre-commit-config.yaml` now has **27**`- id:` entries and **9**`stages: [pre-push]` (`grep -c -- "- id:"`; `grep -c "stages: \[pre-push\]"`), all nine repo-authored. The struck figures are replaced from these commands. They were run against the working tree, and every figure reproduces exactly from the committed tree at `c07ca07`: `git ls-files | wc -l`; `cat` over every non-gitlink tracked path `| wc -l`; `git ls-files plugins | xargs cat | wc -l`; `git ls-files scripts | xargs wc -l` (no untracked files under `scripts/`); `ls tests/test-*.sh | wc -l` and `cat tests/test-*.sh | wc -l`; `bash tests/run-tests.sh --strict`. The earlier 469 / 74,594 / 46,106 did not reproduce exactly at `8451169^` either (469 / 74,638 / 46,121), so they were taken at an earlier commit than this note's "at HEAD" says. Re-checked and unchanged, so left alone: `docs/research/` inside plugins (19,030) and repo-level `docs/research/` + `docs/notes/` (4,488). Not re-measured, and still carrying their last stated basis: the preload-tax and commit-share rows, §2's timings, and the per-plugin table in the note above.
> > **Re-measured (2026-09-16, at `c07ca07`):** commit `8451169` added `check-skill-version-bump` — a pre-push hook, `scripts/check-skill-version-bump.sh` (238 lines) and `tests/test-skill-version-bump.sh` (410) — after the figures above were taken, so each was one short. `.pre-commit-config.yaml` now has **27**`- id:` entries and **9**`stages: [pre-push]` (`grep -c -- "- id:"`; `grep -c "stages: \[pre-push\]"`), all nine repo-authored. The struck figures are replaced from these commands. They were run against the working tree, and every figure reproduces exactly from the committed tree at `c07ca07`: `git ls-files | wc -l`; `cat` over every non-gitlink tracked path `| wc -l`; `git ls-files plugins | xargs cat | wc -l`; `git ls-files scripts | xargs wc -l` (no untracked files under `scripts/`); `ls tests/test-*.sh | wc -l` and `cat tests/test-*.sh | wc -l`; `bash tests/run-tests.sh --strict`. The earlier 469 / 74,594 / 46,106 did not reproduce exactly at `8451169^` either (469 / 74,638 / 46,121), so they were taken at an earlier commit than this note's "at HEAD" says. Re-checked and unchanged, so left alone: `docs/research/` inside plugins (19,030) and repo-level `docs/research/` + `docs/notes/` (4,488). Not re-measured, and still carrying their last stated basis: the preload-tax and commit-share rows, §2's timings, and the per-plugin table in the note above.
>
>
@@ -37,19 +37,19 @@ Counting convention: line counts are hand-edited `.apm/` source unless marked "i
| Lines in `plugins/` | ~~70,600 (69% of repo)~~ → ~~46,301 (63% of repo)~~ → ~~46,106 (62% of repo)~~ → 46,127 (61% of repo) |
| Lines in `plugins/` | ~~70,600 (69% of repo)~~ → ~~46,301 (63% of repo)~~ → ~~46,106 (62% of repo)~~ → ~~46,127 (61% of repo)~~ → 46,232 (62% of repo, at `baa2f5d`) |
| Of which the ~~39~~ → 38 `SKILL.md` files a model actually loads | ~~about 2,600 lines (under 4% of plugin lines)~~ → ~~2,509 lines (5.4% of plugin lines)~~ → 2,409 lines (5.2% of plugin lines) |
| Of which the ~~39~~ → 38 `SKILL.md` files a model actually loads | ~~about 2,600 lines (under 4% of plugin lines)~~ → ~~2,509 lines (5.4% of plugin lines)~~ → 2,409 lines (5.2% of plugin lines; unchanged at `baa2f5d`) |
| Commits since 2026-05-10 / share touching hook, test, gate, vale, or sync | 447 / ~25% |
| Commits since 2026-05-10 / share touching hook, test, gate, vale, or sync | 447 / ~25% |
> **Corrected then done (2026-09-14):** the mirror row's figure was wrong. The true mirror was **213 files / 20,061 lines**, not 263 / ~22,000 — the original count swept in files that were never mirror output. All 213 were deleted in commit `718c79a` on `docs/simplification-audit` (245 files changed, 298 insertions, 22,602 deletions across the whole change), so the row is now zero. The enforcement row is stale on **both** halves — it was correct at the 2026-09-10 baseline (`9eb8bc7`: 33 `- id:` entries, 14 repo-authored pre-push hooks), but `.pre-commit-config.yaml` today has ~~**27 entries and 9 `stages: [pre-push]`**~~ → ~~**26 entries and 8 `stages: [pre-push]`**~~ → ~~**27 entries and 9 `stages: [pre-push]`**~~ → **26 entries and 8 `stages: [pre-push]`** (`467bbd7` removed `check-vale-style-sync` with finding 14's merge; `8451169` then added `check-skill-version-bump`; `4de5b6b` then removed `check-release-needed`; re-measured 2026-09-16 at `4b17703` with `grep -c -- "- id:"` and `grep -c "stages: \[pre-push\]"` on `.pre-commit-config.yaml`). Like for like that is 14 → ~~9~~ → ~~8~~ → ~~9~~ → 8 repo-authored pre-push hooks. The stage *reports*~~11~~ → ~~10~~ → ~~11~~ → 10, because the 2 pre-commit `meta` hooks also run there — a different counting basis; see the corrected §3 target, which states it the same way.
> **Corrected then done (2026-09-14):** the mirror row's figure was wrong. The true mirror was **213 files / 20,061 lines**, not 263 / ~22,000 — the original count swept in files that were never mirror output. All 213 were deleted in commit `718c79a` on `docs/simplification-audit` (245 files changed, 298 insertions, 22,602 deletions across the whole change), so the row is now zero. The enforcement row is stale on **both** halves — it was correct at the 2026-09-10 baseline (`9eb8bc7`: 33 `- id:` entries, 14 repo-authored pre-push hooks), but `.pre-commit-config.yaml` today has ~~**27 entries and 9 `stages: [pre-push]`**~~ → ~~**26 entries and 8 `stages: [pre-push]`**~~ → ~~**27 entries and 9 `stages: [pre-push]`**~~ → **26 entries and 8 `stages: [pre-push]`** (~~`467bbd7`~~ → `620f20b` removed `check-vale-style-sync` with finding 14's merge; `8451169` then added `check-skill-version-bump`; `4de5b6b` then removed `check-release-needed`; re-measured 2026-09-16 at `4b17703` with `grep -c -- "- id:"` and `grep -c "stages: \[pre-push\]"` on `.pre-commit-config.yaml`). Like for like that is 14 → ~~9~~ → ~~8~~ → ~~9~~ → 8 repo-authored pre-push hooks. The stage *reports*~~11~~ → ~~10~~ → ~~11~~ → 10, because the 2 pre-commit `meta` hooks also run there — a different counting basis; see the corrected §3 target, which states it the same way.
> **Re-measured (2026-09-14, at `a6434e0`):** this table is a **dated snapshot corrected in place**, not a live figure — every arrow above reads "baseline (2026-09-10, `9eb8bc7`) → value at the stated commit". Three further rows were still carrying baseline values after `d2480b8`/`061bb3d` corrected their neighbours, and are now corrected at `a6434e0`:
> **Re-measured (2026-09-14, at `a6434e0`):** this table is a **dated snapshot corrected in place**, not a live figure — every arrow above reads "baseline (2026-09-10, `9eb8bc7`) → value at the stated commit". Three further rows were still carrying baseline values after `d2480b8`/`061bb3d` corrected their neighbours, and are now corrected at `a6434e0`:
>
>
@@ -112,12 +112,12 @@ This is the area you named as hardest to understand and slowest. Root cause: mos
> Marked `[x]`: all three of this finding's decisions are resolved — `check-manifests` deleted (`e647f14`), `apm-audit-ci` kept on the grill above, `apm-marketplace-check` removed (`0dffff3`).
> Marked `[x]`: all three of this finding's decisions are resolved — `check-manifests` deleted (`e647f14`), `apm-audit-ci` kept on the grill above, `apm-marketplace-check` removed (`0dffff3`).
2. [x] **~~Four~~ ~~two~~ → one surviving "keep two copies in sync" gate: ~~1,100 script lines + 1,600 test lines~~~~778 script lines + 1,079 test lines~~ → 381 script lines + 297 test lines.** Each one is a symptom of duplication that could be removed instead of guarded:
2. [x] **~~Four~~ ~~two~~ → one surviving "keep two copies in sync" gate: ~~1,100 script lines + 1,600 test lines~~~~778 script lines + 1,079 test lines~~ → 381 script lines + 297 test lines.** Each one is a symptom of duplication that could be removed instead of guarded:
> **Re-measured (2026-09-16, at HEAD):**`467bbd7` deleted `check-vale-style-sync` with finding 14's merge, so the "two" above is now **one** — `check-scope-walkup-sync`, at **381** script lines (`wc -l scripts/check-scope-walkup-sync.sh`) and **297** test lines (`wc -l tests/test-check-scope-walkup-sync.sh`). Both grew since `a6434e0`, where they measured 365 + 282. Reading `check-executables-allow-sync` into the group as the note below does makes it two gates, **603 + 540** (222 + 243 for that one, unchanged).
> **Re-measured (2026-09-16, at HEAD):**~~`467bbd7`~~ → `620f20b` deleted `check-vale-style-sync` with finding 14's merge, so the "two" above is now **one** — `check-scope-walkup-sync`, at **381** script lines (`wc -l scripts/check-scope-walkup-sync.sh`) and **297** test lines (`wc -l tests/test-check-scope-walkup-sync.sh`). Both grew since `a6434e0`, where they measured 365 + 282. Reading `check-executables-allow-sync` into the group as the note below does makes it two gates, **603 + 540** (222 + 243 for that one, unchanged).
> **Re-measured (2026-09-14, at `a6434e0`):** two of the four are gone — `check-marketplace-mirror-sync` deleted in `0dffff3` (2c below) and, though it was never in this finding's own count, `check-plugin-content-sync` in `718c79a`. The two that survive are `check-vale-style-sync` (413 script + 797 test) and `check-scope-walkup-sync` (365 + 282); `check-executables-allow-sync` also survives, shrunk to 222 + 243 (2d below), and counts as the third if that gate is read as part of this group rather than as its own item. Two-gate total 778 + 1,079; three-gate total 1,000 + 1,322. The per-bullet script and test figures below are all still exact at this commit except `check-executables-allow-sync`'s "474 lines", which 2d already corrects.
> **Re-measured (2026-09-14, at `a6434e0`):** two of the four are gone — `check-marketplace-mirror-sync` deleted in `0dffff3` (2c below) and, though it was never in this finding's own count, `check-plugin-content-sync` in `718c79a`. The two that survive are `check-vale-style-sync` (413 script + 797 test) and `check-scope-walkup-sync` (365 + 282); `check-executables-allow-sync` also survives, shrunk to 222 + 243 (2d below), and counts as the third if that gate is read as part of this group rather than as its own item. Two-gate total 778 + 1,079; three-gate total 1,000 + 1,322. The per-bullet script and test figures below are all still exact at this commit except `check-executables-allow-sync`'s "474 lines", which 2d already corrects.
- [x] ~~`check-vale-style-sync`: 413 lines + 798 test lines guarding a byte-identical 526-line `vale-wrap.sh` and style directory copied between skill-audit and agent-audit. About 350 of its lines run Vale glob probes against the hook file patterns. Disappears if the two audit skills merge (finding 14); the probes belong in `test-vale-wrap.sh`.~~**Done (2026-09-15, `467bbd7`)** — hook, script and test all deleted; see the settled note below for the corrected probe arithmetic.
- [x] ~~`check-vale-style-sync`: 413 lines + 798 test lines guarding a byte-identical 526-line `vale-wrap.sh` and style directory copied between skill-audit and agent-audit. About 350 of its lines run Vale glob probes against the hook file patterns. Disappears if the two audit skills merge (finding 14); the probes belong in `test-vale-wrap.sh`.~~**Done (2026-09-15, ~~`467bbd7`~~ → `620f20b`)** — hook, script and test all deleted; see the settled note below for the corrected probe arithmetic.
- `check-scope-walkup-sync`: ~~365~~ → **381** lines (plus **297** test lines; re-measured 2026-09-16 at HEAD) cross-checking four independent ports of the same package-root walk-up. Disappears if the ports share one script ~~or the skills merge~~ — the second half is refuted below, and the first is unreachable.
- `check-scope-walkup-sync`: ~~365~~ → **381** lines (plus **297** test lines; re-measured 2026-09-16 at HEAD) cross-checking four independent ports of the same package-root walk-up. Disappears if the ports share one script ~~or the skills merge~~ — the second half is refuted below, and the first is unreachable.
> **Grilled, held (2026-09-14):** both of the above are gated on findings 14/15 (merging skill-audit+agent-audit and skill-author+agent-author), deliberately held for a separate session rather than decided here. Correction for that session: the audit's §8 grouping is wrong — these merges don't need ADR-0012 revisited (that ADR governs the unrelated `core` plugin's three `agentsmd-*` skills). The actual constraint is ADR-0014 (no-cross-skill file sharing on plugin cache-install), and merging sidesteps it rather than requiring it be reversed. The open question for that session is a design one — a shared skill's `description` carrying both skill- and agent-audit trigger phrases — not an ADR supersession. ADR-0012 revisit is needed only for finding 24.
> **Grilled, held (2026-09-14):** both of the above are gated on findings 14/15 (merging skill-audit+agent-audit and skill-author+agent-author), deliberately held for a separate session rather than decided here. Correction for that session: the audit's §8 grouping is wrong — these merges don't need ADR-0012 revisited (that ADR governs the unrelated `core` plugin's three `agentsmd-*` skills). The actual constraint is ADR-0014 (no-cross-skill file sharing on plugin cache-install), and merging sidesteps it rather than requiring it be reversed. The open question for that session is a design one — a shared skill's `description` carrying both skill- and agent-audit trigger phrases — not an ADR supersession. ADR-0012 revisit is needed only for finding 24.
> **Settled (2026-09-15) — split verdict, and the first bullet held in full.** Finding 14 landed as `factory-audit` (ADR-0025). **`check-vale-style-sync` is deleted**, hook, script and test, exactly as the first bullet predicted — and its probes **were** rehomed into `test-vale-wrap.sh`, as cases 28-30 (case 31 carries the override allowlist), so both halves of that bullet are closed. `docs/spec/gates.md` records the rehoming, not an open gap. *(Updated later on 2026-09-15.)* The one assertion this note used to call still uncovered — cross-manifest *agreement* between `.pre-commit-hooks.yaml`'s and `.pre-commit-config.yaml`'s `files:` regexes — is now ported as case 33, which pairs the hooks by `id:`. Case 32 covers the separate zero-match question. It was a real gap while it lasted: narrowing the local skill hook to `^plugins/kyberforge/` left 6 of 38 skills prefiltered and the suite green. `bash tests/test-vale-wrap.sh` now reports ~~`61 passed, 0 failed`~~ → `63 passed, 0 failed` (it was 56 before cases 0 and 33 and the Part B mutation self-tests; 61 on 2026-09-15, and 63 once case 34 — the static `.vale.ini` style-load check — landed on 2026-09-16. Without vale on PATH it reports 19 and exits 77, up from 17). The bullet's "about 350 of its lines run Vale glob probes" overstates the probe half: at `a5962ba` the script is **413 lines**, of which the `.vale.ini` coverage section is **332** (`67..398`) and the machinery that actually invokes vale against a probe path is **204** (`195..398`). The balance of that section is `StylesPath`, `BasedOnStyles` and per-rule-override greps — text assertions, not probes. (Its test file is **797** lines, as the note above says, not the 798 the bullet carries.) **`check-scope-walkup-sync` stays**, and the second bullet's "or the skills merge" is wrong: two of its four walk-up ports are in the *author* skills (`new-agent.sh`, `new-skill.sh`), which this merge does not touch, and the audit-side pair is Python against the author-side pair's Bash, so the gate can never degrade into a text diff. Full reasoning in §10's 2026-09-15 note. Finding 15 would not remove it either.
> **Settled (2026-09-15) — split verdict, and the first bullet held in full.** Finding 14 landed as `factory-audit` (ADR-0025). **`check-vale-style-sync` is deleted**, hook, script and test, exactly as the first bullet predicted — and its probes **were** rehomed into `test-vale-wrap.sh`, as cases 28-30 (case 31 carries the override allowlist), so both halves of that bullet are closed. `docs/spec/gates.md` records the rehoming, not an open gap. *(Updated later on 2026-09-15.)* The one assertion this note used to call still uncovered — cross-manifest *agreement* between `.pre-commit-hooks.yaml`'s and `.pre-commit-config.yaml`'s `files:` regexes — ~~is now ported as case 33, which pairs the hooks by `id:`~~ → was ported as case 33, and case 33 was deleted with `.pre-commit-hooks.yaml` in `4de5b6b` (finding 36), so there is no second manifest left to agree with. Case 32 covers the separate zero-match question. It was a real gap while it lasted: narrowing the local skill hook to `^plugins/kyberforge/` left 6 of 38 skills prefiltered and the suite green. `bash tests/test-vale-wrap.sh` now reports ~~`61 passed, 0 failed`~~ → `63 passed, 0 failed` (it was 56 before cases 0 and 33 and the Part B mutation self-tests; 61 on 2026-09-15, and 63 once case 34 — the static `.vale.ini` style-load check — landed on 2026-09-16. Without vale on PATH it reports 19 and exits 77, up from 17). The bullet's "about 350 of its lines run Vale glob probes" overstates the probe half: at `a5962ba` the script is **413 lines**, of which the `.vale.ini` coverage section is **332** (`67..398`) and the machinery that actually invokes vale against a probe path is **204** (`195..398`). The balance of that section is `StylesPath`, `BasedOnStyles` and per-rule-override greps — text assertions, not probes. (Its test file is **797** lines, as the note above says, not the 798 the bullet carries.) **`check-scope-walkup-sync` stays**, and the second bullet's "or the skills merge" is wrong: two of its four walk-up ports are in the *author* skills (`new-agent.sh`, `new-skill.sh`), which this merge does not touch, and the audit-side pair is Python against the author-side pair's Bash, so the gate can never degrade into a text diff. Full reasoning in §10's 2026-09-15 note. Finding 15 would not remove it either.
- [x] ~~`check-marketplace-mirror-sync`: guards `.github/plugin/marketplace.json`. The script header calls it Copilot's legacy convention path and says Copilot also accepts the Claude path; the vendored Copilot docs list it as primary. Verify against current Copilot CLI before deleting hook, script, test, and mirror file.~~
- [x] ~~`check-marketplace-mirror-sync`: guards `.github/plugin/marketplace.json`. The script header calls it Copilot's legacy convention path and says Copilot also accepts the Claude path; the vendored Copilot docs list it as primary. Verify against current Copilot CLI before deleting hook, script, test, and mirror file.~~
> **Grilled and done (2026-09-14):** verified against GitHub's current Copilot CLI plugin docs (not the vendored copy, which risked drift). Copilot CLI's marketplace discovery checks paths in order — `marketplace.json`, `.plugin/marketplace.json`, `.github/plugin/marketplace.json`, `.claude-plugin/marketplace.json` — falling through to whichever exists first. `.claude-plugin/marketplace.json` (apm's own `claude` output) already satisfies that chain's last step, so the dedicated `.github/plugin/marketplace.json` mirror bought Copilot users its *preferred* discovery path rather than a required one. Decided against reopening ADR-0018 (native install for both Claude Code and Copilot CLI stays supported) to justify this — the deletion holds either way, since Copilot's own fallback covers it. Deleted `.github/plugin/marketplace.json`, `scripts/sync-marketplace-mirror.sh` (81 lines), `tests/test-sync-marketplace-mirror.sh` (304 lines), and the `check-marketplace-mirror-sync` pre-push hook; removed the dangling references to the deleted script in `scripts/sync-plugin-content.sh` and `tests/test-sync-plugin-content.sh` (both had comments citing its reasoning by name), and updated `docs/spec/architecture.md`'s description of the marketplace-manifest compile step. `tests/test-sync-plugin-content.sh` (92 cases) still passes in full.
> **Grilled and done (2026-09-14):** verified against GitHub's current Copilot CLI plugin docs (not the vendored copy, which risked drift). Copilot CLI's marketplace discovery checks paths in order — `marketplace.json`, `.plugin/marketplace.json`, `.github/plugin/marketplace.json`, `.claude-plugin/marketplace.json` — falling through to whichever exists first. `.claude-plugin/marketplace.json` (apm's own `claude` output) already satisfies that chain's last step, so the dedicated `.github/plugin/marketplace.json` mirror bought Copilot users its *preferred* discovery path rather than a required one. Decided against reopening ADR-0018 (native install for both Claude Code and Copilot CLI stays supported) to justify this — the deletion holds either way, since Copilot's own fallback covers it. Deleted `.github/plugin/marketplace.json`, `scripts/sync-marketplace-mirror.sh` (81 lines), `tests/test-sync-marketplace-mirror.sh` (304 lines), and the `check-marketplace-mirror-sync` pre-push hook; removed the dangling references to the deleted script in `scripts/sync-plugin-content.sh` and `tests/test-sync-plugin-content.sh` (both had comments citing its reasoning by name), and updated `docs/spec/architecture.md`'s description of the marketplace-manifest compile step. `tests/test-sync-plugin-content.sh` (92 cases) still passes in full.
>
>
@@ -149,7 +149,7 @@ This is the area you named as hardest to understand and slowest. Root cause: mos
8. [x] **`docs/spec/gates.md` (1,048 lines) is roughly 15% "what is enforced" and 85% post-mortems** of defects already fixed and pinned by tests. The 60-line hook table is the useful part. Target 200 lines. The same applies to the 106 comment lines in `.pre-commit-config.yaml` and to `scripts/`, where 8 of 15 files are 40 to 60% comments. Effort M.
8. [x] **`docs/spec/gates.md` (1,048 lines) is roughly 15% "what is enforced" and 85% post-mortems** of defects already fixed and pinned by tests. The 60-line hook table is the useful part. Target 200 lines. The same applies to the 106 comment lines in `.pre-commit-config.yaml` and to `scripts/`, where 8 of 15 files are 40 to 60% comments. Effort M.
> **Partially done (2026-09-13):** see commit `a35f5e8` on `docs/simplification-audit`. The 85%-post-mortem characterization was stale — the file had already shrunk to 966 lines by other findings, and most of what remained is load-bearing "why this design" rationale cited by ADRs and tests, not dead incident narration. Cut only the two genuinely stale passages: a reproduction paragraph carrying explicitly outdated numbers, and a retrofit-process narrative superseded by current state — a 36-line cut, 966 → 930 as measured at commit `a35f5e8`. Those two figures describe that commit only, not the file: `718c79a` and later findings have edited `gates.md` again, so read its current length from the file rather than quoting a number here. `.pre-commit-config.yaml`'s comments were left untouched; on inspection they're compact constraint notes, not filler. Target of 200 lines not reached and not recommended — would require deleting content the file itself flags as load-bearing.
> **Partially done (2026-09-13):** see commit `a35f5e8` on `docs/simplification-audit`. The 85%-post-mortem characterization was stale — the file had already shrunk to 966 lines by other findings, and most of what remained is load-bearing "why this design" rationale cited by ADRs and tests, not dead incident narration. Cut only the two genuinely stale passages: a reproduction paragraph carrying explicitly outdated numbers, and a retrofit-process narrative superseded by current state — a 36-line cut, 966 → 930 as measured at commit `a35f5e8`. Those two figures describe that commit only, not the file: `718c79a` and later findings have edited `gates.md` again, so read its current length from the file rather than quoting a number here. `.pre-commit-config.yaml`'s comments were left untouched; on inspection they're compact constraint notes, not filler. Target of 200 lines not reached and not recommended — would require deleting content the file itself flags as load-bearing.
>
>
> **Closed (2026-09-16, grill): done to the extent recommended.** The `gates.md` cut in `a35f5e8` stands; the 200-line target stays rejected (the file is ~~1,113 lines at HEAD~~ → **1,164** lines at HEAD (`b426460`), `wc -l docs/spec/gates.md`, grown by later findings' sections, and read on demand only). The tests target below is struck: ~~findings 3, 5 and 16 each found dense suites to be named-incident regression coverage~~ → findings 3 and 5 each found dense test suites to be named-incident regression coverage, and finding 16 found the same of dense validator code, whose comments are an incident log. Any future cut to a test suite is its own finding and starts by reading that suite's header.
> **Closed (2026-09-16, grill): done to the extent recommended.** The `gates.md` cut in `a35f5e8` stands; the 200-line target stays rejected (the file is ~~1,113 lines at HEAD~~ → ~~**1,164** lines at HEAD (`b426460`)~~ → **1,137** lines at `baa2f5d`, `wc -l docs/spec/gates.md`, grown by later findings' sections, and read on demand only). The tests target below is struck: ~~findings 3, 5 and 16 each found dense suites to be named-incident regression coverage~~ → findings 3 and 5 each found dense test suites to be named-incident regression coverage, and finding 16 found the same of dense validator code, whose comments are an incident log. Any future cut to a test suite is its own finding and starts by reading that suite's header.
@@ -204,7 +204,7 @@ The shared pattern: per-skill `README.md` files no model reads, a `docs/research
14. [x] ~~**Merge `skill-audit` + `agent-audit` into one `audit` skill (removes about 3,300 lines and two pre-push hooks).** `vale-wrap.sh` is byte-identical in both; five Vale rules byte-identical (agent-audit carries one extra, so it is the superset); `validate.sh` shares a 1,061-line boundary-target resolver block that diffs as zero lines; SKILL.md steps 1, 3, 4 and the gotchas are the same text. Each copy is hard-wired to one mode, so the merged script needs a path switch. The duplication exists because a plugin-cache install copies only each skill's own files (the rule ADR-0014 follows), so a script cannot be shared across skills; merging the skills is the only way to remove the copy. Effort M.~~
14. [x] ~~**Merge `skill-audit` + `agent-audit` into one `audit` skill (removes about 3,300 lines and two pre-push hooks).** `vale-wrap.sh` is byte-identical in both; five Vale rules byte-identical (agent-audit carries one extra, so it is the superset); `validate.sh` shares a 1,061-line boundary-target resolver block that diffs as zero lines; SKILL.md steps 1, 3, 4 and the gotchas are the same text. Each copy is hard-wired to one mode, so the merged script needs a path switch. The duplication exists because a plugin-cache install copies only each skill's own files (the rule ADR-0014 follows), so a script cannot be shared across skills; merging the skills is the only way to remove the copy. Effort M.~~
> **Done (2026-09-15), with three of its claims corrected.** Merged into **`factory-audit`**, not `audit` — the name states the domain (the artifact factory's own output) rather than the verb. See `docs/adr/0025-skill-audit-and-agent-audit-merge-into-factory-audit.md`. The repo goes from 39 skills to 38. Entry scripts are `scripts/validate.sh`, `scripts/validate-provenance.sh` and `scripts/vale-wrap.sh`. Only the first two auto-detect the artifact type they were handed and dispatch to a per-type library. `vale-wrap.sh` does not and never did: it is byte-identical to both pre-merge copies (`diff` clean against each at `a5962ba`) and names neither `SKILL.md` nor `.agent.md` anywhere in its 526 lines. Its scoping comes from outside it — the `.vale.ini` glob sections and the `files:` regexes of the two hooks that call it.
> **Done (2026-09-15), with three of its claims corrected.** Merged into **`factory-audit`**, not `audit` — the name states the domain (the artifact factory's own output) rather than the verb. See `docs/adr/0025-skill-audit-and-agent-audit-merge-into-factory-audit.md`. The repo goes from 39 skills to 38. Entry scripts are `scripts/validate.sh`, `scripts/validate-provenance.sh` and `scripts/vale-wrap.sh`. Only the first two auto-detect the artifact type they were handed and dispatch to a per-type library. `vale-wrap.sh` does not and never did: it is byte-identical to both pre-merge copies (`diff` clean against each at `a5962ba`) and names neither `SKILL.md` nor `.agent.md` anywhere in its 526 lines. Its scoping comes from outside it — the `.vale.ini` glob sections and the `files:` regexes of the two hooks that call it.
>
>
> - **Yield: 2,934 lines and ONE pre-push hook, not ~3,300 and two.** The hook is `check-vale-style-sync`, deleted with its script (413 lines) and `tests/test-check-vale-style-sync.sh` (797). **They did *not* exist only to diff the two now-merged Vale copies** — an earlier revision of this bullet said so and it was wrong, as this document's own ":104" measurement already implied. The script has **17** assertion sites (13 `err` calls and 4 hard-fail exits; ADR-0025 maps each one). Only **6** are genuinely moot: two diffed the copies and four guarded the script's ability to locate them. **10** were rehomed into `tests/test-vale-wrap.sh`: case 0 (config loads), cases 28–30 (glob probes, style loading, Copilot scoping), case 31 (override allowlist) and the suite-level exit 77. **1**, the cross-manifest `files:` drift check, is ported as case 33, pairing hooks by `id:` since both now share one `entry:`. *(Corrected later on 2026-09-15.)* An earlier revision of this bullet said 18 / 6 / 11 / 1. It called the cross-manifest check knowingly dropped and "seven of them stronger". None of that survives a recount. Two text greps became behavioural Vale probes, not seven, and case 32 alone never covered the narrowing that case 33 now catches. `check-scope-walkup-sync`**survives**; see the §10 correction below for why. Pre-push goes 9 repo-authored hooks to 8 (11 reported to 10). The rest of the saving is the second embedded resolver (1,061), the second `vale-wrap.sh` (526), the second `assets/vale/styles/Kyberforge/` copy (44), and the Contributing-files parser embedded in both `validate-provenance.sh` copies (93). **413 + 797 + 1,061 + 526 + 44 + 93 = 2,934**, which is the headline. An earlier revision of this bullet listed 413 + 797 + 526 + 48 + 1,061 = 2,845: it dropped the 93-line parser outright, and its **48** for the Vale copy is the five byte-identical style rules (13 + 7 + 7 + 7 + 10 = **44**) plus skill-audit's 4-line `.vale.ini`. ADR-0025 counts **44** on purpose — the two `.vale.ini` files were deliberately *not* identical (agent-audit's carried the extra `[**/*.agent.md]` section and the `KyberforgeCopilot` style), so that file is a deleted file rather than a removed duplicate, and folding it in would make the headline 2,938. All six figures measured at `a5962ba`.
> - **Yield: 2,934 lines and ONE pre-push hook, not ~3,300 and two.** The hook is `check-vale-style-sync`, deleted with its script (413 lines) and `tests/test-check-vale-style-sync.sh` (797). **They did *not* exist only to diff the two now-merged Vale copies** — an earlier revision of this bullet said so and it was wrong, as this document's own ":104" measurement already implied. The script has **17** assertion sites (13 `err` calls and 4 hard-fail exits; ADR-0025 maps each one). Only **6** are genuinely moot: two diffed the copies and four guarded the script's ability to locate them. **10** were rehomed into `tests/test-vale-wrap.sh`: case 0 (config loads), cases 28–30 (glob probes, style loading, Copilot scoping), case 31 (override allowlist) and the suite-level exit 77. **1**, the cross-manifest `files:` drift check, ~~is ported as case 33, pairing hooks by `id:` since both now share one `entry:`~~ → was ported as case 33 and later deleted with its second manifest in `4de5b6b` (finding 36). *(Corrected later on 2026-09-15.)* An earlier revision of this bullet said 18 / 6 / 11 / 1. It called the cross-manifest check knowingly dropped and "seven of them stronger". None of that survives a recount. Two text greps became behavioural Vale probes, not seven, and case 32 alone never covered the narrowing that case 33 now catches. `check-scope-walkup-sync`**survives**; see the §10 correction below for why. Pre-push goes 9 repo-authored hooks to 8 (11 reported to 10). The rest of the saving is the second embedded resolver (1,061), the second `vale-wrap.sh` (526), the second `assets/vale/styles/Kyberforge/` copy (44), and the Contributing-files parser embedded in both `validate-provenance.sh` copies (93). **413 + 797 + 1,061 + 526 + 44 + 93 = 2,934**, which is the headline. An earlier revision of this bullet listed 413 + 797 + 526 + 48 + 1,061 = 2,845: it dropped the 93-line parser outright, and its **48** for the Vale copy is the five byte-identical style rules (13 + 7 + 7 + 7 + 10 = **44**) plus skill-audit's 4-line `.vale.ini`. ADR-0025 counts **44** on purpose — the two `.vale.ini` files were deliberately *not* identical (agent-audit's carried the extra `[**/*.agent.md]` section and the `KyberforgeCopilot` style), so that file is a deleted file rather than a removed duplicate, and folding it in would make the headline 2,938. All six figures measured at `a5962ba`.
> - **"Each copy is hard-wired to one mode" was false**, and it is the claim that made this look like a bigger win than it is. The two `validate.sh` files are not one script parameterised per mode: outside the shared 1,061-line resolver they hold **1,293 lines between them** (616 skill-side, 677 agent-side) and share **91** of those. That 91 is ADR-0025's figure and it is exactly reproducible: strip the marked resolver block from each copy at `a5962ba` (`115..1175` skill-side, `189..1249` agent-side, 1,061 lines each), then take the size of the intersection of the two *distinct raw line sets* — 510 distinct lines skill-side, 530 agent-side, 91 in common. An earlier revision of this bullet said "about 115", which matches no counting rule that has been reproduced: dropping blank lines gives 90 and dropping comments as well gives 64. The merged validator dispatches on artifact type over two largely independent bodies of checks; it does not collapse them.
> - **"Each copy is hard-wired to one mode" was false**, and it is the claim that made this look like a bigger win than it is. The two `validate.sh` files are not one script parameterised per mode: outside the shared 1,061-line resolver they hold **1,293 lines between them** (616 skill-side, 677 agent-side) and share **91** of those. That 91 is ADR-0025's figure and it is exactly reproducible: strip the marked resolver block from each copy at `a5962ba` (`115..1175` skill-side, `189..1249` agent-side, 1,061 lines each), then take the size of the intersection of the two *distinct raw line sets* — 510 distinct lines skill-side, 530 agent-side, 91 in common. An earlier revision of this bullet said "about 115", which matches no counting rule that has been reproduced: dropping blank lines gives 90 and dropping comments as well gives 64. The merged validator dispatches on artifact type over two largely independent bodies of checks; it does not collapse them.
> - **The §8 blocker was a non-issue.** The design question held open there — whether one `description` could carry both skills' trigger sets without breaching the ADR-0020 ceiling — was answered against the **400-character FAIL**, which the merged description clears. Read the number from the shipped file, not from a draft. *(Corrected later on 2026-09-15.)* The shipped description is **241 characters**, inside the 250-character SUGGESTION target, and `bash scripts/skill-size-check.sh plugins/kyberforge/.apm/skills/factory-audit/SKILL.md` prints nothing for it. A first cut shipped at **319** and accepted the SUGGESTION as the cost of carrying both artifact types' trigger phrases. That reasoning was wrong. The quoted phrases (`audit this skill`, `review my SKILL.md`, `audit this agent`, `review my agent file`) restated the "skill directory or agent definition audited" trigger in a second register, which ADR-0020 makes a FAIL. Removing them, and keeping both boundary arrows, gives 241. An earlier "241" in this document and ADR-0025's "240" came from a hypothetical single-arrow draft that was never reproduced. That today's figure is also 241 is a coincidence, not a confirmation of it. The real ceiling was the other one: a single body covering both artifact types ran past the **900-word body FAIL**. Solved the way ADR-0020 prescribes — a dispatch body that routes to per-type references, with the 16 per-type reference files namespaced `skill-*` and `agent-*` (plus the shared `sources.md`).
> - **The §8 blocker was a non-issue.** The design question held open there — whether one `description` could carry both skills' trigger sets without breaching the ADR-0020 ceiling — was answered against the **400-character FAIL**, which the merged description clears. Read the number from the shipped file, not from a draft. *(Corrected later on 2026-09-15.)* The shipped description is **241 characters**, inside the 250-character SUGGESTION target, and `bash scripts/skill-size-check.sh plugins/kyberforge/.apm/skills/factory-audit/SKILL.md` prints nothing for it. A first cut shipped at **319** and accepted the SUGGESTION as the cost of carrying both artifact types' trigger phrases. That reasoning was wrong. The quoted phrases (`audit this skill`, `review my SKILL.md`, `audit this agent`, `review my agent file`) restated the "skill directory or agent definition audited" trigger in a second register, which ADR-0020 makes a FAIL. Removing them, and keeping both boundary arrows, gives 241. An earlier "241" in this document and ADR-0025's "240" came from a hypothetical single-arrow draft that was never reproduced. That today's figure is also 241 is a coincidence, not a confirmation of it. The real ceiling was the other one: a single body covering both artifact types ran past the **900-word body FAIL**. Solved the way ADR-0020 prescribes — a dispatch body that routes to per-type references, with the 16 per-type reference files namespaced `skill-*` and `agent-*` (plus the shared `sources.md`).
>
>
@@ -319,7 +319,7 @@ The shared pattern: per-skill `README.md` files no model reads, a `docs/research
>
>
> **The merge is arithmetically impossible as specified.** Body word counts: `agentsmd-author` 485 + `agentsmd-audit` 361 + `provider-adapter-author` 514 = **1,360 words against `BODY_MAX_WORDS=900`** (ADR-0020 hard FAIL). Descriptions: 251 + 275 + 280 = **806 chars into a field capped at 400**. Relocating the overflow into `references/` is PR #129's named anti-goal, and issue #117 records that `references/` is where neither the size gate nor Vale looks.
> **The merge is arithmetically impossible as specified.** Body word counts: `agentsmd-author` 485 + `agentsmd-audit` 361 + `provider-adapter-author` 514 = **1,360 words against `BODY_MAX_WORDS=900`** (ADR-0020 hard FAIL). Descriptions: 251 + 275 + 280 = **806 chars into a field capped at 400**. Relocating the overflow into `references/` is PR #129's named anti-goal, and issue #117 records that `references/` is where neither the size gate nor Vale looks.
>
>
> **Both factual anchors describe a validator that no longer exists.**`scripts/validate-adapter.sh` was **141 lines at birth** (`6fd6876`) and in that form *was* approximately a grep — which is why it shipped two recorded defects: `a8cd5e8` (a UTF-8 BOM hid the import line, so a `CLAUDE.md` whose first line was `@AGENTS.md` failed with "no reference to AGENTS.md" and was told to add the line already in front of it) and issue **#115** (`c59e4bf`: the `--no-import-syntax` flag was a proven no-op — "both branches reduce to the same expression"). 141 → 496 is the fix for those. **"Validator about 40 lines" targets below the version whose defects are on the record.** Line counts otherwise exact: validator 496, bats 519 — but "1,200-line wrapper" is 1,164, of which the *wrapper* is 52; **1,015 are validator + tests** (1,071 with the two READMEs, 28 each), and the remaining 41 are `references/`.
> **Both factual anchors describe a validator that no longer exists.**`scripts/validate-adapter.sh` was **141 lines at birth** (`6fd6876`) and in that form *was* approximately a grep — which is why it shipped two recorded defects: `a8cd5e8` (reachable from no branch: its change reached `main` squashed into `598a7c3`, #129; a UTF-8 BOM hid the import line, so a `CLAUDE.md` whose first line was `@AGENTS.md` failed with "no reference to AGENTS.md" and was told to add the line already in front of it) and issue **#115** (`c59e4bf`, which is only on `rescued/parse-bullet-contributing-files`; its change reached `main` in the same `598a7c3` squash: the `--no-import-syntax` flag was a proven no-op — "both branches reduce to the same expression"). 141 → 496 is the fix for those. **"Validator about 40 lines" targets below the version whose defects are on the record.** Line counts otherwise exact: validator 496, bats 519 — but "1,200-line wrapper" is 1,164, of which the *wrapper* is 52; **1,015 are validator + tests** (1,071 with the two READMEs, 28 each), and the remaining 41 are `references/`.
>
>
> Coverage given up by a 40-line validator: **~67% of the 42-test suite**. **20** tests sit under explicit `Q1`–`Q5` headers — Q1 inert fenced/indented/HTML-comment regions (5), Q2 valid-UTF-8-but-not-UTF-8 encodings (4), Q3 exists-but-unreadable (1), Q4 path resolution (4), Q5 pointer-vs-mention (6); 8 more are hardening, so 28 of 42. Every one was proven non-vacuous by deliberate mutation under PR #129. Representative guards: a ```-fenced `@AGENTS.md` "exited 0"; `@NOTAGENTS.md` counted as an import for want of a path-segment boundary; `"AGENTS.md" in ln` passed *"Do NOT read AGENTS.md; it is obsolete."*; BOM-less UTF-16LE decodes as valid UTF-8 and produced a false diagnosis; exit 2/3 split from 1 "because the skill's closeout tells the agent to fix every non-zero exit by editing the provider file, which for a mistyped flag edits the wrong file forever".
> Coverage given up by a 40-line validator: **~67% of the 42-test suite**. **20** tests sit under explicit `Q1`–`Q5` headers — Q1 inert fenced/indented/HTML-comment regions (5), Q2 valid-UTF-8-but-not-UTF-8 encodings (4), Q3 exists-but-unreadable (1), Q4 path resolution (4), Q5 pointer-vs-mention (6); 8 more are hardening, so 28 of 42. Every one was proven non-vacuous by deliberate mutation under PR #129. Representative guards: a ```-fenced `@AGENTS.md` "exited 0"; `@NOTAGENTS.md` counted as an import for want of a path-segment boundary; `"AGENTS.md" in ln` passed *"Do NOT read AGENTS.md; it is obsolete."*; BOM-less UTF-16LE decodes as valid UTF-8 and produced a false diagnosis; exit 2/3 split from 1 "because the skill's closeout tells the agent to fix every non-zero exit by editing the provider file, which for a mistyped flag edits the wrong file forever".
>
>
@@ -381,7 +381,7 @@ The shared pattern: per-skill `README.md` files no model reads, a `docs/research
>
>
> **The framing question this finding never notices:** it proposes reversing a convention the repo *just* re-affirmed — every banner added by the ADR-0024 work ends with some form of "kept below as the historical record". Is a superseded ADR's body a record or dead weight? Nothing here is mechanical; every proposed cut touches text another file quotes.
> **The framing question this finding never notices:** it proposes reversing a convention the repo *just* re-affirmed — every banner added by the ADR-0024 work ends with some form of "kept below as the historical record". Is a superseded ADR's body a record or dead weight? Nothing here is mechanical; every proposed cut touches text another file quotes.
>
>
> **Closed (2026-09-16, grill): not proceeding.** Decision: a superseded or accepted ADR's text is the historical record (the Nygard convention, and what every ADR-0024 banner already says), so no body is truncated, no amendment is deleted, and ADR-0020's Context is left intact. That removes every remaining cut — ADR-0017's amendments are part of its record, and ADR-0024 consequence 7 (`0024:213-252`, ~40 lines) summarises them rather than restating them in full as the note above says. Corrected headline for anyone quoting it: ~~**79% of `docs/adr/` lines sit in ten ADRs over 150 lines** (measured 2026-09-14)~~ → **80% of `docs/adr/` lines (2,983 of 3,709, across 25 files) sit in eleven ADRs over 150 lines** (at HEAD `b426460`, 2026-09-16, from `wc -l docs/adr/*.md`; ADR-0025 joined the cohort and the ADR-0019 and ADR-0022 amendments grew the directory). The 79%-in-ten figure was the 2026-09-14 state; the 2,740 figure is the `a3e721e` state only.
> **Closed (2026-09-16, grill): not proceeding.** Decision: a superseded or accepted ADR's text is the historical record (the Nygard convention, and what every ADR-0024 banner already says), so no body is truncated, no amendment is deleted, and ADR-0020's Context is left intact. That removes every remaining cut — ADR-0017's amendments are part of its record, and ADR-0024 consequence 7 (`0024:213-252`, ~40 lines) summarises them rather than restating them in full as the note above says. Corrected headline for anyone quoting it: ~~**79% of `docs/adr/` lines sit in ten ADRs over 150 lines** (measured 2026-09-14)~~ → ~~**80% of `docs/adr/` lines (2,983 of 3,709, across 25 files) sit in eleven ADRs over 150 lines** (at HEAD `b426460`)~~ → **85% of `docs/adr/` lines (3,295 of 3,896, across 25 files) sit in twelve ADRs over 150 lines** (at `baa2f5d`, 2026-09-16, from `wc -l docs/adr/*.md`; ADR-0025 joined the cohort and the ADR-0019 and ADR-0022 amendments grew the directory). The 79%-in-ten figure was the 2026-09-14 state; the 2,740 figure is the `a3e721e` state only.
29. [x] ~~**The same facts are stated in full three or four times.** "Edit `.apm/`, never the mirror": README (2 paragraphs), AGENTS.md (2 paragraphs), architecture.md (2 paragraphs plus the lost-README anecdote), ADR-0017. The apm.lock / SessionStart story: README (11 lines), AGENTS.md, ADR-0018, ADR-0019, gates.md. The offline `SKIP=` command and the three-stage install each appear three times. Rule: README has the how-to, AGENTS.md has one-line rules with links, architecture.md has mechanics. Effort S.~~
29. [x] ~~**The same facts are stated in full three or four times.** "Edit `.apm/`, never the mirror": README (2 paragraphs), AGENTS.md (2 paragraphs), architecture.md (2 paragraphs plus the lost-README anecdote), ADR-0017. The apm.lock / SessionStart story: README (11 lines), AGENTS.md, ADR-0018, ADR-0019, gates.md. The offline `SKIP=` command and the three-stage install each appear three times. Rule: README has the how-to, AGENTS.md has one-line rules with links, architecture.md has mechanics. Effort S.~~
> **Corrected then partially done (2026-09-14):** independent re-verification found the "edit `.apm/`, never the mirror" and apm.lock/SessionStart clusters confirmed but the third overstated — no file documents an offline `SKIP=` command (the one `SKIP=`-adjacent mention in `gates.md` explicitly says a *different* opt-out "is not `SKIP=`"), and "three-stage install" appears twice, not three times, with no restatement worth trimming. Trimmed the two confirmed clusters: README's "Editing plugin content" and AGENTS.md's "Edit `.apm/`, never the flat mirror" sections cut to the how-to/one-line-plus-link split the finding itself proposed, full mechanics (the `rm -rf` behavior and the `plugins/kyberforge/hooks/README.md` anecdote) staying solely in `docs/spec/architecture.md`. README's "Keeping the install current" and AGENTS.md's apm.lock bullet trimmed to drop the restated `apm outdated`/`apm update --yes` timing narrative, pointing to ADR-0019 as the canonical mechanism instead. No test greps the trimmed wording (checked).
> **Corrected then partially done (2026-09-14):** independent re-verification found the "edit `.apm/`, never the mirror" and apm.lock/SessionStart clusters confirmed but the third overstated — no file documents an offline `SKIP=` command (the one `SKIP=`-adjacent mention in `gates.md` explicitly says a *different* opt-out "is not `SKIP=`"), and "three-stage install" appears twice, not three times, with no restatement worth trimming. Trimmed the two confirmed clusters: README's "Editing plugin content" and AGENTS.md's "Edit `.apm/`, never the flat mirror" sections cut to the how-to/one-line-plus-link split the finding itself proposed, full mechanics (the `rm -rf` behavior and the `plugins/kyberforge/hooks/README.md` anecdote) staying solely in `docs/spec/architecture.md`. README's "Keeping the install current" and AGENTS.md's apm.lock bullet trimmed to drop the restated `apm outdated`/`apm update --yes` timing narrative, pointing to ADR-0019 as the canonical mechanism instead. No test greps the trimmed wording (checked).
@@ -496,7 +496,7 @@ Not covered by the area audits above; found on a final sweep of the root config
>
>
> **Complete (2026-09-16).** The follow-up read after 15 closed turned up four loose ends, all now settled: finding 6's `check-apm-agents-valid` fold (not proceeding, see its note); finding 27's dangling always-on pointer (constitution moved to `core/` and deployed, `adaa978`); finding 5's differential-suite speed-up (not proceeding, see its note); and finding 16's resolver-sourcing option (done, `ef27c97`). Nothing in this audit is open. **22** is out of scope with `bin` and, by the human's decision, is not tracked anywhere.
> **Complete (2026-09-16).** The follow-up read after 15 closed turned up four loose ends, all now settled: finding 6's `check-apm-agents-valid` fold (not proceeding, see its note); finding 27's dangling always-on pointer (constitution moved to `core/` and deployed, `adaa978`); finding 5's differential-suite speed-up (not proceeding, see its note); and finding 16's resolver-sourcing option (done, `ef27c97`). Nothing in this audit is open. **22** is out of scope with `bin` and, by the human's decision, is not tracked anywhere.
> **Re-derived (2026-09-16, at HEAD):** this ordering was written before the findings were worked, and ~~seven of its entries are now closed~~ → ~~all but two~~ → ~~all but one~~ → all of its bucket-2 and bucket-3 entries are now closed (corrected later on 2026-09-16, after the grill, and again once **36** closed and once **15** was refuted). Struck above: **14** landed (`467bbd7`, ADR-0025); **7** was superseded then done (`718c79a`); **3**, **5** and **19** are not proceeding on refuted premises; ~~**34**,~~**16** and **24** are refuted outright; **34** was refuted as a removal and then decided and done as documentation of the branch hazard in ADR-0019 (`afcf477`), with the hook kept; **33** was decided and done (enforce the bump, `8451169`); **8**, **20** and **28** closed at the grill; **17**, **23** and **25** were declined by the human; **11** was declined by the human; **36** was done (`4de5b6b`). ~~**5** is left standing but is downstream of 16 by its own note, so it cannot be taken in this bucket's order.~~**5** is closed with 16: its own note says it is downstream of 16, and 16 is refuted. ~~Still open, per the Status note above: **15** alone, now that **36** is done (`4de5b6b`);~~**15** was refuted on measurement after its own grill, so none is still open; **22** is deferred with `bin`. Read each finding's own marker, not this list — it is a plan of record, not a status board. Bucket 1 is left as written: every entry in it is marked `[x]` or carries a decision note at its own finding. *(Updated 2026-09-16:)*`[x]` now marks every closed finding, whatever the outcome — done, refuted, declined or not proceeding; read the note for which. The only finding without one is **22**, deferred with `bin`.
> **Re-derived (2026-09-16, at HEAD):** this ordering was written before the findings were worked, and ~~seven of its entries are now closed~~ → ~~all but two~~ → ~~all but one~~ → all of its bucket-2 and bucket-3 entries are now closed (corrected later on 2026-09-16, after the grill, and again once **36** closed and once **15** was refuted). Struck above: **14** landed (~~`467bbd7`~~ → `620f20b`, ADR-0025); **7** was superseded then done (`718c79a`); **3**, **5** and **19** are not proceeding on refuted premises; ~~**34**,~~**16** and **24** are refuted outright; **34** was refuted as a removal and then decided and done as documentation of the branch hazard in ADR-0019 (`afcf477`), with the hook kept; **33** was decided and done (enforce the bump, `8451169`); **8**, **20** and **28** closed at the grill; **17**, **23** and **25** were declined by the human; **11** was declined by the human; **36** was done (`4de5b6b`). ~~**5** is left standing but is downstream of 16 by its own note, so it cannot be taken in this bucket's order.~~**5** is closed with 16: its own note says it is downstream of 16, and 16 is refuted. ~~Still open, per the Status note above: **15** alone, now that **36** is done (`4de5b6b`);~~**15** was refuted on measurement after its own grill, so none is still open; **22** is deferred with `bin`. Read each finding's own marker, not this list — it is a plan of record, not a status board. Bucket 1 is left as written: every entry in it is marked `[x]` or carries a decision note at its own finding. *(Updated 2026-09-16:)*`[x]` now marks every closed finding, whatever the outcome — done, refuted, declined or not proceeding; read the note for which. The only finding without one is **22**, deferred with `bin`.
Findings 9, 10, 11, and 12 are coupled through the provenance validator and the audit criteria; land them together or the audit gates start reporting the removals.
Findings 9, 10, 11, and 12 are coupled through the provenance validator and the audit criteria; land them together or the audit gates start reporting the removals.
@@ -517,7 +517,7 @@ Findings 9, 10, 11, and 12 are coupled through the provenance validator and the
>
>
> **Answered (2026-09-16):** keep it. The human declined finding 11; the chain and its validators stay, and `research` keeps producing it.
> **Answered (2026-09-16):** keep it. The human declined finding 11; the chain and its validators stay, and `research` keeps producing it.
- [x] ~~**ADR-0012 (three core skills) and the one-script-per-skill install constraint.**~~~~The merges in 14, 15, and 24 need the first revisited and are the only way around the second.~~**Corrected (2026-09-14):** this grouping was wrong, and finding 2b's note has said so since `0dffff3` while this bullet said the opposite. ADR-0012 governs only the `core` plugin's three `agentsmd-*` skills (`agentsmd-author`, `agentsmd-audit`, `provider-adapter-author`) — read it: it names those three and nothing else. **Only finding 24 touches them, so only finding 24 needs ADR-0012 revisited.** Findings 14 and 15 merge kyberforge's `skill-audit`/`agent-audit` and `skill-author`/`agent-author`, which ADR-0012 does not govern; what constrains them is the self-containment rule, and merging is the way *around* it rather than a reason to reverse anything. That rule survives ADR-0024 — see §9's negative result and ADR-0024 consequence 6, which also correct its source: it is the agentskills.io spec for APM package mode, not a property of Claude Code's plugin cache-install as finding 2b's note assumed. The open question for 14/15 is a design one — one `description` carrying both skills' trigger phrases — not an ADR supersession. ~~Are you open to superseding ADR-0012, for finding 24?~~
- [x] ~~**ADR-0012 (three core skills) and the one-script-per-skill install constraint.**~~~~The merges in 14, 15, and 24 need the first revisited and are the only way around the second.~~**Corrected (2026-09-14):** this grouping was wrong, and finding 2b's note has said so since `0dffff3` while this bullet said the opposite. ADR-0012 governs only the `core` plugin's three `agentsmd-*` skills (`agentsmd-author`, `agentsmd-audit`, `provider-adapter-author`) — read it: it names those three and nothing else. **Only finding 24 touches them, so only finding 24 needs ADR-0012 revisited.** Findings 14 and 15 merge kyberforge's `skill-audit`/`agent-audit` and `skill-author`/`agent-author`, which ADR-0012 does not govern; what constrains them is the self-containment rule, and merging is the way *around* it rather than a reason to reverse anything. That rule survives ADR-0024 — see §9's negative result and ADR-0024 consequence 6, which also correct its source: it is the agentskills.io spec for APM package mode, not a property of Claude Code's plugin cache-install as finding 2b's note assumed. The open question for 14/15 is a design one — one `description` carrying both skills' trigger phrases — not an ADR supersession. ~~Are you open to superseding ADR-0012, for finding 24?~~
> **Closed on the 14/15 half (2026-09-16, at HEAD):** finding 14 landed as `factory-audit` on 2026-09-15 (`467bbd7`, ADR-0025), and the design question this bullet holds open was answered by doing it — the merged description ships at 241 characters, inside the 250 SUGGESTION target, and the binding ceiling turned out to be the 900-word **body**, solved with a dispatch body over `skill-*`/`agent-*` reference files. See finding 14's own note. What remains open here is finding 15 (`skill-author` + `agent-author`) and the ADR-0012 question below, which the next note already answers.
> **Closed on the 14/15 half (2026-09-16, at HEAD):** finding 14 landed as `factory-audit` on 2026-09-15 (~~`467bbd7`~~ → `620f20b`, ADR-0025), and the design question this bullet holds open was answered by doing it — the merged description ships at 241 characters, inside the 250 SUGGESTION target, and the binding ceiling turned out to be the 900-word **body**, solved with a dispatch body over `skill-*`/`agent-*` reference files. See finding 14's own note. What remains open here is finding 15 (`skill-author` + `agent-author`) and the ADR-0012 question below, which the next note already answers.
> **Moot (2026-09-14):** finding 24 is refuted on arithmetic before this question is reached — the three `core` bodies total 1,360 words against `BODY_MAX_WORDS=900`, and their descriptions 806 chars against a 400 cap. Nothing needs superseding because the merge it would unblock cannot be committed. Question closed unless finding 24 is rewritten.
> **Moot (2026-09-14):** finding 24 is refuted on arithmetic before this question is reached — the three `core` bodies total 1,360 words against `BODY_MAX_WORDS=900`, and their descriptions 806 chars against a 400 cap. Nothing needs superseding because the merge it would unblock cannot be committed. Question closed unless finding 24 is rewritten.
>
>
> **Closed (2026-09-16):** both halves are settled — finding 14 landed and finding 24 is refuted. The only finding left under this bullet is 15, which needs no ADR-0012 revisit (see above); its remaining question is the design one this bullet already names.
> **Closed (2026-09-16):** both halves are settled — finding 14 landed and finding 24 is refuted. The only finding left under this bullet is 15, which needs no ADR-0012 revisit (see above); its remaining question is the design one this bullet already names.
@@ -619,3 +619,39 @@ A review of this branch's grill commits (`8451169` through `b426460`) raised the
- **Remote-entry `version:` guidance.** The `apm-workflow` references now give the right guidance on a remote marketplace entry's `version:`.
- **Remote-entry `version:` guidance.** The `apm-workflow` references now give the right guidance on a remote marketplace entry's `version:`.
- **Lock-file discard reasoning.**`README.md`, `AGENTS.md` and ADR-0019's 2026-09-16 amendment used to say to discard the refreshed lock on a feature branch "because it records `main`'s commit, not the branch's". That was wrong: the branch's committed lock records a `main` commit too, just an older one. In this checkout it is `b7bec71`, which `git branch -r --contains` finds on `origin/main`. All three now give the real reasons. Discarding keeps unrelated lock churn out of the branch diff, and it keeps the deployed tree consistent with the lock that `apm pack --check-clean` reads. They also state the cost: the session runs the older `main` until the next session start refreshes again. The SessionStart notice in `check-apm-current.sh` now gives branch-specific advice, and `tests/test-apm-current-hook.sh` pins it. ADR-0019 had two claims that were checked against apm's source. `apm pack` "refuses to run": precisely, it raises a build error before the `--check-clean` gate is reached, and only when a file the lock lists is missing on disk (`bundle/packer.py`, `pack_bundle`). apm "removes a server on its next update": this holds, and it holds for `apm install` as well (`install/mcp/integration.py`, `MCPIntegrator.remove_stale`). The amendment now says both precisely.
- **Lock-file discard reasoning.**`README.md`, `AGENTS.md` and ADR-0019's 2026-09-16 amendment used to say to discard the refreshed lock on a feature branch "because it records `main`'s commit, not the branch's". That was wrong: the branch's committed lock records a `main` commit too, just an older one. In this checkout it is `b7bec71`, which `git branch -r --contains` finds on `origin/main`. All three now give the real reasons. Discarding keeps unrelated lock churn out of the branch diff, and it keeps the deployed tree consistent with the lock that `apm pack --check-clean` reads. They also state the cost: the session runs the older `main` until the next session start refreshes again. The SessionStart notice in `check-apm-current.sh` now gives branch-specific advice, and `tests/test-apm-current-hook.sh` pins it. ADR-0019 had two claims that were checked against apm's source. `apm pack` "refuses to run": precisely, it raises a build error before the `--check-clean` gate is reached, and only when a file the lock lists is missing on disk (`bundle/packer.py`, `pack_bundle`). apm "removes a server on its next update": this holds, and it holds for `apm install` as well (`install/mcp/integration.py`, `MCPIntegrator.remove_stale`). The amendment now says both precisely.
- **ADR-0022 amendment.** The amendment's placement and the validator names it cites are fixed.
- **ADR-0022 amendment.** The amendment's placement and the validator names it cites are fixed.
## 12. Final review round on the whole branch (2026-09-16)
Seven parallel reviewers went over the whole branch against `main`, each covering one area: the gate scripts, a full test and hook run, references to removed files, kyberforge, the other five plugins, the docs and ADRs, and this document. The full suite and every hook passed at `55221d0`. Nothing still pointed at a removed file, and no finding marked done was missing. The fixes below landed after that review.
**Fixed:**
- **Duplicated `SessionStart` entry on a fresh install** (`3a9d257`). apm recognises its own `settings.json` entries only through the `.claude/apm-hooks.json` sidecar. With the sidecar gitignored, a fresh clone's `apm install` added a second copy of the entry and `apm audit --ci` reported drift. This was reproduced on `main` too. The sidecar is now committed and excluded from `pretty-format-json`, and ADR-0019 carries a correction.
- **Version-bump gate frontmatter shape** (`614a0d5`). `read_version` now accepts the leading whitespace that `skill-size-check` already accepts, and case 39 pins it. The hook entry now describes the main-tip check.
- **README advice in the size checks** (`8ce5392`). `skill-size-check` and factory-audit's validator no longer tell authors to move detail to a README, since skills no longer have one.
- **Test runners inside a Claude worktree** (`7380bed`). The runners' worktree exclusion is now relative to the search root, so they also work when the repo itself is a Claude worktree.
- **Catalog version and Copilot wording** (`2574391`). The catalog is bumped `0.4.7` → `0.5.0`: removing an entry is a minor change under apm-workflow's marketplace policy. The Copilot wording now says Copilot is reached through apm.
- **Stale doc claims** (`1f3d4f9`):
- ADR-0014 carries a correction: `skill-size-check` sources the resolver since `ef27c97`.
- ADR-0017's status line now matches its supersession.
- `architecture.md` and `gates.md` carry the current duplication counts.
- "Vacuous green" is defined where `gates.md` uses it.
- The gitleaks lesson is marked historical.
- **Bare-`git` rule in `git-commits`** (`0323c29`). The general rule is restored.
- **Retrofit cut order in `skill-author`** (`baa2f5d`). The ordered cuts are restored inline, and the stale hook name and plugin-mode wording are fixed.
- **Stale hashes and figures in this document** (this commit). Hashes left by the branch rewrite are corrected in place. `a8cd5e8` and `c59e4bf` are annotated as unreachable, with their content in `598a7c3`. The case 33 lines are struck. The §1 table, the `gates.md` length and the ADR share are re-measured at `baa2f5d`. The document moves to `docs/notes/`.
**Declined:**
- **Removing the remaining `(ADR-0023)` tags.** They are the opt-out markers `check-rtk-prefix` requires on deliberately bare git commands, so removing them would fail that hook.
- **Fixing the constitution path in `plugins/kyberforge/docs/research/examples/skill-write/`.** That directory is a frozen research snapshot of a retired skill.
- **Restoring factory-audit's dropped trigger phrases.** Removing them was deliberate under ADR-0020's duplicate-wording rule.
- **Checking every ref in a multi-ref push.** The version-bump gate checks only one ref per push. That is pre-commit's behaviour and is documented; closing the gap needs a server-side CI check, not a hook change.
**Open follow-ups:**
- **Gitea #101.** Close it through this branch's PR with `Closes #101`. A comment is posted.
- **Gitea #66.** It needs re-scoping, because its `.mcp.json` target is gone. A comment is posted.
- **The dropped `LESSONS.md` entry.** The entry saying that "read at session start" is only a hope was removed, and no issue tracks it.
- **The ADR-0020 constants.** They could move into the shared library that `skill-size-check` now sources, which would remove the last duplicated copy.
- **The `a8cd5e8` citations.**`scripts/skill-size-check.sh` and `tests/test-skill-size-check.sh` still cite `a8cd5e8`, which no branch reaches. `598a7c3` is the reachable equivalent.
@@ -27,7 +27,7 @@ Skills are **not** deployed by `install.sh`. They are distributed as plugins and
Skills, agents, MCP servers, and hooks are distributed as self-contained plugin units under `plugins/`, installed independently via `apm install`, here and in any consuming repo (ADR-0018). Each unit is an **apm package**: `plugins/<name>/apm.yml` plus a hand-authored `plugins/<name>/.apm/{skills,agents,hooks,commands,instructions,extensions}/` tree (ADR-0015). There is no per-plugin `plugin.json` at all — apm reads `apm.yml`, and the repo's one generated manifest, `.claude-plugin/marketplace.json`, is compiled from that source.
Skills, agents, MCP servers, and hooks are distributed as self-contained plugin units under `plugins/`, installed independently via `apm install`, here and in any consuming repo (ADR-0018). Each unit is an **apm package**: `plugins/<name>/apm.yml` plus a hand-authored `plugins/<name>/.apm/{skills,agents,hooks,commands,instructions,extensions}/` tree (ADR-0015). There is no per-plugin `plugin.json` at all — apm reads `apm.yml`, and the repo's one generated manifest, `.claude-plugin/marketplace.json`, is compiled from that source.
Self-contained is a hard constraint, not a description: a file reference inside `.apm/skills/<name>/` may not reach outside that skill's own directory, and there is no cross-skill sharing mechanism to reach for instead. That is why the Vale styles are duplicated across two skills rather than shared (ADR-0014), and why ADR-0020's constants are copied into three validators rather than sourced from one. The constraint used to be explained by Claude Code's plugin cache-install copying a plugin to a cache; that is no longer the reason and never was the only one. It is stated independently for APM package mode by the agentskills.io spec (`plugins/kyberforge/.apm/skills/skill-author/references/deployment-modes.md`), which is why ADR-0024 consequence 6 pins it as a negative result: ending native install did not relax it, and it is not to be re-litigated on the assumption that it did.
Self-contained is a hard constraint, not a description: a file reference inside `.apm/skills/<name>/` may not reach outside that skill's own directory, and there is no cross-skill sharing mechanism to reach for instead. That is why the Vale styles ship inside the one skill that uses them, `factory-audit/assets/vale/` (ADR-0014, ADR-0025), and why ADR-0020's constants are copied into two validators — the plugin's `validate.sh` and the repo's `scripts/skill-size-check.sh` — rather than sourced from one. The constraint used to be explained by Claude Code's plugin cache-install copying a plugin to a cache; that is no longer the reason and never was the only one. It is stated independently for APM package mode by the agentskills.io spec (`plugins/kyberforge/.apm/skills/skill-author/references/deployment-modes.md`), which is why ADR-0024 consequence 6 pins it as a negative result: ending native install did not relax it, and it is not to be re-litigated on the assumption that it did.
Which apm package a new skill belongs in follows from what each one is scoped to. The boundary that matters most in practice is `core` vs `kyberforge`: `core` is the home for cross-cutting, repo-agnostic utility skills that a consumer would want against *their* repo, while `kyberforge` is meta-tooling for the holocron marketplace itself. A skill that authors a target repo's `AGENTS.md` is `core`; a skill that audits a `SKILL.md` against this marketplace's contract is `kyberforge`.
Which apm package a new skill belongs in follows from what each one is scoped to. The boundary that matters most in practice is `core` vs `kyberforge`: `core` is the home for cross-cutting, repo-agnostic utility skills that a consumer would want against *their* repo, while `kyberforge` is meta-tooling for the holocron marketplace itself. A skill that authors a target repo's `AGENTS.md` is `core`; a skill that audits a `SKILL.md` against this marketplace's contract is `kyberforge`.
@@ -61,7 +61,7 @@ Plugin-root documentation belongs in `docs/`. That convention is older than the
Those on-demand files are plain markdown — no frontmatter, no schema. The agent decides when to read each one from task context and the content index label alone. Frontmatter is deferred until there is evidence that agents are loading the wrong files in practice; it is a deliberate deferral, not an oversight to close.
Those on-demand files are plain markdown — no frontmatter, no schema. The agent decides when to read each one from task context and the content index label alone. Frontmatter is deferred until there is evidence that agents are loading the wrong files in practice; it is a deliberate deferral, not an oversight to close.
The governance layer has two phases:
The governance layer has two phases:
- **Phase 1** (complete): instruction and documentation layer — `governance.md` loaded via `@import`; `core/ai-constitution.md` and `docs/wiki/HUMANS.md` as human-facing reference; `CONTEXT.md`extended with governance domain language.
- **Phase 1** (complete): instruction and documentation layer — `governance.md` loaded via `@import`; `core/ai-constitution.md` and `docs/wiki/HUMANS.md` as human-facing reference; `CONTEXT.md`glossing the one governance term used unglossed elsewhere (HITL); the rest of the governance vocabulary is defined in `core/ai-constitution.md`.
- **Phase 2** (planned): deterministic enforcement layer — pre-commit hooks, CI gates, secret scanning, licence scanning. Specified in `docs/research/governance_principles/CONTROLS.md`.
- **Phase 2** (planned): deterministic enforcement layer — pre-commit hooks, CI gates, secret scanning, licence scanning. Specified in `docs/research/governance_principles/CONTROLS.md`.
`factory-audit`'s `validate.sh` holds a second copy of the four ADR-0020 constants
`factory-audit`'s `validate.sh` holds a second copy of the four ADR-0020 constants
(`DESC_SUGGEST_CHARS` / `DESC_MAX_CHARS` / `BODY_SUGGEST_WORDS` / `BODY_MAX_WORDS`) — the two
(`DESC_SUGGEST_CHARS` / `DESC_MAX_CHARS` / `BODY_SUGGEST_WORDS` / `BODY_MAX_WORDS`) — the two
description constants apply to both artifact types it handles, the two body constants only to
description constants apply to both artifact types it handles, the two body constants only to
skills. They are copied rather than imported because a cache-installed plugin's scripts cannot read
skills. They are copied rather than imported because a skill's files may not reach outside that skill's own
files outside their own plugin directory. `tests/test-skill-size-check.sh` asserts the copies agree,
directory (the self-contained constraint in `docs/spec/architecture.md`, "Plugin model"), and
`scripts/skill-size-check.sh` does not ship with the plugin. `tests/test-skill-size-check.sh` asserts the copies agree,
so drift fails CI rather than silently letting an audit bless a skill the commit hook then rejects.
so drift fails CI rather than silently letting an audit bless a skill the commit hook then rejects.
**The shared boundary resolver is one copy** (ADR-0025, then 2026-09-16). It lives between the
**The shared boundary resolver is one copy** (ADR-0025, then 2026-09-16). It lives between the
@@ -438,7 +439,7 @@ Both, and neither is a best-effort accelerator.
`python3` because the script measures the **folded** `description` value. Most descriptions here are
`python3` because the script measures the **folded** `description` value. Most descriptions here are
`>`-block scalars, so a regex over the raw lines measures indentation and newlines instead of the
`>`-block scalars, so a regex over the raw lines measures indentation and newlines instead of the
value. Missing it fails the hook with an install pointer rather than skipping the ADR-0020 checks,
value. Missing it fails the hook with an install pointer rather than skipping the ADR-0020 checks,
which would be a vacuous green. In practice it is already present — pre-commit is itself a Python
which would be a vacuous green — a gate that reports success without having checked anything. In practice it is already present — pre-commit is itself a Python
application.
application.
**PyYAML** because the hand-rolled fallback frontmatter reader has been **removed deliberately**. It
**PyYAML** because the hand-rolled fallback frontmatter reader has been **removed deliberately**. It
- **Run git as `rtk git <subcommand>`, never bare `git`** — org convention, in `&&` chains too, except where a skill's Gotchas name a specific bare-git case (interactive rebase here).
- **Run git as `rtk git <subcommand>`, never bare `git`** — org convention, in `&&` chains too. Run it bare only when rtk would break it: output a script parses, or a command that opens an interactive editor (`rebase -i` in `references/rewrite-history.md`). Say why inline.
- **Refuse to force-push `main`/`master`.** A rewrite diverges the branch and the reflex is to force it back — safe only where nobody else has based work on it.
- **Refuse to force-push `main`/`master`.** A rewrite diverges the branch and the reflex is to force it back — safe only where nobody else has based work on it.
- **`reset --hard` is a confirmation gate, not a default.** It overwrites the working tree, and uncommitted edits it discards were never in git, so no reflog recovers them. Name what will be lost and offer a stash first.
- **`reset --hard` is a confirmation gate, not a default.** It overwrites the working tree, and uncommitted edits it discards were never in git, so no reflog recovers them. Name what will be lost and offer a stash first.
- **Never add `--no-verify`** — using it when a hook fails bypasses the QA gate the pipeline depends on. Only on the user's explicit demand, with a warning.
- **Never add `--no-verify`** — using it when a hook fails bypasses the QA gate the pipeline depends on. Only on the user's explicit demand, with a warning.
Skills deploy standalone, or as part of a package — either a legacy plugin-mode cache install or an APM (`apm.yml`-governed `.apm/` tree, compiled via `apm compile`). All resolve relative paths from the skill root — the SKILL.md body works the same in any of them. Differences only arise when referencing files *outside* the skill directory.
Skills deploy standalone, or as part of an APM package (an `apm.yml`-governed `.apm/` tree, compiled via `apm compile`). Some consumers also receive a package through a host's plugin install, which copies it into a cache. All modes resolve relative paths from the skill root — the SKILL.md body works the same in any of them. Differences only arise when referencing files *outside* the skill directory.
## Cache isolation (plugin mode)
## Cache isolation (host plugin install)
When a plugin is installed, its directory is copied to a cache. Only the plugin's own files are copied. **Any path that leaves the skill directory breaks post-install:**
When a host installs a plugin, it copies the plugin directory to a cache. Only the plugin's own files are copied. **Any path that leaves the skill directory breaks post-install:**
expect_pass "bumped skill with leading blank lines passes""$F"
echo""
echo""
echo"Results: $PASS passed, $FAIL failed"
echo"Results: $PASS passed, $FAIL failed"
[[$FAIL -eq 0]]
[[$FAIL -eq 0]]
Reference in New Issue
Block a user
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.