Commit Graph

496 Commits

Author SHA1 Message Date
c232e69645 fix(gates): block a dangling /name route with no preceding verb
The header promised explicit route notation always blocks. It did not: /name
reached extraction only behind a ROUTE_VERB, so a target with no verb before it
was never extracted at all -- exit 0, no output. Taking the SUGGESTION's own
advice ('write it as /name and it will be checked properly') was the one edit
that blinded the gate.

Adds two notation sweeps gated on BOUNDARY_MARKER and routed through _add, plus
a path guard so file paths and URLs are not read as routes. Also excises the
matched pointer span before the REFERENCE_PAST sweep, so a reference file can no
longer exempt itself by its own filename, and guards the agent branch with the
isfile test the skills branch already had.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EJJrm5YmacbwMdzZpXcoti
2026-08-31 19:45:45 +00:00
5b80f305e9 test(kyberforge): pin the provenance checker's three new behaviours
27a7669 changed validate-provenance.sh in three ways and tested none of
them: the bullet-form parser returning None rather than [] on unparsable
input, the source_keys: [] house-authored declaration, and the stripping
of section annotations off a Research doc path.

All three are checks that previously failed by staying silent, which is
the failure mode this repo keeps rediscovering. Shipping them untested
left nothing to catch a regression back to silence.

The 22 cases assert consequences rather than return values -- that check
8 runs on an unparsable block, that an unresolvable path emits an INFO
naming its slug -- and each was verified against a deliberate mutation
of the behaviour it covers.

Addresses #111.
2026-08-31 08:28:25 +00:00
8599b4a058 chore(release): bump the marketplace version to 0.5.0
0959291 bumped all six plugin versions for the ADR-0020 retrofit but left the
marketplace itself at 0.4.5, so consumers resolving against the marketplace
entry would see no change despite every packaged plugin having moved.
2026-08-31 08:02:49 +00:00
ee248ff5a5 docs: describe the boundary check's new outcomes and de-pin stale counts
The gate spec still described the routing-target check as pass/fail against a
single-arrow clause. It now documents the three outcomes, the hand-invocation
carve-out and the one-arrow rule, so a contributor hitting a SUGGESTION can
tell whether it is a real defect or accepted phrasing.

Both files cited skill counts and source line numbers that go stale on the
next edit and were already wrong; those citations are removed rather than
refreshed.
2026-08-31 08:02:44 +00:00
131b89733b fix(apm-orchestrate): correct the marketplace add-package direction and trim duplication
`apm marketplace package add` rejects a local path — it registers a remote
package reference — but the agent routed local-package registration to it, so
that dispatch could only fail. Local registration is a manifest edit and now
routes to `edit-config`.

The agent also carried its own copy of the `type:` guidance and a numbered
workflow that restated the dispatch procedure below it; both drift from
apm-workflow independently. `type:` correctness is delegated where it belongs,
and the description is rewritten to a trigger plus the apm-install boundary
rather than a restatement of the body.

Addresses #120.
2026-08-31 08:02:38 +00:00
dac9cad912 refactor(kyberforge): move audit guidance out of the per-run rubric load
skill-audit loaded roughly 4,268 words of rubric on every run, most of it
criteria for findings a clean skill never triggers. The auditing guidance moves
into finding-criteria.md, read only when a finding is actually raised, cutting
a clean audit to about 999 words. The named-skill exemption is replaced with
properties, so the rubric stops carrying a list that ages the moment a skill
is renamed.

apm-workflow's `type:` trap sat in one flow while biting several, so it is
promoted to a common gate reachable from all of them; its claim to be
self-contained was untrue once it started routing to apm-install. skill-author's
contract had drifted from body-discipline.md and is realigned, and agent-audit's
field inventory is brought in line with the same split.
2026-08-31 08:02:19 +00:00
e869912374 docs(bin): give every bin skill a README
The bin skills were the only plugin without per-skill READMEs, so a reader
had to open SKILL.md — an agent-facing contract, not an explainer — to learn
what a skill does and when it fires. Each README states purpose, triggers and
boundaries for a human audience, leaving SKILL.md free to stay terse.
2026-08-31 08:02:09 +00:00
03abcffb77 refactor(bin): cut per-invocation load and repair broken skill references
diagnose read its feedback-loops reference unconditionally, so every
invocation paid for guidance most runs never used; the read is conditional
again and the per-invocation cost drops from 1,278 to 831 words. Its HITL
template moves to assets/ because it is copied out, not read as reference.
prototype's two branch flows move into references/ for the same reason —
only one branch is ever taken.

research could not search the codebase it was asked to research without Grep
and Glob. caveman's description had grown into a paragraph where one sentence
carries the trigger. Four references pointed at things that do not exist: a
to-prd skill, a /setup-matt-pocock-skills command, two cross-skill ../ links
that only resolve in the source tree, and two places calling this project's
Gitea host GitHub.

Addresses #114.
2026-08-31 08:02:03 +00:00
1c3af75642 fix(core): make --no-import-syntax actually change the adapter validation
Both branches of the flag reduced to the same expression, so the option was
inert: a caller who asked for the no-import form got the import-form check
anyway and a passing result that meant nothing.

Two further defects in the same validator: --max-lines failed silently when
given a value it could not use, and the Fix text told the agent to edit
AGENTS.md when the offending content is the provider adapter's. The boundary
clauses now name the operation being routed rather than the file type, which
was ambiguous where both skills touch the same file.

Addresses #115.
2026-08-31 08:01:45 +00:00
14af50bc07 fix(git): scope git-workflow's trigger to ambiguity rather than to its domains
git-workflow's description enumerated the six domains it exists to route away
from, so it competed for selection with the very skills it should be handing
off to. It now triggers on the case it actually serves: an interactive request
whose domain is not yet clear.

