- LESSONS.md's 2026-06-22 test-placement entry told authors to put test
files directly in scripts/ with a README row. The file-structure contract
the repo now enforces permits tests/ as one of four directories, requires
a tests/README.md when it exists, and FAILs test files in scripts/.
- LESSONS.md's 2026-08-16 entry described a dispatch chain ending at
skill-author/references/retrofit.md in the present tense. This branch
deleted that file. Sibling entries whose referents the branch removed
were marked historical; this one was not.
- VISION.md's Phase 1 now puts stack, framework and deployment choices out
of scope for this repo, while Phase 3 still named React Native and Tauri.
README.md was checked and needed no change: its offline guarantee already
carries the populated-apm_modules condition from 8cfd54f and agrees with
gates.md and AGENTS.md.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NwD8Egs5r4ndqeFLmhusX2
The document has been re-measured four times and each pass moved figures
the next pass had to chase -- three of the last five commits on this branch
were figure corrections to it, and correcting it changes the line counts it
reports about itself. It is now a dated record frozen at 1ec3e8a. Figures
stand as measured at the commit each one names and are not maintained.
Freezing covers staleness. It does not cover a figure that never
reproduced or a claim that says verified for a check that fails, so those
are struck:
- Finding 11's provenance-validator counts, 320/1,145/572/134 = 2,171, were
true at no commit. The files are 324/1,152/576/134 = 2,186 and have been
since 620f20b created them. The derived 5,380 and 7,136 follow.
- Finding 11's line citations into lib-provenance-skill.sh, stated as
re-derived at HEAD, were uniformly seven low and none landed on the code
named.
- The tests/ line total pinned to 1614bce is that commit's suite count with
384756b's line count.
- Finding 33's "all five instruction-level citations still resolve at HEAD,
verified with sed -n", dated 2026-09-19, is false. Four resolve.
improve.md:82 stopped carrying the content at baa2f5d, three days before
the verification was claimed.
Also reconciled: finding 11's 242-file effort total against its own struck
46, finding 16's two different deltas for ef27c97, a clause pinned to
baa2f5d carrying c07ca07's figures, and the preload-tax row's 39 skills
against the census row's 38.
Notes that date themselves "at HEAD" name no fixed commit, and this commit
moves HEAD under them, so the banner now says so rather than re-deriving
twenty of them.
This review round is recorded on the pull request, not here. A frozen
document that grows another section is not frozen.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NwD8Egs5r4ndqeFLmhusX2
Seven ADRs described code that no longer exists or behaviour the gates do
not have. Where the wrong text came from main it carries a dated
correction; where this branch introduced it, it is fixed in place, because
main never published it and there is no record to preserve.
Fixed in place, branch-introduced:
- ADR-0021's 2026-09-14 correction asserted apm audit --ci "was never a
drift gate at all". It is one: it replays the install and diffs. The
claim contradicted this branch's own AGENTS.md and gates.md.
- ADR-0015 said unconditionally that no pre-push hook needs the network.
The guarantee holds only once apm install has populated apm_modules/.
- ADR-0014's 2026-09-16 correction said restoring .pre-commit-hooks.yaml
would ship a hook that fails for every consumer, because their checkout
has no lib-boundary-resolver.sh. pre-commit clones the whole hook repo
and skill-size-check.sh resolves the library from BASH_SOURCE, so the
hook would work.
- ADR-0019's "twelve hooks pass under unshare -rn" matched neither HEAD
(8) nor main (14), and stated the offline guarantee unconditionally.
Corrected, inherited from main:
- ADR-0022 and ADR-0013 named skill-frontmatter's pre-commit hook as the
enforcer of mandatory metadata.version. That hook was deleted on this
branch; the check lives in skill-size-check.sh.
- ADR-0022 enumerated the tip rule's carve-outs as a closed list and
described a single merge-base. The gate also exempts a tree-identical
skill and intersects every base from merge-base --all, and emits a third
failure form. 8cfd54f said the documented behaviour did not change; it
did. The gate is correct and is unchanged -- the record was not.
- ADR-0020's Decision still routed description overflow to README.md, its
ADR-0025 amendment pointed the mirrored constants at validate.sh, which
holds none, and its Enforcement table still named the two deleted
validate.sh paths.
- ADR-0015's Status claimed every plugin's plugin.json is pack output;
none exist.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NwD8Egs5r4ndqeFLmhusX2
boundary_clause_status() ran BOUNDARY_ARROW.search() and _arrow_targets()
over the whole description, so one arrow clause that parsed suppressed the
diagnostic for every other clause in it. A backticked hyphenated routing
target wrapped across lines in a folded scalar was therefore silently
unchecked -- no error, no suggestion, exit 0 -- whenever the description
carried one other clause that parsed. Written bare, the same wrap errors
correctly. That is the shape #100 regressed on.
The check is now per clause. Nothing that passed starts failing: all 68
routing targets across the 38 SKILL.md files resolved before and still do.
26 of those descriptions carry more than one arrow clause, so the
suppression was live across two thirds of the corpus, not an edge case.
validate-skill.bats pins the shape. test-adr0020-targets.sh's comment
described the #100 regression as a backticked wrap; the historical text was
unbackticked, which is precisely the shape the gate did not catch.
Also closes three README misroutes the branch left in the enforcement
layer: CompositionNote.yml's message, agent-description-quality.md:58 and
vale-wrap.sh's header still sent overflow to a skill-root README.md and
named the two skills ADR-0025 merged away. 1ec3e8a fixed the prose and
missed the rules that enforce it.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NwD8Egs5r4ndqeFLmhusX2
Four claims in the spec and the hook config stated as fact what the tools
do not do:
- gates.md:485 said factory-audit's validate.sh "holds its own copy of"
the ADR-0020 constants. gates.md:411-413, twenty lines earlier, said it
carries none of them and named the mode libraries. The libraries are
right: lib-checks-skill.sh:313-316 and lib-checks-agent.sh:164-165.
architecture.md repeated the same error.
- gates.md stated the case count for test-adr0020-contract.sh as "29 at
HEAD", explicitly presented as measured. Running it prints 44; 384756b
added the hook-wiring assertions after the text was written.
- gates.md:913 and :916 described "Both audit skills'" behaviour in the
present tense, three and six lines above :919 saying factory-audit's is
the only copy left.
- The apm-audit-ci block named manifest-parse as a check, said the hook
does not scan for hidden Unicode, and called root lockfile-exists
vacuous. apm 0.28.0 runs ten checks, content-integrity does scan for
hidden Unicode, and there is no manifest-parse row.
Also: the version-bump gate's baseline is documented as the single
merge-base it is not -- it resolves every base with merge-base --all,
intersects the changed-skill sets, exempts a tree-identical skill, and
emits a third sha-suffixed failure form. The gate's own header documents
this correctly; the spec did not. Behaviour is unchanged.
The "none of them need the network" line added on this branch cited a
README section that says the opposite for a fresh clone, and the
check-vale-style-sync rationale said 6 of 17 assertions diffed the Vale
copies where ADR-0025 says 2 diffed and 4 more only located them.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NwD8Egs5r4ndqeFLmhusX2
skill-author still told authors to move description overflow "to the
body or to README.md" while this branch deleted every per-skill
README.md, every references/README.md and the README scaffold template.
factory-audit's skill-file-structure.md bans non-spec files at the
skill root, and the line that used to carve README out of that rule
went with them. So skill-author created the file, factory-audit failed
it, and nothing read it. 8ce5392 fixed the two scripts and missed the
reference prose.
The two contract.md files now differ deliberately: a skill's overflow
goes to the body or a references/ file, an agent's to the body alone,
because an agent is a single file with no references/ directory to
disclose to. agent-description-quality.md's "the plugin's README.md" is
left alone, plugin READMEs being the ones that survive.
Deleting retrofit.md also dropped three instructions baa2f5d did not
restore with the cut list, two of which retrofit.md itself recorded as
having no validator behind them: re-cite sources.md's Contributing
files after content moves, since validate-provenance exits 0 on exactly
that drift, and re-check a relocated gate's reachability, since a
Gotcha moved into one flow's file is invisible to the others and the
word counts improve either way. The third is that boundary clauses are
plural — contract.md read as a cap where git-remotes carries four.
Also: contract.md named an unqualified scripts/validate.sh that does
not exist in skill-author, which skill-file-structure.md calls a hard
error; and agent-body-and-delegation.md's simile pointed at a stale
README row as the characteristic skill defect, a defect class that can
no longer occur, replaced with a SKILL.md naming a references/ file
that is not there.
skill-author 1.0.4, agent-author 1.0.3, factory-audit 1.0.2.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NwD8Egs5r4ndqeFLmhusX2
CONTEXT.md used two terms it no longer defines. This branch deleted the
Preload tax and Skill context contract entries as audit finding 31, but
the Hand-invoked skill definition and the example dialogue still used
both, bolded, which is this file's convention for a defined term. The
definitional file contradicted itself while AGENTS.md tells every
session to read it as authoritative. Rephrased in place, the way
e2e957e handled the one the audit's own note records.
ADR-0024 said 10 .bats files deploy across 6 skills; ADR-0025 merged
two of those directories the next day, on this branch, leaving 5. It
was also the only ADR ADR-0025 invalidated without an amendment banner,
as was ADR-0016, which still named agent-audit in the present tense as
the live enforcer. Both get the banner the other nine carry, and the
figure and names are corrected in place as well, since these sit in
text asserting present fact rather than a superseded decision.
ADR-0019's correction block from 1614bce was inserted mid-paragraph and
swallowed the original's trailing sentence, leaving the quote malformed
and the next line starting lowercase mid-sentence. gates.md took the
same correction and is not affected.
In the audit note: two of §12's five open follow-ups were already
closed (e4ed343 repointed the a8cd5e8 citations at 598a7c3; #101 closed
2026-09-16, so Closes#101 is a no-op), the same stale hash sat at :330
with a wrong line number, the vale-wrap counts had drifted from 63/19
to 65/14 and are now pinned to a commit per §1's own convention, and
the deleted-suite tally said eight where the diff shows nine.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NwD8Egs5r4ndqeFLmhusX2
The repo's gates were not pinned to their wiring. Deleting the
check-skill-version-bump block from .pre-commit-config.yaml left the
whole suite green; deleting eight blocks at once, run-tests among them,
also left it green. Only 4 of 20 hook ids had their wiring pinned
anywhere, so a merge conflict resolved badly could stop the suite
running at pre-push forever while every test still reported green.
test-adr0020-contract now derives the repo-authored hooks from the
repo: local entries and pins each one's id, entry and stages against an
explicit expected set, both directions, with the same non-vacuity
guards the file already applies to its own fixtures. Upstream hooks and
their rev: values are untouched, so a rev bump does not churn the test.
Mutation-checked: a removed block, a repointed entry and a hook moved
off pre-push each go red; a rev bump, a comment edit and reordering
stay green. 29 -> 44 assertions.
run-bats computed each file's TAP plan and then discarded it, so a
process printing "1..10", three ok lines and exit 0 was counted as
"3 tests, 0 failures" with seven tests silently gone. That is exactly
the wrapper-swallows-the-status case the runner's own comment puts in
its threat model, and the plan was the only surviving signal. The plan
is now enforced in both directions when a file emits exactly one.
Also: test-no-pipefail-early-exit-grep's live-tree floor goes from 20 to
50 against an actual 57, matching test-vale-wrap's per-glob discipline,
and test-vale-wrap's header names the real path to vale-wrap.sh.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NwD8Egs5r4ndqeFLmhusX2
Two reproduced bugs in check-skill-version-bump:
- The origin/main-tip check fired even when the pushed skill was
byte-identical to main's tip, so a cherry-pick or backport failed a
push that ships nothing. The merge-base intersection ea119d8 added
covers that only when some base carries the content, which a
criss-cross history gives and a linear one does not. A new
same_subtree compares tree object ids, so the exemption holds
whatever route the history took.
- The failure line reported "baseline: none" when the skill was absent
at every merge-base but present at the tip, and the Fix: line then
named no version. The author writes the natural 1.0.0 and gets a
second blocked push. It now falls back to the tip's version.
ADR-0022 is not amended: the documented behaviour does not change, and
ea119d8 set the precedent by fixing the same failure class script-only.
1614bce verified that executables.allow grants are version-blind and
corrected ADR-0019, gates.md and apm.yml, but missed the gate script's
own header and its operator-facing FAIL message, which still told the
reader deployment was silently broken, and gates.md's hook summary,
which still called it a silent-failure guard. All three now match.
Also: README's offline guarantee carries the populated-apm_modules
condition gates.md and AGENTS.md already state; the scripts/ layout row
drops "sync" for the three deleted sync scripts; the check-rtk-prefix
README rationale names the 12 subdirectory READMEs that survive rather
than the skill-root ones this branch deleted; gates.md re-cites its
three head -1 sites by enclosing function per its own :238 rule; and
deploy-manifest drops a pointer to a provider-manifest.sh that has
never existed on main.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NwD8Egs5r4ndqeFLmhusX2
gates.md's check-executables-allow-sync section and ADR-0019 both stated
that apm matches executables.allow on an exact `<package>#<version>`
dictionary lookup with no wildcard and no version-less form, and drew the
conclusion that a kyberforge version bump silently stops the entry
matching and the SessionStart hook deploying.
Verified against apm 0.28.0: is_package_approved is an exact lookup, but
install/exec_gate.py calls it across a candidate list carrying the
version-blind name, materialize_exec_map stores each approved key under
its version-blind name as well, and _map_grants matches exact key,
version-blind name, or any stored key sharing that name. Approving
kyberforge#2.0.0 therefore keeps covering kyberforge#2.1.0.
The decision is unchanged: check-executables-allow-sync stays, justified
by this repo's own requirement that the key track plugins/kyberforge/
apm.yml's version:, rather than by an apm-level failure mode. ADR-0019
keeps its original text with a dated correction, since whether apm
behaved this way when it was written was not established.
Follows the same correction applied to root apm.yml's comment in 82b7bbc.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NwD8Egs5r4ndqeFLmhusX2
Group 3 of the validated PR #135 review fixes. Every figure and commit
citation below was re-verified at HEAD before being written.
ADR and architecture:
- #7 ADR-0025 cited 61b0b9c, which no published branch reaches. Repointed
to 620f20b (identical parent tree, reachable from the PR branch), with a
note that neither is reachable from origin/main. The parser-drift
paragraph now credits 598a7c3 (the reachable PR #129 squash) and keeps
484357a only as a pre-squash parenthetical.
- #8 architecture.md dropped the pointer at the LESSONS.md entry this
branch deleted.
- #9 architecture.md's ADR entry points now name ADR-0015 (the one
compiler) and ADR-0024, and list ADR-0024 as superseding ADR-0017.
- #10 ADR-0024 section 4 rewritten: the standing patch-bump rule is
apm-workflow's configure.md, not ADR-0006's, and this change does not
trigger it. ADR-0015:93 carries a correction for the misattribution.
- N5 ADR-0021 gained a Correction note for the deleted
scripts/check-manifests.sh (e647f14).
gates.md:
- #11a the four ADR-0020 constants live in lib-checks-skill.sh:313-316 and
lib-checks-agent.sh:164-165, not in validate.sh.
- #11b the pretty-format-json exclude is two alternations expanding to
three tracked files, including .claude/apm-hooks.json.
- #11c the ADR-0020 contract suite runs 28 -> 27 -> 29 (620f20b,
4de5b6b, ef27c97), 29 at HEAD; the unverifiable 25 is dropped.
- #11d the boundary resolver is one copy since ef27c97.
- #12 apm-audit-ci documents the 10 root checks and the 1 plugin check
apm 0.28.0 actually runs, that content-integrity IS the hidden-Unicode
scan, that manifest-parse is not a named check, and that the hook needs
a completed apm install. The offline claim is qualified accordingly.
- N9 gates.md:142-146 verified to still match the hook description.
AGENTS.md:
- #12 the no-network session rule is qualified to a populated
apm_modules/.
Audit note:
- A1 hook counts corrected to 27/9 -> 26/8 -> 27/9 -> 26/8 (26 and 8 at
HEAD) and the dangling pointer dropped.
- A2 skill-size-check.sh is 509 lines with the resolver sourced, not 1,522
embedded; citations repointed to skill-size-check.sh:323-335 and
lib-checks-skill.sh:235-283 (fail() at :265 and :280), and that library
is 627 lines.
- A3 consumers receive 15 test files across 5 skills; 16 tracked test
paths repo-wide.
- A4 the "do not run apm update on this branch" instruction is marked
superseded, with the branch-aware guidance in its place.
- Finding 31's "true orphans" claim corrected for HOTL and Sycophancy,
both still used in core/ai-constitution.md.
Same class, found during group 2:
- skill-author's deployment-modes.md no longer points at .mcp.json
configs (deleted in c96ca9c); metadata.version 1.0.2 -> 1.0.3.
- git-orchestrate's context contract clarifies that
user_config_overrides is caller-supplied session state, not a config
read. The field name is unchanged.
- B3 root apm.yml's executables.allow comment: grants are version-blind
in apm 0.28.0, so the #2.0.0 suffix is cosmetic to apm and a bump does
not break the hook; the suffix stays because
check-executables-allow-sync.sh requires it.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NwD8Egs5r4ndqeFLmhusX2
Closes four PR #135 review findings in skill content.
#2 — plugins/git/config.example.json was deleted in f5e4d0d, but four
git-plugin files still told the agent to read it. The file only ever
carried branching_pattern, commit_style and rebase_strategy, so the
`base_branch` and scope instructions were wrong even before the
deletion. Each site now describes what the skill actually does: base is
`main` under GitHub Flow or `develop` when Gitflow is inferred, the
Gitflow fallback keys off the repo's own branches, the orchestrator
contract's `base` defaults to the inferred base branch, and the commit
scope is inferred from the changed files.
N6 — gitea-prs was the one gitea skill with no permission-scope caveat
on a 404. Added one alongside the existing issue/PR number-space
guidance rather than replacing it: a 404 is only evidence of
"that number is an issue" once write:repository scope is confirmed.
N4 — plugins/kyberforge/bin/README.md pointed at `.mcp.json`, but all
six plugin-root .mcp.json files were deleted in c96ca9c (ADR-0018).
${CLAUDE_PLUGIN_ROOT} itself is still live, so the sentence now points
at .apm/hooks/hooks.json, which kyberforge's own hook already uses.
N7 — not applied. The finding claimed a marketplace field override
emits a verbose BuildDiagnostic that `apm pack -v` surfaces, so
"silently wins" was wrong. apm 0.28.0 does construct the diagnostic in
marketplace/output_mappers.py, but nothing renders it:
_render_marketplace_result in commands/pack.py iterates `warnings`
only, and BuildReport.diagnostics has no consumer. Confirmed on a
fixture — neither `apm pack -v` nor APM_LOG_LEVEL=DEBUG prints the
override, and --check-versions reports [matches]. The existing wording
in configure.md and marketplace.md is correct, so both are unchanged.
Version bumps required by check-skill-version-bump.sh: git-branches
1.0.4 -> 1.0.5, git-commits 0.1.6 -> 0.1.7, gitea-prs 0.1.4 -> 0.1.5.
bin/README.md is outside any skill directory and needs no bump.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NwD8Egs5r4ndqeFLmhusX2
B1: check-skill-version-bump.sh resolves every merge-base with `git merge-base
--all` instead of the single base git happens to pick. A criss-cross history has
two, so the verdict turned on that choice: a skill byte-identical to main's tip
could still be reported "not above merge-base" / "not above main tip" and fail a
push that should pass. A skill now counts as changed only when it differs from
EVERY base, and its version must exceed the version at every base it exists at
as well as at the main tip; with more than one base the failure names which one.
Case 40 in tests/test-skill-version-bump.sh builds the criss-cross fixture and
pins both directions.
B2: check-apm-current.sh no longer assumes the remote default branch is `main`
when origin/HEAD is unset. A checkout whose default is `master` was standing on
its default branch and being told "this is a feature branch, so discard it" --
to throw away a real lock update. With origin/HEAD unset nothing is asserted and
the neutral advice stands. tests/test-apm-current-hook.sh covers the unset case
on both `main` and `master`.
#4: the required-frontmatter checks folded into skill-size-check.sh by c8a7c9e
were untested apart from the leading-zero shape -- mutating the missing-version
ERROR into a no-op left every suite green. tests/test-adr0020-frontmatter.sh now
pins name presence and non-emptiness, metadata.version presence and semver
shape, and the four grep defects the deleted test-skill-frontmatter.sh named.
#5: nothing asked whether a Vale rule still MATCHES anything -- rewriting
CompositionNote.yml's tokens to match nothing left test-vale-wrap.sh at 63/63.
Case 35 enumerates the rule files under the Kyberforge* style directories at run
time, requires an alert from each on its own fixture, and fails when a
discovered rule has no fixture row. The stale comment at case 31 is corrected.
#6: tests/run-tests.sh --strict exited 0 when discovery found no test-*.sh at
all; strictness only ever acted on skips, and with no suites there were none. It
now cross-checks the git index the way run-bats.sh does and fails
unconditionally on an empty set, naming the search root.
N9: the skill-size-check hook description in .pre-commit-config.yaml covered
only the size, context-budget and boundary-target gates. It now also names the
required frontmatter fields, matching docs/spec/gates.md.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NwD8Egs5r4ndqeFLmhusX2
The accepted ADR still called the skill-author/agent-author duplication
an open input to #101. The branch decided it: continued drift, no sync
gate, on the measured 150-180 line overlap. State that decision so the
ADR stops contradicting the PR that closes#101, and mark the old
skill-audit citation historical.
Refs: #101
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
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>
Why: 4de5b6b removed check-release-needed and two test suites, but the
audit's hook and test figures still described the tree before it.
Implementation Notes: re-measured at 4b17703. Hook entries 27/9 -> 26/8
(line 52 and finding 1), gates.md now reads 10 reported / 8 authored,
the enforcement row is 10,000 test lines over 19 suites + 502 runner
lines + 1,924 in scripts/, and the withdrawn tests target is struck.
The walk-up gate figures (381 + 297) were re-checked and are unchanged.
Also records that the run-tests wall-time follow-up and finding 22 are
deliberately untracked.
Impact: docs only.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Why: the last loose end, finding 6's check-apm-agents-valid fold, was
never closed, and the document did not say it was finished.
Implementation Notes: finding 6 records that the hook stays at repo
level, because it validates this repo's own agent files, which a
consumer-shipped skill test cannot reach. A status banner at the top
and a closing note in §7 mark the audit complete, with only 22
deferred with bin.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Why: HUMANS.md pointed at docs/ai-constitution.md, which adaa978 moved
to core/ai-constitution.md. The wiki commit is pushed, so the pointer
can now follow it.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Why: the 2026-09-16 grill decided the two salvage options the verified
notes for findings 5 and 16 had left open.
Implementation Notes: finding 5's differential-suite speed-up is closed
as not proceeding, with the per-suite timings that decided it. Finding
16's resolver-sourcing option is recorded as done in ef27c97, with the
line delta and the output-identity check.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Why: scripts/skill-size-check.sh embedded a byte-identical 1,061-line copy
of the ADR-0020 boundary resolver only because it was also exported
through .pre-commit-hooks.yaml, whose consumers could not reach a file
inside the plugin. 4de5b6b retired that export, so the hook now runs only
in this repo and can source factory-audit's lib-boundary-resolver.sh like
validate.sh does. One copy removes the edit-one-paste-the-other hazard.
Implementation Notes:
- The hook's Python program is assembled from its own preamble, the
library's resolver and its own checks, read from quoted here-docs. The
assembled program matches the old one line for line except one comment,
and the hook's stdout, stderr and exit code are identical over every
corpus SKILL.md and the 26 differential-suite fixtures.
- The hook fails closed, naming the library, when it is missing or
defines no resolver.
- test-adr0020-contract.sh assertion 1 now pins the single copy: one
marker pair in the library, none in the hook, fail-closed on a missing
or gutted library, and a sentinel planted in a copied library that must
appear in the hook's output. 1a expects exactly one authority. 27 -> 29
passes.
- ADR-0020 and ADR-0025 carry dated amendments; gates.md and the
library, hook and mode-library comments no longer describe two copies.
- factory-audit is new on this branch, so the version-bump gate exempts
it; kyberforge is already at 2.0.0 against main's 1.6.2.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Why: the always-on governance.md told agents to read
docs/ai-constitution.md when a decision is not covered, a path that
exists only in this repo, so the fallback failed in every other project.
(Simplification audit finding 27, second defect.)
Implementation Notes:
- Move docs/ai-constitution.md to core/ai-constitution.md; the existing
core deploy step now ships it to ~/.claude/core/.
- governance.md line 4 and line 73 name ~/.claude/core/ai-constitution.md;
the HUMANS.md and CONTROLS.md pointers now say they live in the
holocron repo.
- Repoint path-qualified citations in AGENTS.md, architecture.md,
skill-implementation-workflow.md and CONTROLS.md. The vendored
write-skill example and the audit's historical notes are left as
records.
- The docs/wiki gitlink is not bumped here; the wiki commit awaits push.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Why: a full read after finding 15 closed found notes that still read as
open or predated later outcomes, and the fixed governance.md had never
reached ~/.claude.
Implementation Notes:
- Record that the fixed governance.md is deployed. It was copied alone
rather than through install.sh, which would have overwritten
machine-local keys in ~/.claude/settings.json.
- Close finding 2: check-scope-walkup-sync stays now that 14 left its
ports at four and 15 is refuted.
- Correct the §9 and §10 sentences that still assumed 15 would merge,
and tick the §8 external-consumers question.
- Mark every closed finding [x] regardless of outcome; only 22, deferred
with bin, stays unmarked. §7 states the convention.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Why: finding 15 proposed merging skill-author and agent-author on the
claim that they share most of contract.md and near-identical steps.
Measured, the pair shares about 150-180 distinct non-blank lines,
against the 2,934 the factory-audit merge removed. ADR-0020's exclusion
of the pair holds, so the merge does not proceed.
Implementation Notes: finding 15 is struck through and carries a dated
Refuted note with per-file counts and the command that reproduces them.
The §7 order and status notes and the §8 ADR-0012 bullet now show no
open finding, with 22 deferred with bin. ADR-0020's rejected
alternative records the measurement; ADR-0025 point 7 no longer calls
the finding open and unmeasured.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Why: a review of 4de5b6b and 3c5f6a6 found seven places that still
described the retired external hook contract as current, or that could
pass without checking anything.
Implementation Notes:
- gates.md: drop the "one caveat below" pointer; the caveat is gone.
- SIMPLIFICATION-AUDIT.md: the §7 note now says all but one entry is
closed and lists 36 (4de5b6b) as struck. Finding 2's count chain gets
a closing note: pre-push is 8 repo-authored hooks (10 reported).
- .pre-commit-config.yaml: the check-vale-style-sync comment points at
case 32 (one-plugin narrowing guard), not the deleted case 33.
- ADR-0014: the retirement pointer now covers the ADR-0025 amendment
above it too, naming case 33 and the exported hook IDs. ADR-0022 gets
a reciprocal amended-by note on its check-release-needed comparison.
Historical body text is unchanged.
- test-vale-wrap.sh case 32: property 3 fails when a class's corpus
regex matches no tracked file, instead of passing vacuously after a
layout move. The corpus regexes are now globals so a new Part D can
point them at a missing layout and require that failure.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Why: 4de5b6b retired the external pre-commit hook contract, so the audit
still listed 36 as open, with a deferred decision.
Implementation Notes: finding 36 is struck through and carries a dated
Done note (measured line counts, the case 33 cost and how its guard was
kept). The §7 status notes, the §8 consumer question and the §10
dispositions now leave 15 as the only open finding, with 22 deferred
with bin.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Why: .pre-commit-hooks.yaml and its release-tag gate served external
consumers that do not exist. No repo on the Gitea instance pins these
hooks, and the README names apm as the only supported install path. The
mechanism was also already failing: skill-size-check.sh changed after
v2.0.1 with no tag cut, and the gate cannot fire through Gitea's merge
button. (Simplification audit finding 36.)
Implementation Notes:
- Delete .pre-commit-hooks.yaml, scripts/check-release-needed.sh,
tests/test-check-release-needed.sh and tests/test-vale-hooks-consumer.sh,
and remove the check-release-needed pre-push hook. The repo: local
skill-size-check and vale-audit-prefilter-* hooks are unchanged.
- ADR-0014 is amended, not retired: its runtime decision to bundle Vale
inside factory-audit stands. The amendment keeps the entry[0]-only
constraint (LESSONS.md:101,105) in case the export returns. ADR-0025
gets a pointer.
- test-vale-wrap.sh: drop case 33 (the cross-manifest drift check) and
case 28's hook-scope half, which read the published manifest. Case 32
now also requires each hook to select every tracked file of its class,
which keeps case 33's one-plugin-narrowing guard, with a mutation test.
- test-skill-size-check.sh and test-adr0020-contract.sh now assert the
hook contract and verbose: true on .pre-commit-config.yaml only.
- gates.md: pre-push count goes from 9 to 8 authored hooks (11 to 10
reported), and the Release table, the External consumers section and
the two-manifest scope table are removed. README and script/test
comments no longer describe the export as live. The resolver comment
is edited identically in both copies.
- The v1.0.0/v2.0.0/v2.0.1 tags are left in place; they are inert.
ADR: 0014
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Why: the audit carried stale figures and working-tree citations after the
grill commits landed, and this review round's decisions needed recording.
Implementation Notes: line totals pinned to c07ca07, working-tree
citations replaced with commits, version-location count corrected, and a
new section lists this round's dispositions.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Why: the docs said to discard a refreshed apm.lock.yaml on a feature
branch because the refresh records main's commit, but the branch's own
lock records a (older) main commit too, and the SessionStart notice gave
the same "commit or discard" advice on every branch.
Implementation Notes:
- check-apm-current.sh picks fixed advice by branch: commit or discard
deliberately on the default branch (origin/HEAD, else main), discard and
reinstall on a feature branch; the branch name is never interpolated.
- README, AGENTS.md and ADR-0019 give the real reasons (no lock churn in
the branch diff, deployed tree matches the committed lock), the cost
(the session runs the older main) and that the next session start
refreshes again.
- ADR-0019's check-clean and stale-server claims restated to match apm's
source.
ADR: 0019
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Why: for a local-path marketplace entry, apm treats a root description: as
an override of the package's own apm.yml, reported only at verbose level,
so the "two copies stay identical" rule had no enforcement. Same fix as
2def060 made for version:.
Implementation Notes:
- All six root copies matched their package apm.yml before removal; the
compiled marketplace.json descriptions are unchanged.
- apm-workflow references now scope the "omit it" advice to local-path
entries: on a remote entry, version: is the semver range that selects
the tag, and version: or ref: is required.
Impact: ADR-0021 amended; the package apm.yml is the single source.
ADR: 0021
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Why: each of these plugins changed shipped .apm/ content on this branch
without a package version bump, which configure.md requires and no gate
catches. No skill, agent or hook was removed, so a patch bump fits.
Implementation Notes: marketplace.json regenerated with apm pack; the diff
is the five version strings only.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Why: two branches that both bump a skill 1.0.0 -> 1.0.1 with different
content merge without a conflict, and each passed the gate against its own
merge-base, so main could ship two changes under one version.
Implementation Notes:
- check-skill-version-bump requires the pushed version to exceed both the
merge-base and the main tip; failures name the baseline they missed.
- Presence is read from the tree, so a blob missing from a partial clone is
a read failure instead of a silently exempt "new" skill.
- A leading UTF-8 BOM no longer reads as a missing version.
- Version parts reject leading zeros in all three validators
(check-skill-version-bump, skill-size-check, factory-audit).
- New tests cover equal bumps, moved files, major/minor ordering, bad refs,
unreadable blobs, mode-only changes, symlinks and tag peeling.
Impact: ADR-0022 amended (reverses "not main's current tip"); gates.md
updated to match, including pre-commit 4.6.1's exact ref selection.
ADR: 0022
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Re-measures the figures the new hook and ADR edits moved, re-points
shifted gates.md and config citations, ticks the decided §8 questions,
and aligns §7, §10 and the finding 8/18/28/33 notes with the decisions.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The root apm.yml packages[] entries no longer carry version:, and a
version there is a silent override that --check-versions does not
catch. configure.md and marketplace.md now name the package's own
apm.yml as the single source and drop version: from the examples.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
ADR-0022 and gates.md now state the merge-base baseline, the fail-closed
cases, the PyYAML requirement and the multi-ref push gap (shared with
check-release-needed); the new hook gets its own gates.md group. Both
ADR additions follow each file's amendment format. README and AGENTS.md
now say to discard a refreshed apm.lock.yaml on a feature branch, and
that an .apm/ edit is live only once it is on the remote's main.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
- Read changed paths NUL-delimited so non-ASCII paths are no longer
silently skipped.
- Fail closed when only local main resolves and the pushed commit is
the merge-base, instead of passing on an empty diff.
- Accept ASCII-only versions with at most nine digits per part.
- Check for python3/PyYAML up front, and report read failures as such
rather than as a missing version; name a missing SKILL.md.
- Document that pre-commit gates only the first ref of a multi-ref push.
Tests grow to 29 cases covering each fix plus annotated tags, CRLF
frontmatter, unrelated histories and pushing main.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Findings 11, 17, 23, 25 and 27 are declined. From the grill: 33 and 34
done, 8, 18, 20 and 28 closed, 22 deferred with bin. Open: 15 and 36.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The session-start refresh resolves against main, so on a branch it
redeploys content the branch removed and rewrites the lock to main's
commit. Documented as a consequence rather than skipped in code, since
a skip would only freeze the session on an older main. Also records the
re-measured refresh time (~24-26 s). Simplification audit finding 34.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The six packages[].version lines restated each plugin's own apm.yml
version and were unpoliced: on drift apm silently shipped the curator
value. apm reads the plugin's apm.yml when the entry is absent, and
apm pack --check-versions --check-clean still passes with the committed
marketplace.json unchanged. Simplification audit finding 33.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
check-skill-version-bump fails a push when a skill directory changed
against its merge-base with main (tests/ excluded) without a strictly
higher metadata.version than main. New, renamed and deleted skills are
exempt; every plugin is covered. Recorded as a dated section in
ADR-0022 and documented in gates.md.
Patch-bumps the 17 skills that changed on this branch without a bump,
so the branch passes its own gate. Simplification audit finding 33.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Why
A six-agent review of the two preceding commits found their code sound -- the
differential claim holds, the published hook contract is byte-unchanged -- but
their prose drifted from it in three ways: statements of fact the code
contradicts, markers in a convention this repo does not use, and figures that
went stale when the merge changed what they counted.
Implementation Notes
ADR-0025's edge-path table is rewritten around one stated doctrine: exit 0 is
audited and clean, exit 1 is audited with findings or a target present but
unreadable, exit 2 is that nothing was audited. Its old row 1 promised "one
generic matches-neither message" for three different inputs; there are three
distinct messages, and the missing-path case exited 1 until the preceding commit
fixed it. Rows are added for the preflight and CDPATH changes, because a table
claiming to enumerate every entry-point behaviour change reproduces its own
"an earlier revision of this ADR said they were behaviour-neutral" failure if it
omits any.
ADR-0025 also gains a Consequences supersession record in ADR-0016's form:
partially-superseded entries for 0008, 0014, 0020 and 0021, and explicit
"is not superseded" entries with reasoning for the rest. Twelve ADRs are amended
and it previously listed none.
ADR-0008 moves from an amendment note to partially superseded. Its contract
genuinely narrowed -- an agent .md outside an agents/ directory was audited
before the merge and is refused now -- and ADR-0020 already recorded that the
merge "reopens ADR-0008". Its detector description said "a path under
.apm/agents/", the phrasing ADR-0025 rejects as wider than the script and
circular; the shipped rule is a .md whose immediate parent is named agents/, at
any scope.
ADR-0020's amendment claimed the boundary resolver is sourced by
validate-provenance.sh. It is not, and never was; only validate.sh sources it,
once per mode branch. Three Home-column entries pointed at reference filenames
the merge renamed, one of which now resolves to two files because its row covers
skills and agents.
Five ADRs opened with "Skill renamed per ADR-0025", a form this repo does not
use, in the same commit that used the conventional "Amended by ADR-0025" twice.
They are normalized. "Renamed" was also wrong: the BREAKING-CHANGE trailer says
the skills were removed and their flows merged.
SIMPLIFICATION-AUDIT.md had 2026-09-15 notes attached to headlines that were
never updated, against its own convention of correcting in place with
strikethrough. Every figure here was re-derived at HEAD by command, and several
differed from the review's own numbers, so the notes record the basis rather
than the result alone.
LESSONS.md asserted the two review-time suite failures were the SIGPIPE race.
The commit that fixed that race explicitly declined to claim it -- the suite was
running while agents edited live config files -- so the hedge is restored.
Impact
No code, test or configuration change; documentation only. Suites stay 20/20
strict with 0 skipped and 374/374 bats. No gate parses ADR or gates.md content,
so nothing here is load-bearing for a hook.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YR2CjVumUbEGWcMikcoXBD
Why
Two suites failed intermittently — tests/test-vale-wrap.sh case 21 and
tests/test-check-release-needed.sh cases 4 and 15 — on correct output, and never
when run alone. The cause is the `echo "$OUT" | grep -q P` idiom under
`set -o pipefail`: grep -q exits as soon as it has an answer, bash's echo can
hand a multi-line value to the pipe one line at a time, and a write after the
reader is gone kills echo with SIGPIPE. pipefail then reports the writer's
death, so output that DID match reads as "no match". Every observed failure had
lines after its match; case 15's match is on line 1 of 6, the widest window in
that file.
Forced with a pause before the writer's last line, the pipe form failed 50 of 50
runs; a here-string, a match on the last line, and the same pipe without
pipefail each passed 50 of 50. Unforced the rate is about 1 per 670 suite runs,
which is why it read as a flaky gate rather than a bug.
The failures at review time are consistent with this, but were not proven to be
it: the suite was running while agents edited live config files in place, and a
brief change to .vale.ini or .pre-commit-hooks.yaml would produce the same two
failures. The race is real and fixed either way.
Implementation Notes
`grep -q P <<< "$VAR"` has no separate writer process, so there is nothing to
race. It is not a retry or a sleep. 121 sites converted across 9 files, three of
them scripts rather than tests: new-agent.sh, new-skill.sh and
check-executables-allow-sync.sh. None ships via .pre-commit-hooks.yaml, so no
external consumer pins them, and all three are single-pipeline checks whose
verdict cannot change.
Left alone deliberately: 14 sites whose writer is a command, not a shell
builtin — they either absorb the writer's status with `|| true` or are python3
and awk, which write once at exit — and one file with no pipefail. `printf '%s'`
sites differ from a here-string only by a trailing newline, which no -q verdict
on a non-empty pattern depends on.
tests/test-no-pipefail-early-exit-grep.sh is a static guard against new
occurrences, discovered automatically by run-tests.sh. It only scans files that
set pipefail, joins continuation lines, skips comments, and flags only
echo/printf writers. Its first case proves the scanner can fail before its
second trusts a clean verdict on the tree.
A guard covers exactly the spellings its regex models, so the miss surface was
measured rather than assumed. Four were found and closed: pipefail declared as
`set -o errexit -o pipefail` (where the old pattern required pipefail to follow
the FIRST -o, and a file-level miss skips every site in that file); a writer
separated from grep by an intermediate stage; a pipeline wrapped on a trailing
`|` rather than a backslash; and readers spelled egrep, fgrep, /bin/grep,
`command grep` or with an env-var prefix. Segment characters exclude a bare `&`
so `echo ok && other | grep -q x`, whose writer is `other`, does not false-fire.
Widening surfaced 5 live sites invisible to the original scanner, all in
tests/test-apm-current-hook.sh, all `echo "$out" | json_field ... | grep -q`;
they are safe today only because json_field is python3, which reads to EOF and
writes once. Fixtures go 4 to 12 vulnerable spellings plus near-miss negatives.
Two `grep ... | head -1` sites (test-vale-wrap.sh) are the same race with a
different early-exiting reader, and are fixed by absorbing the writer. The
scanner deliberately does not model `head`, `sed -n 1p` or a bare `read`: most
legitimate uses in this tree are already absorbed with `|| true` and the scanner
cannot see absorption from pipeline text, so a high false-positive rate would be
how this guard gets weakened. Heredoc bodies are scanned as code; none in the
tree trips it today.
Impact
The bug predates the factory-audit merge: every converted site in
check-release-needed and case 21 dates to 4d018af and aa8cc22 (2026-08-09).
Test suites go 19 to 20. `run-tests.sh --strict` passes 20/20 with 0 skipped,
four consecutive runs.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YR2CjVumUbEGWcMikcoXBD
Why
The two audit skills carried 1,724 lines of byte-identical duplication: the ADR-0020 boundary
resolver (1,061), vale-wrap.sh (526), the Vale style rules (44) and the Contributing-files parser
(93). Nothing shared them — they were held in sync by a 413-line pre-push gate and its 797-line
test suite. Sync-by-gate had already failed once: at 484357a the two parser copies drifted into
different spellings of the bullet loop while a docstring asserted they were identical. That drift
was behaviour-neutral and was re-unified by hand at 598a7c3, so the copies were identical at merge
time — but nothing had caught it, and the next drift need not be neutral.
Implementation Notes
Self-containment binds BETWEEN skills, not within one. The agentskills.io spec forbids reaching
across skill directories, which is why two separate skills needed embedded copies; two files inside
ONE skill may source a third. That is the whole reason the merge removes duplication rather than
relocating it.
The union of both bodies measured 1,532 words against BODY_MAX_WORDS=900, and only 211 of those
words were shared, so SKILL.md is a dispatch body. Step 0 resolves the flow from the target path
before any validation, and its table mirrors validate.sh's detection exactly: a directory holding
SKILL.md or a SKILL.md file (skill); a *.agent.md, or a .md directly under an agents/ directory
(agent); anything else stops without running a validator. Steps 1-3 live in
references/skill-flow.md and references/agent-flow.md, and gotchas that apply to one flow live in
that flow's file, since it is loaded on every invocation anyway. If validate.sh reports on the
other artifact type, the body restarts at Step 0.
Named factory-audit rather than forge-audit because forge is a live skill, and a family prefix that
matches a live sibling reads as ownership rather than membership.
The description carries one arrow per boundary target, because ADR-0020 resolves only the first
target after an arrow. It drops the quoted "audit this skill"-style phrases, which restated
"audited" in a second register (ADR-0020's duplicate-register rule). 241 characters, Gotchas 16%
of the body: no size SUGGESTIONs.
The boundary resolver stays embedded in two files rather than imported: a cache-installed plugin
cannot read outside its own directory, and the repo-root hook resolves via .pre-commit-hooks.yaml
where entry[0] is the only token pre-commit rewrites, so no single file is reachable by both.
tests/test-adr0020-contract.sh hashes both copies for byte-identity, and asserts validate.sh sources
the resolver and that no third copy exists.
The entry scripts classify the target from its resolved parent directory, so a bare agent filename
typed inside agents/ works; resolve SCRIPT_DIR CDPATH-safely; and exit 2 when a lib-*.sh is
missing, rather than dying with exit 1, the tier the flows relay as real findings.
The provenance run functions stash their findings code in KYBERFORGE_PROV_RC and
return 0, so validate-provenance.sh calls them UNTESTED. Testing a function's
status (`f || RC=$?`) disables errexit for its entire body, and no subshell or
`set -e` inside can re-arm it once the call sits in a condition context
(measured, both spellings). Their error paths use `exit`, which is unaffected
either way; this keeps errexit armed for anything added later.
Case 0's readability guard reads the file instead of asking `[[ -r ]]`. `-r` is
access(2), which answers yes for uid 0 even on a mode-000 file, and this repo's
dev environment is root -- so the guard could never fire where it exists to fire.
A read attempt is also the stricter question, catching EIO. This is the reasoning
scripts/check-vale-style-sync.sh carried before this commit deleted it; the
hazard did not go with it.
All three entry scripts are CDPATH-safe, vale-wrap.sh included: both of its cd sites are cleared,
the --config resolution and the directory-mirror walk, where an exported CDPATH would otherwise
print a decoy path into the -print0 stream and build the mirror from the decoy's files. The two
remaining bare cd calls take absolute paths, which CDPATH is never consulted for.
Impact
BREAKING: skill-audit and agent-audit no longer exist as invocable skills. kyberforge goes to
2.0.0 (catalog 0.4.7).
Check logic is unchanged: differential runs of the old and new validators across every skill and
agent produced byte-identical stdout, stderr and exit codes, and the reconstructed Python payloads
differ only in comments and the references/field-inventory.md -> agent-field-inventory.md rename.
One doctrine governs the tiers: exit 0 is audited and clean, exit 1 is audited with findings OR a
target present but unreadable, exit 2 is that nothing was audited at all. Edge paths DID change,
deliberately (full table in ADR-0025):
- a missing target exits 2 (never ran), not 1, under its own "does not exist" message; detection is
by path shape, so a shape-matching path that is simply absent used to reach the validator and come
back as a FAIL against a file that never existed;
- an unshaped target exits 2 under the generic "matches neither" message, and a directory with no
SKILL.md under a third, distinct one -- three exit-2 messages, not one;
- a dangling symlink or a symlink loop stays exit 1: it is present but broken, which is a finding
about the artifact rather than a usage error;
- a SKILL.md file path is audited as its skill directory instead of refused;
- a .md agent outside an agents/ directory is refused rather than audited;
- a missing script library, a missing python3, a missing PyYAML, and no argument at all each exit 2.
validate-provenance.sh already exited 2 for the last two; validate.sh now matches it.
.pre-commit-hooks.yaml is a published contract consumed by external repos. Both hook IDs and both
files: regexes are unchanged; only entry: and description: moved.
scripts/check-vale-style-sync.sh (413), scripts/sync-vale-styles.sh (21),
tests/test-check-vale-style-sync.sh (797) and agent-audit/scripts/README.md (47) are deleted. The
checker made 17 assertions: 6 compared the two Vale copies and are moot; 10 are rehomed into
tests/test-vale-wrap.sh (case 0, cases 28-31, and the suite's Vale-absent skip); and the
cross-manifest files: agreement check, which selected hooks by entry: and so could not survive both
hooks sharing one, is ported as case 33 pairing hooks by id:. Cases 28, 30 and 33 carry mutation
self-tests; narrowing the local skill prefilter to 6 of 38 SKILL.md files now fails the suite.
Skills go 39 to 38. Pre-push goes 9 repo-authored hooks to 8.
ADR: 0025
BREAKING-CHANGE: the skill-audit and agent-audit skills are removed. Both flows are served by
factory-audit, which auto-detects whether it was handed a skill directory or an agent file.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YR2CjVumUbEGWcMikcoXBD
Why: three governance documents cited `docs/HUMANS.md`, which has never existed -- the human
practitioner rules live at `docs/wiki/HUMANS.md`. One of the three is
`core/instructions/governance.md`, which is `@`-imported into every session in every project,
so an agent following its "read it when making decisions not covered here" pointer hit a dead
path. That file was self-inconsistent: line 73 already cited the correct path while line 82 did
not.
Implementation notes: five occurrences corrected across three files --
`core/instructions/governance.md:82`, `docs/research/governance_principles/CONTROLS.md:5,101,106`,
and `docs/ai-constitution.md:238`. Text is otherwise untouched; this is a path correction only,
not a change to any governance rule. Marked the defect fixed in SIMPLIFICATION-AUDIT.md, which
recorded it in two places as outstanding.
Impact: no rule, gate or behaviour changes. The deployed copy at
`~/.claude/core/instructions/governance.md` no longer matches the repo and stays stale until
`scripts/install.sh` re-runs; it was byte-identical before this commit.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YR2CjVumUbEGWcMikcoXBD
Why: this audit was written read-only and its scope estimates proved systematically optimistic.
Ten open findings with claimed yield were re-verified against the files by independent agents.
One premise of ten survived, and the headline figure was wrong in at most eight of the ten.
Implementation notes: per-finding verification notes on 11, 16, 20, 22, 24, 27, 28, 33, 34 and
36. Finding 5 marked not proceeding, on the same grounds as finding 3 -- its six suites are
split by failure class, not ADR section, and five of the six headers name the incident they
guard. Finding 18 re-scoped and folded into finding 22 under three exemptions (audit criteria,
assets/templates and sourced spec restatement, the last now carrying a decidable test rather
than resting on the presence of source_keys). Finding 32 closed with its premise corrected.
Section 8 questions updated where measurement answered them: ADR-0012 is moot, git/gitea
granularity fails an enforced gate at 4.9x, and the external-consumer question has its evidence
but awaits a decision. New section 10 records the wave, the recurring failure mode behind six
wrong findings, and where the remaining opportunity actually sits.
The wave's own notes were then re-verified by a second independent round, and this commit
carries those corrections. The notes had an error rate comparable to the findings they
corrected. Four errors changed a verdict. Finding 11's note anchored its search at column 0 and
so missed every source_keys carrier nested under metadata:, producing "172 carriers" (196),
"zero of 40 SKILL.md files carry source_keys" (28 of 39) and "check 2 is dead code" (live, with
bats coverage); its double-counting accusation was a misreading of the word "plus" and is
withdrawn. Finding 28's note claimed 2,740 lines "has never matched any commit" -- it is exact
at a3e721e, the unique commit of the 67 touching docs/adr/ that yields it, and where all of the
finding's headline figures reproduce simultaneously; the finding went stale, it was not
fabricated. Finding 20's note argued the gitea split was blocked a fortiori by ADR-0011, which
inverts that ADR's reasoning (its objection is to a boundary being crossed, not to bundle size)
-- withdrawn and replaced with the same objection aimed at the correct seam, in the note and in
section 8. Finding 18's "sourced spec restatement" exemption collided with finding 20's own
salvage recommendation in the same commit and now carries a test that separates them.
Bookkeeping corrected throughout: the dangling docs/HUMANS.md path is five occurrences across
three files, not four (the sentence enumerated five while stating four); finding 16's c8a7c9e
chronology was inverted, and its resolver core is 549 executable lines, not 357, making it 2.7x
the proposed budget rather than 1.8x; finding 24's Q1-Q5 coverage is 20 tests and ~67%, not 24
and ~76%; finding 22's estimate is ~150-180 lines with its components summing, and its
RED/GREEN rebuttal no longer depends on ignoring the two diagrams the finding most plausibly
named; finding 27's preamble is 43 words; finding 28's proposal is a wash (+5 to -1) rather
than a firm +5; finding 32's citation is architecture.md:22 and its net is 6 lines. Section 10's
table reconciled against every corrected note.
Impact: no code, gate or behaviour changes. Two defects are flagged for independent fixing -- the
deployed core/instructions/governance.md cites docs/HUMANS.md, which does not exist, in five
places across three files; and apm update on this branch resolves against main and would restore
the obsidian MCP server removed in c96ca9c, via the regenerated repo-root .mcp.json, which is
gitignored and so would not appear in git status.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YR2CjVumUbEGWcMikcoXBD
Why: AGENTS.md's Structure bullet restated apm-install mechanics already owned by
docs/spec/architecture.md:24 and README.md:55, and docs/VISION.md carried stack, framework and
deployment choices for a product that lives in a separate repo.
Implementation notes: AGENTS.md keeps two actionable one-liners plus pointers to the README
layout table and architecture.md, preserving the session rule that .claude/skills/ and
.claude/agents/ are install output and must not be edited. VISION.md's Phase 1 Architecture
block becomes a one-line scope statement; the "Mobile/desktop (Phase 3)" line is dropped as an
intra-file duplicate of the Phase 3 section.
Impact: no behaviour change. README.md and docs/spec/architecture.md are untouched -- the
finding's premise was inflated, and architecture.md had already been differentiated in a way it
documents in the file itself.
Refs: SIMPLIFICATION-AUDIT.md finding 32
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YR2CjVumUbEGWcMikcoXBD
A five-agent review of today's seven commits found no executable
regressions and no dangling references, but a set of documents still
asserting, in present tense, machinery that ADR-0024 and its commits
removed. This corrects them in place, keeping the original text as the
historical record wherever the repo's amendment convention applies.
LESSONS.md: the 2026-06-21 entry prescribed a `claude plugin validate`
sweep that now fails on every plugin, so it is marked superseded with
the surviving gates named. The 2026-08-09 entry gained a recurrence
note: today's manifest deletion broke apm's MCP propagation exactly as
that lesson describes, and its prescribed repo-local grep could not
have caught it, because `plugin_parser.py` ships in the apm toolchain
installed outside this repository.
ADR-0019, ADR-0011 and ADR-0021: amendments extended to passages the
earlier correction passes stepped over -- a dead native-consumer guard,
Consequences bullets still calling for a `plugins/gitea/.mcp.json` that
must not be recreated, and a drift-gate list naming a deleted script.
ADR-0021's list is down to one gate, not two: `apm audit --ci` never
read `description` and was never a drift gate.
architecture.md and enrichments.md: the self-containment constraint is
restated on its live source, the agentskills.io APM package-mode spec,
rather than on Claude Code's plugin cache-install, which ADR-0024
consequence 6 pins as a superseded rationale. releasing.md's pointer to
the deleted sync script is rewritten as history.
tests/run-bats.sh and scripts/lib/batch-run.sh: comment-only. The
`.claude/skills/` exclusion comment claimed a duplication that is not
live yet; apm does not strip `tests/`, and the deployed tree is empty
of them only because the lockfile still resolves the six dependencies
to a pre-ADR-0024 commit carrying the flat mirror. The exclusion is
correct but forward-looking, and now says so.
SIMPLIFICATION-AUDIT.md: reconciled against what the commits actually
did. Two closed findings recorded conclusions that ADR-0024 reversed
hours later; findings 1, 3, 31 and 35 carried prescriptions voided the
same day; finding 28 is now recorded as having moved backwards, with
docs/adr/ measured at +336 lines over the day. The section 1 headline
table is re-measured at a6434e0 and labelled with its basis. The
ADR-0012 contradiction between finding 2b and section 8 is resolved in
2b's favour after reading the ADR: only finding 24 is governed by it.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YR2CjVumUbEGWcMikcoXBD
A six-agent review of today's commits found three documentation defects
that the apm-only change left behind. All three are records that describe
deleted machinery in the present tense; no code or gate is affected.
ADR-0021 was the one ADR that ADR-0024 invalidates without carrying an
amendment note -- 0001, 0006, 0011, 0013, 0014, 0015, 0017, 0018 and 0019
all got one. Its Context section still compiles a plugin description into
four generated files and its Consequences section still names eight, but
718c79a deleted the per-plugin manifest pairs and 0dffff3 deleted the
.github/plugin/marketplace.json mirror. One target survives. The decision
itself is untouched: the note marks the counts historical rather than
rewriting them, since the staleness hazard that motivated the ADR is
exactly what shrinking the blast radius does not fix.
That note also lands the one ADR-0021 has promised since it was written:
its Context section said "see the note below" about the codex profile's
removal and no such note has ever existed in the file.
SIMPLIFICATION-AUDIT.md's section 8 still asked whether Copilot reads the
legacy mirror path. Finding 2c answered that at 11:35 and 0dffff3 acted on
it; two later passes over section 8 (d2480b8, 061bb3d) each checked off a
different question and stepped over this one. Closed with the answer that
already shipped: Copilot's discovery falls through to .claude-plugin/,
so what the deletion cost is discovery-order preference, not consumability.
ADR-0020 cited plugins/bin/skills/zoom-out/SKILL.md:4 as end-to-end
verification evidence. That path is mirror, deleted by ADR-0024. The .apm/
source and the deployed copy both still carry the flag and the pass-through
still holds, so the citation is narrowed to the two live paths rather than
the finding being withdrawn.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YR2CjVumUbEGWcMikcoXBD
718c79a deleted every per-plugin .claude-plugin/plugin.json, which
reclassified each package from a marketplace plugin to a plain apm
package. That silently broke MCP propagation: apm_cli/deps/plugin_parser.py
maps a plugin-root .mcp.json into .apm/.mcp.json, and that code path runs
only for marketplace plugins. With no manifest, apm never reads the file.
Reproduced on ref-pinned consumer clones: at the parent commit a consumer
receives the obsidian server, at HEAD it receives none, and on upgrade apm
prints "Removed stale MCP server 'obsidian' from .mcp.json". This repo
consumes its own plugins through apm (ADR-0018), so the tracked root
.mcp.json would have been rewritten to an empty server map on the next
lock re-resolve -- silent tool loss plus unexplained working-tree drift.
The server is removed entirely rather than relocated to .apm/. It was
already a standing question (SIMPLIFICATION-AUDIT finding 37, deferred on
2026-09-13 pending confirmation, now confirmed), and plugins/bin/apm.yml
declares dependencies.mcp: [] -- apm's supported mechanism was never used.
All seven .mcp.json files go; the root one is apm-generated output and is
now gitignored alongside the other install artifacts.
ADR-0011's deferred ".mcp.json wiring gap" is moot twice over -- the
install route it blocked no longer exists and neither does the file --
and ADR-0018 records why it lost its only worked example of MCP
propagation. apm.lock.yaml still carries the server; it clears on the
first apm update after this reaches the default branch.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YR2CjVumUbEGWcMikcoXBD
A five-agent review of 718c79a and d2480b8 found no skill, agent or hook
regressions (39 skills before and after) and confirmed both hook removals
are genuinely moot -- verified against the tree, not taken on the commit's
word. It did find one functional regression (fixed separately) and this
documentation drift.
Counting errors, all from a git pathspec `*` crossing `/`:
- 17 .bats files shipped to consumers is really 10; 17 counted tracked
paths merely containing /tests/, one of them a template asset
- "roughly 88s off every push" is ~92.4s; 88 omitted validate-plugins
- "roughly 70% of each plugin remains live" holds only for kyberforge;
the real spread is 44.3% (bin) to 70.6%, now a table
- the pre-push enforcement row was half-corrected: 33 entries stood
unstruck (now 27) and 14 -> 11 switched counting basis mid-sentence
- the root .claude-plugin/plugin.json was described as "kept"; it has
never been tracked
gates.md said "Ten hooks" above a nine-row table (11 was decremented for
one removal, not two), and "both need the claude CLI" for one remaining
validator. Its pretty-format-json exclude rationale claimed six
alternations expanding to sixteen files in a passage headed "Mind which
number you are quoting" -- four alternations, two live files; the two
dead ones are dropped from the pattern. check-useless-excludes could not
catch this: it only flags an exclude matching nothing at all.
ADR-0024 cited ADR-0006 for a patch-bump rule it does not contain and
which ADR-0015 explicitly retired; stated apm's marketplace probe order
backwards (.claude-plugin/ is the last candidate, not the first, so the
earlier .github/plugin/ deletion only demoted resolution); undercounted
apm's skill-deploying targets as seven when there are fifteen; and never
recorded that validate-plugins was removed. The symlink hedge is resolved:
apm_cli/security/gate.py's ignore_non_content() drops symlinks silently on
deploy while apm_modules/ materialization dereferences them, so content
survives that far and vanishes at install. Accepted with no replacement
guard, per decision -- kyberforge/docs/hooks.md previously asserted a
guard that had been deleted with its script.
Four plugin READMEs still advertised `claude plugin install`; ADRs 0001,
0006, 0013, 0014, 0015 and 0019 described deleted machinery in the present
tense, 0019 most consequentially as the live justification for the
SessionStart hook's .apm/ path. CONTEXT.md's "apm package" entry forbade
"plugin" while using it in its own body, and "Output profile" lost the
antecedent for "one catalogue serves both".
run-tests.sh gains the .claude/skills/ exclusion run-bats.sh already had.
Latent today -- no test-*.sh lives under any .apm/skills/*/tests/ -- but
apm now deploys those directories, so one would be discovered twice.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YR2CjVumUbEGWcMikcoXBD
Marks §8's install-path question answered and finding 7 superseded-then-done
(deleted rather than shrunk, since the mirror it guarded is gone). Corrects
two stale figures: the mirror was 213 files / 20,061 lines, not 263 / ~22,000,
and the pre-push stage now reports 11 hooks, not 14.
Adds §9 for what the decision carries forward rather than resolves: the two
accepted residuals, the self-containment negative result (findings 14/15 still
need skill merges, not file sharing), and the now-unreported symlink drop.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YR2CjVumUbEGWcMikcoXBD
apm becomes the only supported install path. The flat mirror at each plugin
root existed solely so Claude Code's native `claude plugin install` could
convention-scan plugin content (ADR-0017). With no native consumers, it cost
~20,000 tracked lines plus ~2,100 lines of sync tooling and ~88s of every
push to guard content apm never reads — and its only automated gate,
`claude plugin validate --strict`, passes on a plugin with zero content, so
it could not detect the defect ADR-0017 was created to fix.
Removes the mirror (213 files), the six per-plugin manifest pairs,
sync-plugin-content.sh, its 1,289-line test, the orphaned
marketplace-plugins.sh, and the check-plugin-content-sync and
validate-plugins pre-push hooks. The root `marketplace:` block and
.claude-plugin/ catalogue stay: apm's own marketplace consumers read that
same file, so `<name>@holocron` short names keep working.
tests/run-bats.sh now excludes .claude/skills/. apm installs from .apm/,
which carries the tests/ dirs the mirror stripped, so deployed .bats files
would otherwise be discovered and double-run.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YR2CjVumUbEGWcMikcoXBD
apm-marketplace-check checked network reachability of remote marketplace
refs, but finding 35 already removed the only remote package entry, so
every marketplace.packages[] source is local and the hook is pure
overlap with apm-pack-check-clean. apm-audit-ci was re-examined and kept
as-is -- its pre-commit-config.yaml comment already carries a dated,
verified justification the audit had missed.
check-marketplace-mirror-sync guarded .github/plugin/marketplace.json
against drift from .claude-plugin/marketplace.json. Verified against
current GitHub Copilot CLI docs: Copilot's marketplace discovery already
falls back through .github/plugin/marketplace.json to
.claude-plugin/marketplace.json, which this repo generates anyway -- the
dedicated mirror bought a discovery-order preference, not a capability.
Deleted the mirror file, its sync script, its test, and the hook.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YR2CjVumUbEGWcMikcoXBD
README and AGENTS.md each fully restated the "edit .apm/, never the
mirror" rule and the apm.lock/SessionStart mechanism instead of linking
to their canonical sources (architecture.md, ADR-0019).
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YR2CjVumUbEGWcMikcoXBD
Three findings from the simplification audit were independently
re-verified before execution, corrected, then implemented by
subagents:
- Finding 2 (check-executables-allow-sync): the audit's "drop it"
option was found unsafe (ADR-0019 calls this failure mode silent,
not "visible and recoverable" as claimed); shrunk instead of
deleted, 231 -> 222 lines.
- Finding 31 (CONTEXT.md): "most terms unused by skills" was found
overstated (13 of 28 are model-facing must-keeps); cut only the
9 confirmed true orphans, 28 -> 19 terms. Also de-referenced one
dangling pointer to a deleted term in the Flagged-ambiguities
section.
- Finding 38 (pc-author/pc-run): line count was found overstated
(598 actual vs. 689 claimed); trimmed the two generic reference
files by 60 lines while preserving house-specific content.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YR2CjVumUbEGWcMikcoXBD
Corrected scope for audit finding 31: the audit's claim that most of
CONTEXT.md's 28 terms are unused was overstated (13 are must-keep,
referenced in model-facing skill references/*.md files). This cuts only
the 9 confirmed true orphans, independently re-verified by grep across
plugins/*/.apm/, docs/, scripts/, and tests/ with zero hits outside
CONTEXT.md (two had a single incidental ADR mention that doesn't
constitute a dependency): Content mirror, apm-consumed install, Vale
audit prefilter, Vacuous green, Management Application, Sycophancy,
HOTL, Preload tax, Skill context contract.
Term count: 28 -> 19. Also removed two Relationships bullets that
existed solely to relate now-deleted terms (Preload tax/Skill context
contract, and HITL/HOTL/Sycophancy), leaving HITL's own entry to stand
alone. The Preload tax entry's self-contradiction (quoting two dated
character counts immediately after saying not to quote either) is
moot since the whole entry is removed. Example dialogue and flagged
ambiguities sections left untouched per scope, including one now-stale
bold reference to "Preload tax" in flagged ambiguities.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YR2CjVumUbEGWcMikcoXBD
Trim the two comment blocks that re-derived ADR-0019's argument in full
(apm's exact-dict-lookup key matching, and why the PyYAML fallback is not
a hard requirement) down to a short summary plus a pointer at ADR-0019,
which already carries that reasoning verbatim. 231 -> 222 lines.
The hook is kept, not deleted, per the audit's own corrected scope: the
"or drop it" option in SIMPLIFICATION-AUDIT.md finding #2 is off the
table because ADR-0019's Consequences section and the script's own
header both call this failure mode silent, and the ADR says a
silent-staleness failure here is strictly worse than the duplication
this repo's other gates catch.
The dual-reader design (PyYAML preferred, hand-rolled shape-scan
fallback) is also kept as-is: it exists specifically so a missing
python3/PyYAML can't silently skip the check or block every push, which
is exactly the loud-failure guarantee this finding must not weaken. No
genuine redundancy was found in the parsing logic, the per-branch
Why/Fix error messages (each tied to a specific test), or the test
matrix (which verifies the two readers agree across every failure mode)
without cutting something load-bearing -- so those are untouched, and
tests/test-check-executables-allow-sync.sh needed no changes since
script behavior and output are byte-identical.
All 23 tests in tests/test-check-executables-allow-sync.sh pass, and
`pre-commit run check-executables-allow-sync --all-files --hook-stage
pre-push` passes against the real repo state.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YR2CjVumUbEGWcMikcoXBD
Collapse the per-language hook tables in pc-author's hooks-by-language.md
into one shared-repo table plus an "other repos" table, dropping the
repeated repo/rev/rationale text that just restated what each hook does.
128 -> 92 lines. Kept both "Unverified — not in research corpus" flags
and the rev-freshness caveat.
Remove the generic SSH/proxy CI failure sections, the shellcheck SC-code
listing, and compress the generic validate-config schema-error bullets
in pc-run's failure-patterns.md, all of which just restated
pre-commit.com's own docs. 133 -> 109 lines. Kept the rtk-prefixed
re-stage/recommit fix (ADR-0023), the "do NOT reach for
`pre-commit install -f`" warning, and both gitleaks/shellcheck
not-sourced-from-corpus notes.
Combined cut: 60 lines. Flat mirrors regenerated via
scripts/sync-plugin-content.sh and verified byte-identical
(--check exits 0); no plugin.json drift.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YR2CjVumUbEGWcMikcoXBD
The Context section still described .agents/plugins/marketplace.json
(apm's codex profile) as an existing, unaffected generated file. It
was removed today in 568ca74; point to the removal instead of leaving
the text describing a file that no longer exists.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YR2CjVumUbEGWcMikcoXBD
Neither has a real consumer: Codex is not a supported target, and
mattpocock-skills was the sole remote marketplace.packages[] entry
forcing apm-marketplace-check and apm-pack-check-clean to git
ls-remote on every push. Removing both drops .agents/plugins/marketplace.json
(the codex output artifact) and makes every pre-push hook resolve
fully offline. Updates README, AGENTS.md, gates.md, architecture.md,
and ADR-0015/ADR-0021 to match.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YR2CjVumUbEGWcMikcoXBD
Cut the "Verified fixed" reproduction paragraph carrying explicitly
stale pre-retrofit figures, and condensed the "Current retrofit
status" section's issue-#99 process narrative to the current-state
facts and the commands to check them.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YR2CjVumUbEGWcMikcoXBD
f11b645 and bde9f7f swept check-manifests and skill-frontmatter
references left over from e647f14 and c8a7c9e, but missed two worked
examples in this vendored pre-commit research doc: a "Validate a
generated file" hook naming the deleted scripts/check-manifests.sh,
and a "SKILL.md frontmatter validation (inline bash, as used in this
repo)" example for the skill-frontmatter hook, folded into
skill-size-check.sh in c8a7c9e.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YR2CjVumUbEGWcMikcoXBD
c8a7c9e folded the skill-frontmatter hook into skill-size-check but
left validate.sh's FAIL messages, skill-author's create.md, and the
skill template's frontmatter comment naming the deleted hook as the
enforcer -- misleading for anyone tracing a FAIL back to the gate that
raises it.
f11b645 swept check-manifests references but missed a hand-authored
"Local hooks in this repo" table in the git plugin's vendored
pre-commit research doc, which still listed both check-manifests and
skill-frontmatter as active hooks.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YR2CjVumUbEGWcMikcoXBD
e647f14 deleted scripts/check-manifests.sh but missed three live files
that still named it: the apm-marketplace-check hook description in
.pre-commit-config.yaml, and comments in sync-plugin-content.sh and
lib/marketplace-plugins.sh explaining design decisions by pointing at
a script that no longer exists.
Also deletes list_marketplace_remote_plugin_names from
marketplace-plugins.sh — its only caller was check-manifests.sh, so
it's been dead code since that commit.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YR2CjVumUbEGWcMikcoXBD
test-governance-layer.sh and test-instructions-and-docs.sh (583 lines
combined) grep markdown files for expected phrases, including a
one-shot "issue 0015 refactor incomplete" assertion made permanent and
an assertion that docs/notes/ exists. Neither is referenced by any
other script or doc.
check-apm-agents-valid.sh is left untouched — it is tied to the
separate, out-of-scope skill-merge finding 14.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YR2CjVumUbEGWcMikcoXBD
skill-frontmatter was a 62-line bash script inlined in
.pre-commit-config.yaml, re-parsing SKILL.md frontmatter with grep and
awk to check for name/description/metadata.version fields.
skill-size-check.sh already parses the same frontmatter block with
PyYAML for its ADR-0020 checks, so the two checks belonged in one
script.
Adds a ~20-line required-frontmatter check (name, description,
metadata.version as three-part semver) to scripts/skill-size-check.sh.
Removes the inline skill-frontmatter hook from .pre-commit-config.yaml
and deletes tests/test-skill-frontmatter.sh (366 lines). Removes the
79-line "the other hook on that scope" discussion from
docs/spec/gates.md and its now-dangling cross-reference, replacing
both with a one-line note of the fold, and updates the pre-push hook
counts there.
Updates fixture builders in test-skill-size-check.sh,
test-adr0020-body-checks.sh, test-adr0020-targets.sh,
test-adr0020-differential.sh, and test-vale-hooks-consumer.sh to carry
valid metadata.version so the new check doesn't spuriously fail
existing fixtures that predate it.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YR2CjVumUbEGWcMikcoXBD
Six pre-push hooks were validating overlapping sets of the same
manifests. check-manifests (marketplace.json/plugin.json path checks)
is redundant with validate-plugins (claude plugin validate) and
apm-pack-check-clean, which already cover the same ground.
Deletes the check-manifests hook entry, scripts/check-manifests.sh
(282 lines), and tests/test-check-manifests.sh (771 lines).
scripts/lib/marketplace-plugins.sh is kept — it is still sourced by
sync-plugin-content.sh. Updates the now-stale check-manifests.sh
mentions and hook counts in README.md and docs/spec/gates.md.
The apm-audit-ci and apm-marketplace-check hooks named in the same
finding are left untouched — the audit flags them as needing a
separate decision.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YR2CjVumUbEGWcMikcoXBD
Finding 13: five blocks of near-identical wording were repeated across
skills within a plugin — the gitea "resolve owner and repo" step (5
skills), the 404-masks-403 note (6 files), the manual pagination
explanation (8 files), the git plugin's main/master force-push refusal
(7 files, some with multiple internal restatements), and the bin
skills' domain-glossary/ADR paragraph (5 skills). Tightened each
instance in place — same meaning, fewer words — rather than extracting
to a shared file, which ADR-0014's one-file-per-skill install
constraint rules out. Left the three git skills' structured-result
JSON shapes alone (coupled to the separate, out-of-scope git-orchestrate
merge candidate, finding 19).
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YR2CjVumUbEGWcMikcoXBD
Finding 21: `config.example.json` (and the never-tracked
`.claude/plugins/git/config.json` it documented) was read by
git-orchestrate and git-branches but written by nothing, and the
default-inference fallback (GitHub Flow, with Gitflow inferred from a
`develop`/`release/*` branch) already covered the no-config case.
Removed the config-read step from both, updated git-workflow's
description of the orchestrator to match, dropped the now-dangling
`applied_config` field from git-orchestrate's output shape, and
deleted the config file and its stale example reference in
docs/spec/architecture.md.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YR2CjVumUbEGWcMikcoXBD
Checkbox and strikethrough findings 10, 12, and 30, each pointing at
the commit that implemented it (edcc57c, 629320b). Record the decision
on findings 9 and 26 (delete docs/research and docs/notes): declined,
those docs are kept on purpose as context for work sourced from them.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YR2CjVumUbEGWcMikcoXBD
Finding 30 of the simplification audit. 41 entries had grown to 255
lines; 10 described a write-skill / write-eval bootstrap workflow
whose skills no longer exist in this repo, and the longest entries ran
200-550 words of incident narrative for a one-line lesson.
Deleted the 10 stale entries. Kept 3 same-dated ones (RLHF defaults,
secrets-rule gap, HITL gap) whose content is unrelated to the defunct
workflow and still applies. Removed one open-work entry ("neither part
landed", about CONTEXT.md not being @import-ed at session start)
rather than filing it as a tracker issue -- not turned into an issue,
just dropped; the audit's own commit history and this repo's session
transcript carry the detail if it's wanted later. Compressed the
remaining 30 entries to roughly 60-90 words each.
255 -> 131 lines, 41 -> 30 entries.
Refs: SIMPLIFICATION-AUDIT.md finding 30
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YR2CjVumUbEGWcMikcoXBD
Two related simplification-audit findings, bundled because they edit
some of the same skill-audit files and splitting would fragment
single-file diffs.
Finding 10: delete 48 per-skill/reference README.md files (they
restated SKILL.md in narrative form and no agent ever loads them) plus
2 scaffold templates. Drop the README criterion from skill-audit's
file-structure.md and finding-criteria.md, and the README-generation
step from skill-author's new-skill.sh; update new-skill.bats to match.
Plugin-root READMEs are kept intentionally, out of scope.
Finding 12: strip historical ADR-0020/ADR-0023 citations and
changelog-style narration from model-facing skill content across
kyberforge and git plugin skills. Delete skill-author's one-time
retrofit.md migration guide and its references. Some ADR-0023 tags
were not narration but check-rtk-prefix's required opt-out marker for
intentionally-bare git commands -- those were restored, not stripped.
Mirror re-synced and full pre-commit/pre-push suite verified green.
Refs: SIMPLIFICATION-AUDIT.md findings 10, 12
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YR2CjVumUbEGWcMikcoXBD