Also clears frontmatter drift across the plugin and removes duplicated guidance
in pc-run that had diverged from its reference.
2026-08-31 08:01:39 +00:00
8680adf4c0 fix(gitea): make gitea-releases executable and correct misleading domain claims
gitea-releases was the weakest skill in the plugin: no allowed-tools, no
owner/repo resolution, and a checkbox list where a dispatch table belongs, so
an agent reaching it had to guess both its permissions and its inputs. The
id-vs-tag_name trap — deleting by tag name where the API wants the numeric id —
is restored as an explicit Gotcha because it destroys the wrong release
silently.

Elsewhere the `exclusive` flag was documented on the wrong side of the
read/write split, and label data from one instance was presented as though it
were universal, which invites an agent to assume a taxonomy that does not
exist on the target repo. rename_branch was missing from the branch surface.
Reference prose and fences are cleaned up in passing.
2026-08-31 08:01:33 +00:00
b07d54ad7a fix(lint): correct four Vale behaviours the skills described wrongly
Each of these would send a user down a path Vale does not support:

Core options placed under a glob header are not scoped to that glob — Vale
rejects them with E201, so the guidance to nest them produced a config that
will not load. The built-in `Vale` style is compiled in, but Vale still
requires StylesPath to exist on disk before it will run, so the "no StylesPath
needed" shortcut fails. The MDX guidance was inverted: under `[formats]
mdx = md` the mapping is what makes MDX lint at all, and it needs the mdx2vast
prerequisite that was never mentioned. And a spelling rule's `ignore` paths
resolve against StylesPath, not against the rule file's own directory, so the
documented relative paths silently matched nothing.
2026-08-31 08:01:26 +00:00
27a76692b0 fix(kyberforge): stop the provenance checker skipping check 8 on unparsed input
484357a taught the Contributing parser the bullet form, but a block it still
could not parse returned the same empty result as an explicit "(none)", so the
checker read "no contributing files" and skipped check 8 rather than reporting
that it could not tell. Checks 7 and 8 were consequently dead across the whole
git plugin without anything failing.

The parser now distinguishes "declared none" from "could not parse", which
wakes both checks. Because the parser is duplicated between the skill-audit and
agent-audit copies, it is fenced with BEGIN/END markers and a test hashes the
two regions so the copies cannot drift apart again silently.

Addresses #111.
2026-08-31 08:01:18 +00:00
db5a426416 fix(gates): make the ADR-0020 boundary check parse what skills actually write
The routing-target check understood only a single-arrow clause naming a bare
skill, so most real boundary prose was silently skipped rather than verified.
Two of those silences were fail-open: an unrecognised token following a target
dropped that target from the check entirely, and a skill directory with no
SKILL.md still resolved as a valid routing target, so a broken route passed.

Multi-target arrow clauses now draw a SUGGESTION instead of being ignored,
hand-invocation phrasing is carved out so it is not read as a route, and a
dotted filename parses into a new `unparsed` status rather than disappearing.
Three test fixtures had been relying on the SKILL.md-less directory resolving
as a target; they are corrected alongside the check.

Addresses #107, #108, #110.
2026-08-31 08:01:07 +00:00
095929142f chore(release): bump all six plugin versions for the ADR-0020 retrofit
No wave on this branch bumped a version across 56 commits, though
apm-workflow's own configure.md states the policy: bump a package's
apm.yml version: whenever anything reaching its compiled output changes.
All six local packages have substantive .apm/ edits here.

Minor rather than patch. The retrofit rewrote every skill description,
which is the routing surface a caller matches against, and redistributed
bodies into references/. Behaviour is preserved but discovery changes, so
this is more than a fix.

The same file is explicit that versions are per package — editing
plugins/foo/.apm/ never bumps plugins/bar — so this is six independent
bumps that happen to land together, not one release number. Under the
per_package strategy the catalog carries a second copy in
marketplace.packages[], and apm-marketplace-check fails the push when the
two disagree; it cannot see a bump skipped in both, which is the state the
branch was in.

executables.allow is version-pinned by apm's design and moves with
kyberforge, or the SessionStart hook silently stops deploying (ADR-0019).
The comment above that block predicted exactly this; the pre-push suite
caught it when the first bump orphaned the key.

Refs #99
2026-08-30 21:00:58 +00:00
c7c9311d80 docs(gates): refresh the retrofit status to measured state
docs/spec/gates.md still described both ADR-0020 gates as "currently red"
and tabled the pre-retrofit figures: 26 of 39 descriptions and 9 of 39
bodies over their FAIL tier, 2 dangling targets, 58 SUGGESTIONs, and 10
Kyberforge.CompositionNote errors across four gitea-* skills. Measured
now: 0, 0, 0, 33 and 0.

The branch correctly left ADR-0020 itself untouched, since it self-pins
every citation to base commit f9b919d. gates.md carries no such pin, and
AGENTS.md names it three times as the authoritative reasoning layer — so
the shallow doc and the deep doc it defers to asserted opposite facts
about the same two gates, with the stale one telling a reader that an
unrelated one-line fix to a skill is blocked pending a retrofit that is
already done.

Also corrects the apm-orchestrate agent body figure, which drifted from
1,080 to 1,113 across this branch. Its point is that the numbers are live
evidence for leaving that hook's files: pattern alone, so a reader who
re-measures and gets a third value loses the argument.

AGENTS.md gains the second cause of the references/ blind spot: besides
the Kyberforge style being scoped [**/SKILL.md], the
vale-audit-prefilter-skill hook filters on a SKILL.md-only files: pattern,
so widening .vale.ini alone would change nothing. It also no longer
implies the kyberforge wave was the end of the work.

Refs #99 #117
2026-08-30 20:52:06 +00:00
484357a3b9 fix(gates): parse the bullet form of Contributing files
validate-provenance.sh matched Contributing files only as a single inline
line beginning "- **Contributing files:**". Seven skills write it as a
bare "**Contributing files:**" heading above a bullet list, so
parse_contributing_files returned None and checks 4 (contributing file
exists) and 5 (bidirectional source_keys) silently verified nothing on
git-branches, git-remotes, git-submodules, git-workflow, git-worktrees,
gitea-files and gitea-releases.

Those are among the skills this branch changed most — git-branches alone
gained five reference files — and the retrofit's mandatory sources.md
collateral went in unchecked. Demonstrated rather than argued: planting a
nonexistent contributing path in git-remotes yields 0 findings under the
old parser and 1 FAIL under the new one.

Both forms are now accepted. The bullet form is parsed per bullet rather
than by splitting a joined value, because its per-file notes contain
commas that would otherwise be read as path separators. The return type
becomes a list of note-stripped paths, with "(none)" as an empty list and
an absent entry as None, so the two callers no longer re-split a string.

Applied to agent-audit's copy as well. No agent ships a sources.md today,
so it is latent there, but it is the same defect.

This is a third gate blind spot alongside #117 and #118, and was unfiled.
One real defect surfaced immediately and is fixed separately.

Refs #99
2026-08-30 20:51:56 +00:00
164948a0bc fix(apm-workflow): type: selects processing, it does not validate content
configure.md said apm.yml's `type:` field "constrains what .apm/ may
contain" and that changing it later "does not retroactively validate what
is already on disk" — both implying a validation step that does not exist.
Read against the installed apm-cli 0.28.0: PackageContentType controls how
a package is processed during install/compile, apm_package.py only
enum-checks the declared string, and validate_apm_package() branches on the
structural type derived from files on disk, never on the declared field.
There is no content-vs-type mismatch check anywhere.

The hazard is therefore the opposite of what the wording primed for:
silent omission. A package declaring type: instructions while shipping
.apm/skills/ installs no skill and compiles AGENTS.md only, exits 0, and
reports success having shipped none of its primitives. The rule is now to
verify deployed output rather than the exit code. apm-orchestrate carried
the same wording as a Hard Rule and is corrected in step; its separate
defects stay with #120.

Also refreshes the exemplar figures this branch had re-staled. 264a5db set
them to 3,222 words of references; 6cb47f8 then added 63 words and
invalidated them, and the correction above adds more. Re-measured after
all edits: body 237 and whole-file 304 both still hold, references total
3,416. body-discipline.md's "roughly 3,200" moves with it.

ADR-0020 is deliberately untouched — it self-pins its citations to
f9b919d — as is the git-commits negative example pinned to 5e23250.

Refs #99
2026-08-30 20:51:45 +00:00
ca744d5d6c fix(vale-config): correct the missing-style failure mode
Gotcha 1 said a style in BasedOnStyles that is not built-in and not
already under StylesPath "finds nothing until vale sync fetches it — a
clean run is not proof anything linted". Reproduced against vale 3.15.2:
that case is a hard `E100 [loadStyles] style '<name>' does not exist on
StylesPath`, exit 2. Nothing is linted and nothing is silent.

Worse, the same commit deleted the Gotcha that was the actual diagnostic —
that Packages and BasedOnStyles are separate keys and a style lints only
once it is in both. So the surviving rule sent a reader staring at E100 to
run `vale sync`, which fetches only what Packages declares and reports
"Synced 0 package(s)" against a BasedOnStyles-only name. The remediation
loop did not terminate. Reproduced end to end.

The genuinely silent case is the reverse — declared in Packages and
synced, but absent from BasedOnStyles — and it is now the one labelled as
such. Testing also turned up that StylesPath must exist as a directory
even when Vale is the only style (E201, exit 2), which was documented
nowhere.

references/configuration-reference.md gains a seven-row resolution matrix,
each row backed by a fixture. Its Frontmatter Scopes section claimed
provenance from the vale.sh research corpus, which contains no frontmatter
material at all; it is house-verified and now says so under its own slug.

Issue #99's wave-3 comment recorded this defect as found and repaired. It
was not — the file was byte-identical to the commit that introduced it, so
nothing here was treated as already correct.

Refs #99
2026-08-30 20:51:32 +00:00
d2da45f78c fix(gitea-releases): restore the prerelease imperative to the create path
The retrofit left only a descriptive sentence on the loaded path — "Gitea
never infers a prerelease from a -beta/-rc tag name" — and moved the
imperative into references/conventions.md behind a trigger listing semver
naming, release-notes sourcing and release-to-tag relationships. Draft and
prerelease are not in that list, and "cut a v2.0.0-beta.1" is exactly the
request where the caller does not raise the topic, so the rule was
unreachable from the flow that needs it.

Severity comes from the repair path: the MCP surface has create, get,
get_latest, list and delete only — there is no update or edit tool. A
release published without is_pre_release can only be corrected by
delete_release plus a fresh create, and get_latest_release points
consumers at the beta meanwhile. Neither SKILL.md nor call-signatures.md
said so anywhere.

The flags are now set explicitly on every create, the missing update tool
and its delete-and-recreate consequence are stated in the body, and the
semver pass-through rule stranded behind the same trigger is promoted
alongside it. call-signatures.md now cites the deployed tool description
as direct evidence that get_latest_release excludes drafts, while keeping
the prerelease half hedged — that remains unconfirmed.

Verified live against gitea-mcp v1.7.0, read-only calls.

Refs #99
2026-08-30 20:51:22 +00:00
8982ac58b7 fix(gitea-labels-milestones): read exclusive per label, never infer it
SKILL.md called `exclusive` "an org-labels-only flag" and concluded that
applying a Kind/*, Priority/* or Status/* label "must replace the one
already there, not stack on it". Live `list_repo_labels` on this repo
returns `exclusive` on every REPO label: all seven Kind/* plus
Compat/Breaking are false, while Priority/*, Reviewed/* and Status/* are
true. So the field is not org-only, and the replace rule would strip a
valid Kind/* label — a destructive write from a false premise.

The nuance kept: label_write's `exclusive` parameter genuinely is
annotated org-only, so that row was schema-accurate. The error was
generalising a write-parameter restriction into a claim about where the
field exists. The row is qualified rather than deleted.

The rule is now per-label: where exclusive is true the server drops the
sibling itself, so do not pre-remove; where it is false the label is
legitimately stackable.

Also fixes the org-label fallback, which treated any list_org_labels
failure as proof the owner is a user account with no org pool and said so
was "an answer, not an error to report". Under the token scopes this skill
declares the call fails with required=[read:organization] before any
org-vs-user determination is made, so a capability gap was being reported
as an absent label. Scope errors are now distinguished and reported.

Verified live against gitea-mcp v1.7.0, read-only calls.

Refs #99
2026-08-30 20:51:12 +00:00
dd1981db80 fix(gitea-issues): list_issues does have type and milestones on v1.7.0
SKILL.md's headline Gotcha said `list_issues` "has no `type` filter", and
references/issues.md stated in bold that neither `type` nor `milestones`
exists, "despite both appearing in api-reference.md". Both parameters are
present on the deployed gitea-mcp v1.7.0 and both work: `type: "issues"`
returns only issues, `type: "pulls"` only PRs, and `milestones` filters by
name. Unfiltered, the same window returns them interleaved, so the mixing
the Gotcha describes is real — only the stated remedy was wrong.

This mattered most in gitea-workflow's no-args check-in, which lists open
issues through this skill and so reported PRs under "Open Issues" while
the skill forbade the one-parameter fix. The list flow now passes
`type: "issues"`.

references/sources.md recorded the absence as a live-verification win over
stale research docs; it now records that the earlier check was superseded
by v1.7.0, since drift runs in both directions. gitea-prs cited the same
parameter as its canonical drift example and no longer does — no
replacement example was substituted, because the obvious candidate was not
verified in this pass.

Also defaults label writes to `add_labels`: `replace_labels` clears every
label not in the array, and per-label exclusivity makes blanket replacement
destructive for a non-exclusive scope.

Verified live against gitea-mcp v1.7.0, read-only calls.

Refs #99
2026-08-30 20:51:02 +00:00
c5b207aee8 fix(git-branches): restore merging.md's provenance back-reference
references/sources.md credits atlassian-gitflow-tutorial with contributing
the `--no-ff` requirement on Gitflow supporting-branch merges to
references/merging.md, and merging.md:14 does carry that claim — but the
file's own source_keys listed only context7-git-htmldocs, so the chain was
one-directional.

Surfaced by repairing validate-provenance.sh's Contributing-files parser
in the same series; this skill was one of seven whose sources.md the old
parser could not read, so checks 4 and 5 had never run against it.

Refs #99
2026-08-30 20:50:51 +00:00
93d3263f8c fix(pc-run): rebind the clean gate and fixer-hook rule to every branch
SKILL.md tells a dispatching agent to read the matching reference file
"and no other". The retrofit moved the `pre-commit clean` confirmation
gate out of the always-loaded body into references/clean.md, but
references/failure-patterns.md — loaded by the diagnosis route, not the
clean route — prescribes `pre-commit clean` with no gate at all. So "why
is this hook failing" could wipe the machine-wide cache at
~/.cache/pre-commit for every repo without asking.

The gate returns to the body, where every branch loads it, and is
restated at the point of use in failure-patterns.md. It is its own
section rather than a Gotchas bullet because folding it in pushed the
Gotchas ratio to 41%, whose only suggested remedy is moving it back to
references/ — the move that caused this.

The fixer-hook rule had the same shape: reachable only behind "if the
cause is not obvious from the output", which is false precisely when
pre-commit prints `- files were modified by this hook`. The fix
(`git add -u && git commit`) and the prohibition on `pre-commit install -f`
are now unconditional, and the two weakened pointers that stranded them
are restored.

Found by an independent review of this branch.

Refs #99
2026-08-30 20:50:45 +00:00
ddf85518c7 fix(git-worktrees): correct the remote-tracking and repair claims
The dispatch row "Create tracking a remote branch" prescribed
`git worktree add <path> <remote>/<branch>`. Per git-worktree(1) the
tracking DWIM fires only when <commit-ish> is a bare branch name that is
NOT found locally, no -b/-B/--detach is given, and exactly one remote has
a matching name; only then is it equivalent to
`git worktree add --track -b <branch> <path> <remote>/<branch>`.

An explicit <remote>/<branch> is found, so that precondition fails and no
branch is created: the result is a detached HEAD with no upstream. Commits
made in it become unreachable once the worktree is removed or HEAD moves,
and push needs an explicit refspec. Only the -d row was flagged detached.

The table now names --track -b as the always-correct form, keeps the bare
name shortcut with its precondition stated, and adds a Never row for the
<remote>/<branch> spelling. The single-remote and checkout.defaultRemote
preconditions move into the body, since a reader who trusts the table
never follows the reference pointer.

Also corrects `git worktree repair`: the no-argument form is the only
cwd-dependent one, and `repair <path>...` runs from any worktree. The
claim that running it from the wrong directory "reports nothing and fixes
nothing" has no basis in the manual and is removed.

Found by an independent review of this branch.

Refs #99
2026-08-30 20:50:36 +00:00
6cb47f81f6 fix(apm-workflow): surface the registries precondition in compile/install
SKILL.md says the apm experimental enable registries precondition
applies "anywhere — configure, install or publish," but only
configure.md actually carried it. compile.md's Publish flow and
install.md's dependency resolution can both hit a named registry and
silently no-op with no error if the precondition was never run, yet
neither file mentioned it — contradicting the skill's own promise that
each reference file is self-contained for its concern. Add a one-line
cross-reference to configure.md's Gotchas in each.

Found by an independent post-closure audit of #99 (agent-audit +
skill-audit re-run against every changed skill/agent).
2026-08-30 19:31:21 +00:00
0c0df46ac9 fix(gitea-releases): hedge the unconfirmed tag-deletion direction
The retrofit (dfacf05) collapsed a Gotcha into a bidirectional claim —
"deleting a tag never deletes the release wrapping it" — that
references/call-signatures.md never confirms; that file explicitly
marks the reverse direction unconfirmed and "the more dangerous
direction to get wrong." State only the confirmed direction (deleting
a release doesn't delete its tag) and flag the reverse as unconfirmed
with a verification step, on a destructive, irreversible operation.

Found by an independent post-closure audit of #99 (agent-audit +
skill-audit re-run against every changed skill/agent).
2026-08-30 19:31:18 +00:00
59aaec4ed6 fix(gitea-branches): repoint dangling overview.md citation
references/commits.md cited overview.md as the authority for a
scope-gating claim, but no such file exists in this skill's package —
the only overview.md is an external research doc not shipped with the
skill. Repoint to branches.md's own Token scope section, which states
and confirms the same principle, and drop the unverifiable
write:repository enumeration detail no file in this skill actually
makes.

Found by an independent post-closure audit of #99 (agent-audit +
skill-audit re-run against every changed skill/agent).
2026-08-30 19:31:15 +00:00
92ba9abe7c fix(diagnose): restore skill-root-relative script path
00c1e6b (the ADR-0020 retrofit of diagnose) moved a bullet referencing
scripts/hitl-loop.template.sh out of SKILL.md and into the new
references/feedback-loops.md, and in the move flipped the correct
skill-root-relative path into an incorrect parent-relative one
(../scripts/...) -- despite that commit's own message claiming to fix
"a script path that did not resolve." References in this skill are
written relative to the skill root regardless of which file carries
them, matching the convention used throughout SKILL.md.

Found via an independent post-closure audit of issue #99; validated
clean via skill-audit afterward.

Refs #99
2026-08-30 17:45:56 +00:00
38efd2be67 fix(skills): collapse verb-enumerated descriptions to one capability clause
An independent audit of the ADR-0020 retrofit (issue #99) found that
git-submodules, git-worktrees, and gitea-files each collapsed their
description length correctly during retrofit but left the capability
clause as a verb enumeration (e.g. "Create, list, lock, move, remove,
prune, or repair") instead of ADR-0020's required single clause. The
deterministic char-count gate can't catch this — it's a qualitative
rubric violation the retrofit commits' own messages never claimed to
address, only measurable length/word-count fixes.

Validated clean via skill-audit and skill-size-check after the fix;
boundary clauses and routing targets left untouched.

Refs #99
2026-08-30 17:45:50 +00:00
bdff6fdb3c refactor(bin): make caveman hand-invoked only
Sets `disable-model-invocation: true`, so the host withholds caveman from
the model-visible skill listing. It stops paying preload tax and can only
be reached by typing /caveman.

The reason is not the 287 characters. caveman's description was almost
entirely a trigger list -- "caveman mode", "talk like caveman", "less
tokens", "be brief" -- and "be brief" is a false-positive magnet: a user
asking for brevity wants short answers, not dropped articles and
`Respond terse like smart caveman`. Removing the skill from the router
deletes the bad trigger. User's call, made explicitly.

Safe to flag because the flag also hard-blocks the Skill tool, so any
inbound route from another skill would break. Re-checked all 39
descriptions and bodies after four waves of retrofit rewrites: every
mention of caveman is documentation, research notes or the lockfile.
Nothing routes to it.

The description is rewritten as human-facing text, since it is no longer
a routing surface -- it now says how to turn the mode on and off rather
than listing phrases for a reader that can no longer see it.

The two remaining SUGGESTIONs are #108: the boundary-clause check fires on
hand-invoked skills, which ADR-0020 contractually exempts. Advisory, and
not to be "fixed" in the skill.

Preload tax 10,002 -> 9,732 chars across 37 model-visible skills.

Refs #99

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MWb5RQgCL1ye7cGp2RPb2u
2026-08-30 17:04:45 +00:00
b25412bf39 docs: record that the ADR-0020 corpus is clean, and what the gates still miss
Wave 4 closed the last three FAILs, so the session rule no longer describes
a grandfathered set: all 39 skills clear both tiers, 0 descriptions over 400
chars and 0 bodies over 900 words. Preload tax 21,033 -> 10,201 chars
(~2,550 tokens), under the 12,000 success criterion in #99.

The rule now says what that changes for the reader: nothing is grandfathered,
so the gates bite on first commit rather than waiting for a retrofit.

Also names the second blind spot, found this wave. The Kyberforge Vale style
is scoped [**/SKILL.md], so every references/ file is unlinted -- and the
contract's own remedy is to move prose into references/, which moves it out
of the prose gate's reach. forge's retrofit relocated ~900 words that way and
the moved prose carried a rule violation Vale would have caught in a SKILL.md.

Refs #99

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MWb5RQgCL1ye7cGp2RPb2u
2026-08-30 16:42:59 +00:00
79c60715dc fix(gates): restore byte-identity of the shared ADR-0020 boundary resolver
915eb09 rewrote the bare-arrow carve-out comment in scripts/skill-size-check.sh
without pasting the block over the two other copies, so the resolver stood at
764 lines in the hook and 755 in both audit validators.
tests/test-adr0020-contract.sh exists to catch exactly this and did -- it was
the regression behind that suite's failure, and I reported the suite green
after 915eb09 without re-running it.

No behaviour changes: the drift was comment-only. Restoring identity keeps the
invariant the test enforces, which matters because the three copies are the
only thing making the hook and the two validators agree on what a boundary
target is.

Refs #99

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MWb5RQgCL1ye7cGp2RPb2u
2026-08-30 16:42:51 +00:00
264a5dbd67 docs(kyberforge): refresh the apm-workflow exemplar figures after its retrofit
body-discipline.md and contract.md both cite apm-workflow by hard number
as the reference dispatch shape, and instruct authors to calibrate against
its body word count. The retrofit in 1e4aab5 moved every one of those
figures: body 421 -> 237, whole-file 554 -> 304, references 3,006 ->
3,222. Measured, not estimated.

The quoted closing line was stale too -- it now carries a continuation
clause, so it is quoted with an ellipsis rather than as a full sentence.

These are positive exemplars in live guidance, so they are refreshed
rather than pinned. That is the opposite treatment from the git-commits
negative example in the same file, which is pinned to 5e23250 precisely
so it keeps describing the pre-retrofit state. ADR-0020 carries the same
figures and is deliberately left alone: it self-pins every citation to
base commit f9b919d, and refreshing it would destroy the record of what
the decision was taken against.

Refs #99

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MWb5RQgCL1ye7cGp2RPb2u
2026-08-30 16:31:43 +00:00
aa982b9d26 refactor(kyberforge): retrofit apm-install to the ADR-0020 contract
Description 514 -> 213 chars, Gotchas 5 entries/47% -> 2 entries/17%.
Body 350 -> 397 words: three Gotchas fold into the steps they gate, and
the repairs below add back what the fold dropped.

Cuts the second trigger register, the runtime enumeration (still named in
the body and README) and the enumeration inside the boundary clause.

Fixes four defects the first pass introduced:

- The mirror bullet claimed the piped one-liner "ignores" VERSION and
  GITHUB_URL. The installer reads both from the environment and its own
  usage header documents VERSION working through the pipe. The real
  constraint is that an air-gapped host cannot reach aka.ms, so the
  script must be on disk. Also corrects the variable names --
  APM_RELEASE_BASE_URL is the mirror base, GITHUB_URL is the Enterprise
  host.
- "If apm --version already answers, skip to Step 2" was unconditional on
  intent, so a pin or upgrade request routed past the only pin
  instruction in the skill. Now gated on intent.
- The PEP 668 rule was demoted to post-failure recovery, leaving a
  routing rule that sent a Debian box into a command that hard-fails.
  The prohibition is back on the pip bullet, before the choice.
- The apm-is-not-a-runtime Gotcha lost its operative clause. The two
  step headings cited as carrying it already existed pre-retrofit, so
  nothing had replaced it and nothing stated when Step 2 is required.

Refs #99

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MWb5RQgCL1ye7cGp2RPb2u
2026-08-30 16:31:33 +00:00
4aab9d327c refactor(kyberforge): retrofit forge to the ADR-0020 contract
Description 648 -> 387 chars, body 1093 -> 541 words. This was the last
body FAIL in the 39-skill corpus.

The body was not trimmed to fit. forge routes four artifact types that a
single invocation classifies between, so the contract requires a dispatch
table plus the gates common to every route, with each route self-contained
in references/. Adds references/author-routes.md (skill and agent),
references/apm-routes.md (plugin and marketplace entry) and
references/version-bump.md. Skill and agent share one file: they differ on
one axis only, which audit skill verifies the result.

Fixes three defects the first pass introduced or relocated:

- references/apm-routes.md claimed `apm audit` "already runs inside
  apm-workflow's own flow" and told the agent to confirm it ran clean.
  apm-workflow dispatches audit as its own row; the configure and
  marketplace rows never reach it. That was the only completion check
  these routes had, and it could never be satisfied. Replaced with a
  manual read-back the agent performs itself.
- "Read only the reference file" forbade the multi-artifact case the same
  body documents two lines later, and ADR-0011 records eight artifacts
  authored in one pass.
- The announce gate became a closing gate, reachable only after the
  invocation it was meant to precede. Moved to the end of Step 2.

Also restores the artifact enumeration to the plugin row, normalises to
bare unnamespaced skill names per AGENTS.md, adds a dispatch fallback for
artifacts matching no row, and corrects three provenance entries -- one
asserted a contribution that did not happen.

Refs #99

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MWb5RQgCL1ye7cGp2RPb2u
2026-08-30 16:31:23 +00:00
1e4aab53a7 refactor(kyberforge): retrofit apm-workflow to the ADR-0020 contract
Description 817 -> 324 chars, body 421 -> 237 words, Gotchas 6 -> 2.

The five capability clauses, the second trigger register and the prose
boundary form go; one trigger clause, the indirect trigger and one
boundary clause remain. Four of six Gotchas move into the flow file that
every branch needing them already loads.

Two stay in the always-loaded body because a dispatch body must carry the
gates common to every branch, not just the dispatch table: the MCP secret
indirection rule, and the `apm experimental enable registries`
precondition. The first pass moved registries into references/configure.md
alone, which stranded it -- references/compile.md documents publishing to a
registry and references/install.md resolves dependencies through one, and
neither points at configure.md. Declaring a registry without the
precondition is a silent no-op, so the failure had no signal.

Also drops an unsourced claim the compression pass introduced (that apm
checks `type:` going forward -- no source supports it), corrects the MCP
rationale to install *or* runtime per configuration.md:98, and repoints
two apm-orchestrate back-references that pointed at body Gotchas which had
moved.

Refs #99

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MWb5RQgCL1ye7cGp2RPb2u
2026-08-30 16:31:11 +00:00
915eb09ae2 docs(scripts): record that the bare-arrow carve-out is now unexercised
The comment justified the ADR-0020 compressed-form gate with diagnose's
process chain 'fix -> regression-test', which without the gate read as a
route to a non-existent regression-test skill. Issue #99 cut that chain
when it retrofitted the description, so the gate now produces an
identical verdict corpus-wide whether it is applied or not.

Keeping the branch. It guards against prose no one has written yet, any
new process chain re-arms it, and the bare-arrow rule it sits on is the
sole extractor for three real targets in kyberforge's audit skills, all
written unbackticked. Unexercised is not the same as unnecessary — the
comment just needed to stop citing evidence that no longer exists.

Refs #99
2026-08-30 15:51:43 +00:00
d8dfba958c docs(agents): correct the ADR-0020 gate counts after wave 3
Three skills still exceed a FAIL tier, all in kyberforge, down from ten
descriptions and two bodies. No routing target dangles any more, and the
test suite now pins that set as empty rather than tracking a backlog.

Refs #99
2026-08-30 15:07:17 +00:00
7e80c09b13 fix(kyberforge): a dispatch table satisfies the reference-wiring rule
body-discipline.md required every reference load to use the literal
'If X, read references/file.md' form and called anything else a generic
pointer. ADR-0020's own cited dispatch exemplar, apm-workflow, uses a
bare table plus one closing line, so an author could not satisfy both --
and the rule reliably produced duplication in exactly the bodies the
contract exists to keep short.

Resolves #109 with its option 1: a table row already pairs a condition
with a target, so where a body dispatches, the table is the wiring. The
literal form is what a body needs when it loads a reference without a
table.

Two corrections to the issue as filed. There is no Vale conflict --
PaddingPhrase.yml only matches 'see references/ for more info' and never
fired on the exemplar, so this is a one-file prose fix and no rule
changes. And gitea-workflow carried the predicted duplication: a
three-row table restated underneath as three conditionals. Removed, body
227 -> 148 words.

Closes #109
2026-08-30 15:07:16 +00:00
ff187ef9fc refactor(lint): retrofit vale-run to the ADR-0020 context contract
Description 654 -> 294 chars, Gotchas 36% -> 19%. Body 698 -> 642 words:
up from the first pass, because a clean-context audit found four defects
whose fixes are net-additive text.

The suppression-markup warning covered only one of the two paths that
write it -- the list is case-based, so a recurring false positive goes
straight to step 3 and never read step 2's warning. Hoisted above both.

The CI-failure trigger, which the description advertises, had no path to
the file holding its answer: the negative diagnosis 'if the alerts are
warnings, Vale is not what failed the build' survived only in
troubleshooting.md, which no CI-entered invocation loads. An agent would
confidently prescribe --no-exit for a failure Vale never caused.

The description had lost every prose-domain word -- no 'prose', no
'linter' -- while vale-config kept all of them, so 'check prose style'
routed to the wrong skill of the pair.

Accepts two soft SUGGESTIONs rather than dropping restored content;
neither fails the gate.

Refs #99
2026-08-30 15:07:16 +00:00
45cd26045a refactor(lint): retrofit vale-config to the ADR-0020 context contract
Description 657 -> 244 chars, body 504 -> 342 words, Gotchas 50% -> 24%.
The 50% was the worst ratio in the corpus and a real inverted body, not
the denominator artefact the ratio usually flags: half the body was a
Gotchas section doing duty as reference material.

A clean-context audit caught the split leaving a false statement behind.
Gotcha 1's kernel lost the qualifier that only package styles need
fetching, so it asserted that any style vale sync has not fetched fails
-- contradicting the same file twice, since the built-in Vale style and
any committed custom style are never fetched. An agent adding a custom
style would have added a spurious Packages entry and broken vale sync
outright. The qualifier is restored in the body rather than behind a
fourth reference pointer.

Also corrects the moved fixture's framing, which tabulated a control row
under a heading claiming it came from a multi-line fixture.

Refs #99
2026-08-30 15:07:15 +00:00
c59e4bf0c5 refactor(core): retrofit provider-adapter-author to the ADR-0020 contract
Description 833 -> 239 chars, body 370 -> 387 words, Gotchas 41% -> 20%.
The file-type enumeration moves to a new references/provider-matrix.md;
the composition note was already in README.md.

Three defects a clean-context audit found, all fixed:

The 'never edits AGENTS.md' prohibition had become a justification clause
on the false branch of a conditional, so the common path never read a
sentence binding it. That matters because the bundled validator's own
remediation text tells the agent to move content into AGENTS.md, so a
size FAIL actively invited the prohibited edit. Restored as a standing
imperative, plus a counter at the step where the trap fires.

A Gotcha asserted that validate-adapter.sh fails without
--no-import-syntax. The flag is a no-op -- both branches reduce to the
same expression. Reverted to an instruction; the script defect is #115.

The boundary clauses used pronouns to dodge the #110 regex, and 'Not
auditing it' resolved to CLAUDE.md as readily as to AGENTS.md -- routing
'audit my CLAUDE.md' to a skill whose own description declines it.

Refs #99
2026-08-30 15:06:50 +00:00
f0526b310d refactor(core): retrofit agentsmd-audit to the ADR-0020 context contract
Description 944 -> 322 chars, Gotchas 36% -> 22%. The composition note
moves to README.md.

Restores the hand-edit trigger, which a clean-context audit found had no
other caller: agentsmd-author owns the post-authoring invocation, but a
hand-edit has no author skill in the loop, so nothing invoked the audit
at all. It survived only in README.md, which neither the router nor the
invoked agent loads. That is the path on which a human pastes a
credential into AGENTS.md.

The first pass dropped it against a measured budget of '~9 spare chars'.
The real cost was ~49, and 250 is the SUGGESTION tier, not a ceiling --
the gate fails at 400. Ships at 322 with one advisory line.

Names the three audit dimensions in the capability clause, recovering
routing for 'does my AGENTS.md leak credentials', and qualifies the
'is this AGENTS.md safe to commit' phrasing, whose pronoun had no
antecedent inside the quoted string.

Refs #99
2026-08-30 15:06:49 +00:00
e42c055294 refactor(core): retrofit agentsmd-author to the ADR-0020 context contract
Description 960 -> 244 chars, body 470 -> 452 words, Gotchas 36% -> 22%.
Both composition notes move to README.md, which already carried them.

Four of five Gotchas were paraphrases of the step below them and were
deleted with their force folded back into that step. A clean-context
audit overturned the fifth deletion: the provider-file prohibition was
strictly broader than Step 4, so it was never a paraphrase, and Step 4's
'don't rewrite it yourself' is attached to the if-duplicates branch. With
Write and Edit granted, a provider file that was merely stale had nothing
forbidding an edit. Restored as an unconditional Gotcha, read before any
step writes.

Also restores a concrete indirect trigger. The retrofit had replaced two
with the meta-statement 'even when they don't name the file', which
claims an indirect trigger exists rather than being one -- and users
asking to document a repo for AI tools have no reason to know the
filename.

Refs #99
2026-08-30 15:06:48 +00:00
00c1e6b305 refactor(bin): retrofit diagnose to the ADR-0020 context contract
Body 1126 -> 808 words, clearing the FAIL tier, and description 290 ->
220 chars. Phase 1's depth moves to references/feedback-loops.md; the
six-phase spine stays in the body, since a linear procedure is not a
dispatch case.

The description rewrite was not originally in scope, which was an error:
adding a mandatory boundary clause to a 290-char description cannot land
under 400. The dropped capability chain was also inaccurate -- it named
'minimise' as a phase that does not exist while omitting the one phase
the body calls 'This is the skill'.

A clean-context audit found no text lost but three reachability defects,
all fixed: content stranded behind an inverted trigger, Phase 2's
reproduction-rate threshold defined only in a file that path never
loaded, and a script path that did not resolve from the file carrying it.
The two reference files are merged into one, since the split is what
created the first two.

Refs #99
2026-08-30 15:06:26 +00:00
f03bfa8d24 refactor(bin): retrofit prototype to the ADR-0020 context contract
Description 426 -> 286 chars, with a boundary clause added. The body was
already compliant at 467 words and is untouched.

A clean-context audit caught the first pass trading away the LOGIC
branch's routing vocabulary for characters it did not need to save: both
'data model' and 'business logic' had gone, though LOGIC.md defines its
own scope with exactly those words. Restored, so 'does this data model
feel right?' routes here again.

Accepts 286 over the 250 target -- the hard tier is 400, and the
alternative was leaving half the dispatch reachable by one phrase.

Leaves LOGIC.md and UI.md at the skill root; moving them into references/
is tracked as #114.

Refs #99
2026-08-30 15:06:25 +00:00
ee6b04061a refactor(bin): retrofit research to the ADR-0020 context contract
Description 583 -> 231 chars and body 854 -> 519 words. Deletes the
neuledge-context boundary clause outright: commit 6146120 deleted that
skill and no skill has owned MCP-server installation since. That was the
last dangling routing target in the corpus.

Removes META.md, which file-structure.md:20 forbids at a skill root. Its
when: field duplicated the description and its references: entry pointed
at .agents/skills/context7-mcp/SKILL.md, which does not exist.

Restores two rules a clean-context audit found had lost their force: the
starting-URLs branch in step 3, which the retrofit had reduced to a
condition with no behaviour, and the references/file-format.md pointer at
step 6. The second matters downstream -- validate-provenance.sh parses
sources.md with anchored regexes and check 8 short-circuits silently when
the Status field is absent, so a sources.md written from step 6 alone
broke the provenance chain with no error anywhere.

Rewrites steps 4-5 as serial WebFetch reads. They mandated spawning
subagents that allowed-tools never granted; no tool was added because the
name differs across the three compile targets. Tracked as #116.

Updates the two test pins and the eval case that asserted the dead route.

Refs #99
2026-08-30 15:06:23 +00:00
a2ebdafc5e fix(skill-audit): pin the stale worked example and state its reachability precondition
The body-discipline rubric cited git-commits as it stood before the ADR-0020
retrofit -- twelve Gotchas, 387/1102 words, line numbers :31-:52. Every
figure was correct for that version and none survives in the current file,
so the example is now anchored to commit 5e23250 and marked not to be
refreshed against HEAD.

More than staleness: row four called the secrets Gotcha a paraphrase FAIL
because step 2 restated it. Wave 2 followed that reasoning, deleted the
always-loaded copy, and left the amend branch able to commit a credential
unchecked -- dispatch loads exactly one flow file. The paraphrase rule now
carries its missing precondition: delete a restating Gotcha only when the
surviving copy is reachable from every branch that needs it, and relocate
a multi-branch safety gate into the body rather than dropping it.
2026-08-30 13:22:55 +00:00
afbeaff56b docs(agents): correct the ADR-0020 gate counts after the git retrofit
Descriptions over the FAIL tier 19 -> 10, bodies 7 -> 2. This line is
always-loaded context, so a stale count misleads every session.
2026-08-30 13:14:00 +00:00