refactor(skills): retrofit the corpus to the ADR-0020 context contract #129

Merged
Defame1297 merged 89 commits from refactor/adr0020-skill-retrofit into main 2026-09-01 13:47:47 +00:00

89 Commits

Author SHA1 Message Date
afadaae793 fix(lint): reword the two Vale errors this branch put where nothing lints
`Kyberforge.SentenceOpenerThereIs` is `level: error`, but `.vale.ini` scopes the
style to `[**/SKILL.md]`, so no hook can see a violation in `references/`. This
branch's own remedy — move prose out of the body into `references/` — is what
carried these two there:

- `provider-adapter-author/references/provider-matrix.md:31`
- `agent-audit/references/finding-criteria.md:95`

Both confirmed by copying the file to `SKILL.md` and running the audit's own
`vale-wrap.sh`: 1 error each before, 0 after. Reworded to name the subject
directly, which is what the rule asks for.

Five further occurrences exist in `references/` files already on `main`. Those
are the pre-existing corpus, not this branch's regression, and stay with #117 —
which is the real fix: widen the Vale scope so the gate can see this class at
all, and make the wrapper distinguish "0 alerts in 1 file" from "0 files
matched".

Source and generated mirror move together so the commit passes
`check-plugin-content-sync` standalone.

Refs: #117

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Ktx4QJzTXZtw35m6T9WhcK
2026-09-01 13:32:31 +00:00
0427422765 chore(release): bump the six plugins and the catalog to patch, not minor
The branch is 31 fix, 30 refactor, 11 docs, 4 chore, 2 test — zero feat, zero `!`,
zero BREAKING CHANGE — and it adds no skill, agent, command or hook. Two rules
shipped in this repo both make that a patch: forge's version-bump.md, which lands in
this very branch ("minor for new capability, patch for a fix/refactor"), and
git-commits' conventional-commits-spec.md, which maps fix to PATCH and refactor to
none. Minor was the one answer neither rule produces, and the release was arguing
with a policy it was simultaneously introducing.

  bin 1.1.6, core 1.1.2, git 1.3.6, gitea 1.3.7, kyberforge 1.6.1, lint 1.1.7

The catalog goes 0.5.0 to 0.4.6 for the same reason: apm-workflow's marketplace.md
reserves a catalog minor for a packages[] entry added or removed and assigns patch
to an existing entry's version moving. The set is 7 entries on both sides with
unchanged names, so only the patch trigger applies.

Each number is +1 patch on the pre-bump value rather than stacked on the minor, and
executables.allow follows kyberforge to 1.6.1 so the ADR-0019 SessionStart hook does
not orphan.

Note what this does not settle: four published files were removed from the installed
tree on this branch, three more moved, and caveman gained disable-model-invocation,
which retires its old triggers. Under a strict reading of the repo's own breaking
rule those are major-class and they currently ship under refactor: with no marker.
Patch is right for the code; whether the deployed skill surface is a public contract
is still unwritten, and that question outlives this commit.
2026-09-01 12:39:54 +00:00
ccc54cbb58 docs: retire ADR-0020's stale measurements and fix a CONTEXT.md code span
971e148 de-pinned three stale present-tense figures in gates.md and left ADR-0020's
copy of each, while CONTEXT.md points readers at the ADR for the current number. All
three were wrong at HEAD, re-measured against a `git archive` of the tree rather
than the working copy:

- "26 description FAILs, 9 body FAILs, 2 dangling targets, 58 SUGGESTIONs" is now
  0 / 0 / 0 / 29.
- "pre-commit run --all-files is red on 10 alerts" is 0 errors in 39 files.
- apm-orchestrate was cited as a 900-word body FAIL. It is 876 words, a SUGGESTION.
  git-orchestrate at 933 and gitea-orchestrate at 1,199 were correct.

Marked historical with a dated amendment and the measured current values, following
the convention already in this file. The "realistic landing is somewhere in that
33-58% band" projection is left alone: it is a forecast rather than a measurement,
and the realised 55.3% falls inside it.

The Enforcement table claimed exhaustiveness while listing only ERROR and SUGGESTION
for boundary targets; gates.md documents a third verdict, INFO "DID NOT RUN". Added.

CONTEXT.md carried shell-escaped backticks inside a markdown code span, which closes
the span early and leaves an unterminated double-backtick span that swallows the
rest of the glossary entry — in the definition of a term this branch introduces. It
also called the ADR's pre-retrofit 23,427 the current preload figure; the measured
value is 10,478, recorded here so the glossary and the gate agree.

ADR: 0020
2026-09-01 12:39:42 +00:00
f40deada86 fix(lint): restore the glob-scoping rule and warn about the MDX invocation kill
vale-config deleted "settings in a glob section only apply to files matching that
glob" — the single most common Vale misconfiguration. Nothing in either lint skill
told an agent that BasedOnStyles under `[*.md]` governs `.md` only; the nearest
survivor was a parenthetical in a reference file the skill opens only for the full
field listing. Restored to the always-loaded body, at the point of use.

vale-config also never mentioned `[formats]` or MDX at all, leaving the whole
decision in that same reference file. Verified against Vale 3.15.2: an unmapped
`.mdx` covered by one of your globs is `E100 [lintMDX]`, exit 2, and every other
file in that invocation produces no output whatsoever. Because the failure is
invocation-wide rather than per-file, it belongs in the Gotchas.

The crash requires a glob that actually covers `.mdx` — under `[*.md]` the file is
skipped and nothing fails — so the Gotcha states that condition rather than the
broader claim that any `.mdx` in the tree kills the run.

The mapping-versus-mdx2vast attribution disagreed across the two skills: vale-run
said `[formats] mdx = md` is "what vale-config recommends" while vale-config
presented it as a bare either/or. Fixed on the source side by having vale-config
actually recommend it and say why, which makes vale-run's existing sentence true
with no edit to vale-run.

Refs: #117
2026-09-01 12:39:31 +00:00
40ff89eabf fix(bin): restore prototype's deleted anti-patterns and two routing triggers
ADR-0020's stated anti-goal is satisfying the size gate by deleting content rather
than relocating it. prototype's logic.md lost three anti-patterns, including
"Don't generalise" — the one with a distinct failure mode, a throwaway growing
abstractions for hypothetical futures, and the one the logic branch is most exposed
to. It survived nowhere in the repo.

The deletion bought nothing measurable: references/ sits outside the body FAIL,
outside the 600-word suggestion and outside the Vale gate, and prototype's body is
483 words. 4011d14 restored the byte-identical defect in the sibling ui.md with
exactly that reasoning in its message and left this file alone. Restored verbatim
from main.

improve-codebase-architecture had dropped "refactoring" from its description
entirely, so "find refactoring opportunities in this repo" had no lexical match,
while spending characters on a boundary against tdd — which cannot plausibly steal
an architecture request. retrofit.md names that exact failure: an invented boundary
costs characters and buys no routing accuracy.

write-docs had dropped all four literal trigger phrasings, leaving them only in the
body and a `when:` field, neither visible to the router at routing time. Its
boundary also sent PRDs to grill-with-docs, which has no PRD flow, and the body
repeated that at two more places. Per #123 nothing in the corpus produces a PRD, so
no target was invented — the boundary is now honest about the ADR case only.

Two READMEs added by this branch contradicted the SKILL.md they document: triage's
label resolution, and grill-with-docs' fifth during-session behaviour. Unconditional
reference pointers in tdd and improve-codebase-architecture are now conditional; the
files stay at the skill root, which is #122's scope.

Refs: #114, #122, #123
ADR: 0020
2026-09-01 12:39:22 +00:00
be9b8d277f fix(gitea): restore the withLines exception and route rename_branch through the orchestrator
gitea-files' always-loaded Gotchas said "content is base64 both ways" without
qualification. The `main` text carried an exception for `withLines: true` and both
halves were dropped. Verified live: `get_file_contents` with `withLines: true`
returns plain JSON text while the same response still reports `"encoding":"base64"`.
An agent that follows the recommendation two sentences later and applies the
unconditional decode gets garbage, with the response's own field confirming the
wrong answer. Exception restored, and the lying field named.

gitea-orchestrate was never updated for `rename_branch`: absent from the operation
enum, so an agent caller got "unknown operation", and absent from the
destructive-confirm list, though branches.md requires a rename with open PRs or a
protection rule to be confirmed exactly as `delete_branch` is. Added to both — the
confirm gate rather than the enum alone, because accepting the operation without it
routes around a rule the skill states while appearing to support it. The
compatibility frontmatter, which the agent reads, still omitted the tool too.

Two more always-loaded Gotchas contradicted their own reference files, and the
Gotcha was wrong both times: issues and PRs are distinguishable on a list item by
the `html_url` path segment (confirmed live — #129 at /pulls/, #128 at /issues/),
and `get_repository_tree` takes `tree_sha`, not `ref`.

`review_comments` was asserted as unconditionally present on the PR get response.
It is absent on a PR with no review comments, so the claim is downgraded to
present-when-non-zero rather than stated as response shape.

The label-exclusivity relocation moved the rule out of label-inference.md and into
labels.md without updating sources.md, leaving the one rule in this branch that
writes differently to live repos citing a file that no longer carries it. The rule
itself is correct as it stands and `main` was wrong — every Kind/* label on this
instance is exclusive:false, every Priority/* and Status/* is true — so only the
provenance record is corrected.

Routing: gitea-workflow lost the human-caller discriminator and widened from status
checks to any request, which sent "close #42" to a branch that resolves the number
and presents detail without ever closing it. gitea-branches and gitea-issues regain
trigger phrasings the retrofit dropped.

Refs: #92
2026-09-01 12:38:53 +00:00
20e5627fd7 fix(git): correct three command claims the plugin got wrong, and give rebase an owner
Three statements were false against the git in use (2.39.5), each verified by
running it.

`git rebase --autosquash HEAD~N` without `-i` is a silent no-op: git prints
"Successfully rebased", the `fixup!` commit survives with the same SHA, and
rewrite-history.md presented that as the preferred flow with `-i` as an optional
review step. The agent reports the squash as done and the `fixup!` subject then
trips this repo's own commit-msg gate. `-i` is now the command, not the alternative.

"Fetch never modifies a local branch, so it is always safe to run" — new text on
this branch — is false: `git fetch origin main:probe` fast-forwarded the local
branch, and a `+` prefix force-updates it, losing commits. The claim is scoped to
the no-refspec form.

`git worktree add --orphan` does not exist before Git 2.42; on 2.39.5 it is
`error: unknown option 'orphan'`, exit 129. The same file gives a version floor for
`--recurse-submodules` three sections earlier. Floor added, with a fallback that
was tested before being documented.

git-workflow claimed "every request resolves to exactly one of these six" while
rebase, reset and stash were owned by no skill — `git reset` appeared nowhere in
the plugin, old tree or new — so "undo my last commit" routed nowhere. The claim is
gone, the router gains rows for all three, and the procedures now exist: plain
rebase with `--onto` and conflict handling, a reset mode table gated on `--hard`,
and stash save/pop/list/drop. `--hard` gets an always-loaded Gotcha, matching the
register the force-push refusal already sets.

Cherry-pick had three claimants pointing at git-history while git-commits owned the
flow, and the two copies were not equivalent — git-history's lacked the destination
check, the rtk prefix and `--abort`. Resolved to git-commits per #112; the weaker
duplicate is replaced by a hand-off.

git-commits' metadata.version was deleted rather than bumped in 14af50b, leaving two
house-contract documents citing a worked v0.1.2 to v0.1.3 transition against a file
declaring no version. Restored to 0.1.3.

Also: the divergent-pull explanation stated a `--ff-only` default that does not
exist (it is a hard error); `--remote` "requires" a configured branch where it uses
one; `git push origin --delete` moved to git-remotes, which owns the remote-side
gates; seven retired `git:<name>` source slugs; git-orchestrate quoted a
git-workflow sentence that no longer exists; git-submodules regains the indirect
trigger that made "add a dependency repo" routable; and commit atomicity is a common
gate rather than reachable only from the create flow.

Refs: #112, #113
2026-09-01 12:38:38 +00:00
fc305ba7d9 fix(kyberforge): correct the routing-tier contract and make agent-audit rubrics conditional
Five documents told authors that a prose-form dangling routing target blocks. The
gate reports it as a SUGGESTION and exits 0. Verified on fixtures: `-> name` and
`/name` are blocking ERRORs, the prose form is SUGGESTION-tier unless a second
resolving target in the same sentence corroborates it. ADR-0020 and gates.md were
right; contract.md, retrofit.md, description-quality.md, finding-criteria.md and
agent-author's contract.md were wrong — and they are what an author and an auditor
actually read. The whole 39-skill corpus was retrofitted against them.

skill-audit was also self-contradictory: it imports validate.sh's SUGGESTIONs into
the Structure dimension verbatim while its own rubric grades the same target a FAIL,
so one target got reported twice at two tiers. The script owns the grade; the rubric
now says so.

The YAML-fold trap that broke gitea-labels-milestones (#100) was warned about only
in retrofit.md, reachable only from the improve flow when a budget is exceeded. It
is now in both contract.md files, which SKILL.md mandates on the create flow too.

agent-audit loaded both rubrics unconditionally on every run — 3,323 words for a
clean audit against skill-audit's 1,636. dac9cad fixed exactly this in skill-audit
and edited agent-audit in the same commit without applying it. Same treatment: the
criteria move to a new finding-criteria.md and load per dimension. Clean run now
2,083 words, a 37% cut.

Routing: apm-workflow's description shed dependency installation while still owning
the flow, and apm-install's boundary did not exclude it, so "install my apm
dependencies" matched the CLI-binary skill with no route back. Fixed on both sides.
forge regains two of the three phrasings the retrofit deleted.

forge Step 1 called grill-with-docs unconditionally — a skill in plugins/bin, which
kyberforge does not declare as a dependency. It resolves here only because the
walk-up sweeps sibling plugins; a standalone install dead-ends. Step 1 now names
the cross-plugin dependency and gives an inline fallback. Declaring it properly in
apm.yml remains the better fix.

Also: both audit SKILL.md files now grade exit 2 as "did not run, dimension
unverified" rather than as findings; skill-audit's README row described content that
moved, which its own finding-criteria.md grades a FAIL; and body-discipline.md's
`git show <sha>:plugins/...` command is fenced, since an installed plugin cache has
no repo and file-structure.md makes a bare repo path a FAIL.

Refs: #100, #101, #125
ADR: 0020
2026-09-01 12:38:22 +00:00
59f27dbd94 fix(core): close five ways validate-adapter.sh graded an adapter it had not read
The adapter check shipped green on files it should have failed, and failed files it
should have passed. Each defect is a residual of the fix that closes #115.

A fenced, indented or HTML-commented `@AGENTS.md` counted as an import, though
Claude Code resolves none of them — the adapter deferred to nothing and the gate
said so approvingly. Import matching now runs against a character mask that marks
fenced blocks and HTML comments inert, and applies CommonMark's four-space rule.
The mask is deliberately not applied to prose pointers, where four-space
indentation is ordinary list continuation.

The encoding fix reached only BOM-carrying UTF-16/32. BOM-less UTF-16LE/BE and
UTF-32LE are valid UTF-8, so they still produced the exact false diagnosis the fix
was written to remove: "no reference to AGENTS.md" on a file whose first line is
`@AGENTS.md`. A NUL-byte check is the complete signal. BOM stripping is no longer
positional, which also drops the mirror-image false FAILs on a doubled or mid-file
BOM.

`@NOTAGENTS.md`, `@zzzAGENTS.md` and `@docs/does/not/exist/AGENTS.md` all passed:
the pattern had no path-segment boundary and the target was never resolved on disk.
Both now hold, and a zero-byte or blank target is reported rather than credited.
Unresolved candidates print as `Near miss:` lines so the author sees why a line was
not counted.

`--no-import-syntax` still used substring matching, so `Do NOT read AGENTS.md; it
is obsolete.` passed as a pointer. That is #115's own defect surviving in the flag's
other mode. A mention must now carry a deference cue and must not be negated.

An unreadable file passed `isfile()`, raised, and exited 1 with a traceback and no
FAIL line — the one exit code no document covered, while Step 3 says to re-run
until it exits 0. It now exits 3 with a diagnostic, and the README states all four
codes and what to do about each instead of "exits non-zero on any failure".

Tests 18 to 42. Two of the new cases initially survived their own mutation and were
strengthened: `@NOTAGENTS.md` was being rejected by the disk check before the token
boundary ran, and stripping that boundary leaves the fragment `NOT`, which the
negation cue then rejects for an unrelated reason.

Refs: #115
2026-09-01 12:38:04 +00:00
88866b81a3 fix(kyberforge): announce every provenance skip and scope checks 7-8 to source indexes
The clean provenance bill was an artifact. Checks 7 and 8 assume `Research doc:`
names a source index whose H2s are slugs, but 30 of 121 corpus entries point at
topic content documents whose H2s are topics. Those 30 produced every new check-7
INFO — all false positives. Check 8 aimed at the same documents, which carry no
`Status:` line at all, would have emitted a large false-FAIL flood; the only thing
preventing it was an unannounced `rd_status != extracted` skip. So "0 new FAILs"
rested on exactly the fail-open class this branch exists to remove, and naively
fixing the skip would have turned the branch red.

Checks 7/8 now run only when the research doc's basename is `sources.md`, and every
other case emits a visible INFO naming the slug. The dangling-path INFO stays ahead
of the basename gate, because a path that does not resolve is rot whatever it is
named. `parse_status` accepts the bullet form and a trailing note after the
backticked value, so a status it cannot read no longer reads as "nothing to check".

Corpus: 36 INFOs of which 30 were false, to 56 of which none are. FAIL stays 0, and
no Status line flipped to `extracted` under the new parser, so no FAIL was
suppressed by luck.

Also closed, each a silent pass: a nonexistent directory, a directory with no
SKILL.md, and extra arguments now exit 2; a UTF-8 BOM no longer defeats frontmatter
parsing; the bare `except Exception: return False` that turned an unreadable file
into a clean pass is gone, with all reads pinned to UTF-8; check 3 walks nested
`references/` subdirectories; `FILL IN:` at end of line no longer escapes checks 1
and 6; duplicate `## slug` blocks and repeated `Research doc:` lines are announced
rather than half-read.

The agent-audit copy carried all of the above unfixed and is now ported, minus the
four fixes that are genuinely N/A at agent scope — it reads a plugin-root
`sources.md` and has no checks 7/8 and no `references/` tree. Its silent exit 0 for
a file outside plugin scope is preserved deliberately: that is a verdict about a
valid file, not a skip, and `check-scope-walkup-sync.sh` pins it. Every exit-2 gate
therefore decides from the argument alone, before the walk-up runs.

`validation-scripts.md` said flatly that silence from the validator is a pass, not
a skip. That sentence is what made a typo'd path dangerous, and both copies are
corrected here. The matching SKILL.md exit-code guidance lands with the audit
rubric change, which touches the same files.

Tests: skill-audit 45 to 65, agent-audit 24 to 43, every new case proven by mutation.

Refs: #111, #118, #121
2026-09-01 12:37:50 +00:00
9fe734573d fix(gates): close the /name fail-open and stop the path guard inventing targets
Two defects in the routing-target resolver, both latent in the corpus but hot for
anything written next.

The free-standing `/name` sweep sat inside `if boundary:`, so route notation in a
sentence carrying no boundary marker was never extracted at all — not an ERROR, not
a SUGGESTION, not an INFO. That contradicted ADR-0020's amendment and gates.md,
which both promise `/name` blocks unconditionally. The sweep now runs over every
sentence. `-> name` and backticked forms stay gated deliberately: an arrow also
writes a process chain and a code span cites tools, files and skills alike, so
ungating either fires on ordinary prose.

The path guard used `\b`, which still holds after a hyphen, so the engine
backtracked to a shorter hyphen-terminated prefix whenever the lookahead rejected
the full segment. `/api-docs/v2.md` in a boundary clause raised blocking ERRORs for
'api' and 'api-docs' — names no author wrote, with no corroboration escape.
`(?![\w-])` forbids the shortened prefix outright; MARKED_TARGET, which had no
trailing guard at all, gained one.

Zero arguments now exits 2 rather than 0, so a mis-scoped `files:` pattern is no
longer indistinguishable from a clean corpus. Both hook manifests pass filenames
and pre-commit skips a filename-passing hook when nothing matches, so the hook
never sees an empty argv — that contract is now asserted by a test rather than
left in prose.

Deleting the sweep entirely used to leave every suite green. It now kills eight
assertions. The suite also gains its first slash-path and URL fixtures, in both
directions.

Refs: #107, #110, #124
ADR: 0020
2026-09-01 12:37:12 +00:00
971e148e19 docs: amend ADR-0020 and correct the gate reference to match what ships
The ADR said a /slash target behind a route verb takes the follower test; the
implementation decides notation first and skips it. Recorded as a dated amendment
rather than a silent edit, per the ADR-0016/0017 convention.

Its Enforcement table called itself exhaustive 'because the failure this ADR is
most exposed to is a rule filed under Enforcement that no validator implements'.
Three shipped behaviours were missing, including the disable-model-invocation
carve-out that removes two rows. Also de-pins the 'zoom-out is the one carrier'
claim, which caveman falsified, and the stale dangling-target statuses.

gates.md's ERROR row made terminality a conjunct for route notation, telling an
author a form is safe that exits 1. It now documents the references/ Vale blind
spot and its two independent causes -- AGENTS.md trimmed to the operative rule
per the split gates.md itself states -- plus the undocumented skill-frontmatter
hook and the second scope exclusion. CONTEXT.md glosses 'routing target'.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EJJrm5YmacbwMdzZpXcoti
2026-08-31 19:47:04 +00:00
4011d149bc fix(bin): put five skills' boundaries where the router can read them
grill-me, grill-with-docs, improve-codebase-architecture, tdd and triage each had
their routing boundary written into README.md, which nothing loads at runtime,
while the gate still reported all five descriptions as boundary-less. The
boundaries move into the descriptions; write-docs' clause, which said 'those have
dedicated skills' without naming one, now names them.

research had moved its body out and then read both references unconditionally --
the anti-goal ADR-0020 names, where the word count moves and the per-run context
does not. Both loads are genuinely conditional now, with the topic list and the
four literal sources.md field names inlined, since the provenance validator
matches those literally.

Also restores the promote-the-prototype anti-pattern to prototype's ui.md, which
the gate does not measure, so deleting it bought nothing.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EJJrm5YmacbwMdzZpXcoti
2026-08-31 19:47:00 +00:00
ae791781c2 fix(git): restore router coverage and commands the retrofit dropped
git-workflow calls itself a router but named two of the six domains it routes to;
the other four appeared nowhere in the file. All six are now named, with a
routing table in the always-loaded body.

git-submodules lost the foreach shell-variable semantics -- only the bare names
survived, though $sm_path and $displaypath differ solely by which directory you
are in. The table is back. Its relocated commands had also dropped the rtk git
prefix its own SKILL.md mandates; 24 of them are re-prefixed. The wider rtk
inconsistency across the plugin stays with #113.

Also restores git-commits' body and footers output fields, git-branches' tag/
branch detection commands, git-worktrees' git config --worktree, pc-run's
ambiguity fallback, git-remotes' git-history boundary, and git-history's pickaxe
triggers.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EJJrm5YmacbwMdzZpXcoti
2026-08-31 19:46:35 +00:00
1a971ee003 fix(gitea): restore routing and sourcing content the retrofit dropped
gitea-issues asserted flatly that a merge never closes an issue, contradicting
gitea-prs' references/merging.md, which documents that closing keywords in
commits landing on the default branch do close one. The qualifier that made the
claim true had been deleted; both sides now agree.

gitea-releases had lost an epistemic hedge and its verification step, leaving
conventions.md asserting unconfirmed tag auto-creation as fact. Nothing in the
research corpus sources it, so the hedge and the verify-afterward instruction are
back rather than upgraded.

Descriptions were cut 50-240 chars under the 400 budget and shed routing with
them: gitea-workflow's boundary named no target, gitea-prs lost the issue/PR
number-space directive the suite is built around at 163/400, gitea-releases lost
its boundary and every trigger. Restored, inside budget. Also restores
delete_branch's hard-refusal strength and gitea-files' Read/Write/Edit pointer.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EJJrm5YmacbwMdzZpXcoti
2026-08-31 19:46:32 +00:00
a8cd5e881d fix(core): strip a BOM before the adapter import check, and split usage exits
Narrowing has_reference to bool(import_lines) meant a UTF-8 BOM hid the import
line, since the BOM is not \s: a CLAUDE.md whose first line is @AGENTS.md failed
with 'no reference to AGENTS.md' and was told to add the line already in front of
it. Decoding is now strict too, so a non-UTF-8 adapter gets an encoding
diagnostic instead of being mangled and then graded on the mangling.

Usage errors move to exit 2. They shared exit 1 with real findings, while the
skill tells the agent to fix any non-zero exit by editing the provider file.

The whole-line import rule is kept deliberately -- accepting an inline @AGENTS.md
would also accept one inside backticks, which is the silent-drop failure the
validator exists to catch -- and the message now says so.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EJJrm5YmacbwMdzZpXcoti
2026-08-31 19:46:07 +00:00
00daf285ec fix(kyberforge): tell an unparsable Contributing-files block from an explicit (none)
parse_contributing_files documented that callers depend on None vs [], because a
parse failure returning [] would silently disable the check. Only check 8
honoured it; checks 4/5 (skill-audit) and 3/4 (agent-audit) used a truthiness
test, so an unreadable block disabled them without a word.

Two live corpus entries were skipping this way. A sweep of all 32 sources.md
found 134 entries, exactly 2 parsing to None, both in gitea-files: one heading
carried an inline parenthetical that defeated both regexes, and one (none) was
written without its leading bullet. Also pins EMPTY_SOURCE_KEYS_RE to the two
indents parse_source_keys actually reads.

agent-audit had no INFO tier at all, so it gains one rather than reporting a
check that could not run as a FAIL.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EJJrm5YmacbwMdzZpXcoti
2026-08-31 19:46:05 +00:00
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
3cd3f33706 refactor(pc-author): retrofit to the ADR-0020 context contract
Description 475 -> 213 chars, body 680 -> 212 words. Create and modify
become self-contained flow files behind a dispatch table, since the two are
mutually exclusive on whether the config already exists.

Passed its clean-context audit with no must-fix findings.
2026-08-30 13:10:53 +00:00
15ff7417b9 refactor(pc-run): retrofit to the ADR-0020 context contract
Description 477 -> 211 chars, body 736 -> 367 words. Install, autoupdate,
and clean become flow files behind the Route table.

The audit found no route for 'hooks aren't running': the only pointer to
failure-patterns.md sat inside the failure path, but when hooks never fire
the manual run succeeds, so the request fell to the ambiguity default and
was masked. Restores the disclosure that install -f is not reversible by
uninstall.
2026-08-30 13:10:53 +00:00
7cb8e95379 refactor(git-history): retrofit to the ADR-0020 context contract
Description 450 -> 266 chars, body 1044 -> 462 words. The bisect procedure
moves to references/bisect.md.

The boundary clause read 'not writing or rewriting commits', which
disclaimed the cherry-pick and revert this skill's own Step 3 executes; it
now excludes authoring and rebasing only. Adds 'backport' so cherry-pick is
reachable by natural language at all, accepting 266 chars against a 250
soft target and a 400 ceiling.
2026-08-30 13:10:53 +00:00
261e5b5491 refactor(git-submodules): retrofit to the ADR-0020 context contract
Description 480 -> 248 chars, body 1011 -> 347 words. The single
submodules.md splits into setup-and-update, urls-and-config, and removal.

Restores three regressions the first pass introduced: 'repointing' as the
trigger for the URL branch, which had none while the boundary clause
steered those queries to git-remotes; clone and absorbgitdirs in the output
enum, which dispatch still routed to; and status --cached, the flag that
makes the pre-commit pointer gate verifiable.
2026-08-30 13:10:53 +00:00
3c74beb280 refactor(git-workflow): retrofit to the ADR-0020 context contract
Description 566 -> 249 chars, body 644 -> 398 words, Gotchas 12 -> 2. The
eight organisational hard rules move to references/hard-rules.md.

Its load trigger enumerates operations rather than rule topics: the first
draft keyed on 'commit message form', which left the atomicity and
working-state rules unreachable when a caller supplied a conventional
message. Also promotes the destructive-op confirmation ahead of the
orchestrator invocation, which it previously followed.
2026-08-30 13:10:53 +00:00
38eb0745b7 refactor(git-remotes): retrofit to the ADR-0020 context contract
Description 582 -> 237 chars, body 1217 -> 290 words. The single remotes.md
splits into config, fetch, push, and pull flow files.

Restores the confirm: true token to the force-push gate -- it is the git
plugin's cross-skill contract, gated on by git-orchestrate and git-branches.
Moves push.md's worked example off main, which the skill's own Step 1
refuses, and restores the never-bare---force directive.
2026-08-30 13:10:53 +00:00
3dd5387671 refactor(git-worktrees): retrofit to the ADR-0020 context contract
Description 592 -> 248 chars, body 756 -> 515 words, Gotchas 8 -> 4.

The audit found the dispatch table had no row for a worktree on an
existing local branch, so that request fell to the adjacent -B row, which
resets the branch to HEAD and discards its commits. Non-destructive create
is now the first row and -B names its own destructiveness. Adds the missing
lock/unlock row and repair's run-from constraint.
2026-08-30 13:10:53 +00:00
0fde892f20 refactor(git-branches): retrofit to the ADR-0020 context contract
Description 612 -> 273 chars, body 1124 -> 457 words. Branch patterns,
operations, merging, comparison, and the orchestrator contract move to
references/.

Corrects rebase routing in four places: this skill sent rebase to
git-history, which carries no rebase content and disclaims it. Rebase is
git-commits'; cherry-pick and revert stay git-history's. Drops a Step 3
gate on a rebase flow this skill does not have.
2026-08-30 13:10:53 +00:00
f3b4860e14 refactor(git-commits): retrofit to the ADR-0020 context contract
Description 724 -> 214 chars, body 1102 -> 343 words, Gotchas 12 -> 3.
Create, rewrite, and cherry-pick flows move to self-contained references/
files behind a dispatch table.

The audit caught that moving the secret scan into the create flow left the
amend/squash path with no check in its loaded context; it is now a gate
common to every flow. Also re-homes the interactive-rebase reflog warning
git-history dropped, since this skill owns rebase.
2026-08-30 13:10:53 +00:00
bbc73008a3 chore(gitea): regenerate the flat content mirror after the ADR-0020 retrofit
Generated output, not authored content: scripts/sync-plugin-content.sh --all.
Claude Code has no .apm/ awareness, so this compiled mirror must track .apm/ or
the check-plugin-content-sync pre-push hook reports drift.

Deferred to a single commit at the end of the wave on purpose. sync_dir runs
rm -rf before every copy, so running it while seven agents were editing the
same plugin would have raced them; agents were told not to sync for that reason.

Refs #99
2026-08-30 12:42:20 +00:00
bedbd1d872 refactor(gitea-workflow): retrofit to the ADR-0020 context contract
Description 1012 -> 347 chars, body 582 -> 170 words, Gotchas 3 entries -> 1 at
22.9% of body. Clears the description FAIL and all four Vale CompositionNote
errors -- the last carriers in the corpus, so that rule now fires nowhere.

Cut the 'human-facing entry point and router' architecture note, the /gitea
migration history and the six-skill composition list; all were already in the
README or the routing table.

Split three mutually exclusive flows into a dispatch table keyed on invocation
shape, each branch self-contained in references/: status-checkin.md,
number-resolution.md, skill-index.md. Report stays in the body as the gate
common to every branch; each branch's own format moved to its file. The old
Step 1-4 numbering presented three alternatives as a sequence.

The description grew from an intermediate 283 chars on purpose: that draft had
dropped flow 3's trigger entirely, leaving the domain-routing index -- a third
of the skill -- reachable only through a tail clause whose grammatical subject
was the request rather than the skill. Both gates were green over that.

Boundary clauses are one arrow per target, so both resolve (#107: the resolver
extracts only the first target per clause and reports 1 of 1 on a clause naming
two). The local-git exclusion keeps its wording but drops the route to
git-workflow, which would not resolve in a gitea-only install.

Known residual: the dispatch conditions are stated twice, as a table and as
literal conditionals. That is #109 -- body-discipline.md mandates the literal
form while the ADR's cited exemplar, apm-workflow, uses a bare table plus one
summary line. Fixing it here would settle that contradiction in a skill rather
than in the spec, so it rides with #109.

Refs #99, #107, #109
2026-08-30 12:42:04 +00:00
b8c36c36f6 docs(agents): correct the ADR-0020 gate counts to measured state
The bullet is always-loaded context, and all three of its numbers had gone
stale as the issue #99 retrofit landed. Measured now: 19 of 39 descriptions and
7 of 39 bodies over their FAIL tier, down from 26 and 9; one dangling routing
target left (research -> neuledge-context), down from two.

Kyberforge.CompositionNote fired 10 errors across four gitea-* skills and now
fires nowhere -- those four were the only carriers and all four have been
retrofitted. The 'check both gates' advice stays: skill-size-check still does
not cover the Vale half, and any new description can reintroduce the rule.

Refs #99
2026-08-30 12:35:48 +00:00
23b843a311 test: drop the gitea-labels pin now the retrofit removed that target
tests/test-adr0020-targets.sh pinned the corpus dangling set as exactly
{gitea-labels, neuledge-context} and tests/test-skill-size-check.sh probed both
individually. The gitea-issues retrofit cut the composition sentence whose
'>'-folded scalar produced 'gitea-labels- milestones', so that target no longer
exists and both suites went red.

EXPECTED_DANGLING is now {neuledge-context} and the gitea-issues probe is
removed rather than skipped, per the rule the probe file states about itself: a
probe whose fixture has been retrofitted is a pin that needs updating, not an
assertion-free pass counted in the totals.

The exact-set assertion stays. An empty expected set is still valid and still
pins that no new dangling target appeared -- which is what it becomes once
research is retrofitted. Both loops carry a shellcheck SC2043 waiver for the
same reason: one entry is the expected steady state, not bad quoting.

Refs #99
2026-08-30 12:34:24 +00:00
37382cb72a refactor(gitea-labels-milestones): retrofit to the ADR-0020 context contract
Description 835 -> 214 chars, body 669 -> 426 words, Gotchas 8 entries/63% of
body -> 3/20.4%. Clears the description FAIL, all three Vale CompositionNote
errors and both Gotchas suggestions. 63% was the worst Gotchas ratio in the
corpus.

Cut the composition sentence to README -- it changes no routing decision and an
agent picks this skill because the user asked about labels, not because two
other skills call it. Cut the capability enumeration; 'list, create, edit,
delete' decompose 'reading or writing' and add no trigger.

Boundary clauses are now one arrow per target. The resolver extracts only the
first name per arrow clause, so the previous '-> gitea-issues / gitea-prs' left
gitea-prs neither dangling nor checked while validate.sh reported 1 of 1. Now
2 of 2.

Makes org-scoped label resolution executable. The org label pool was reachable
in principle -- four *_org_label* methods, and a claim to own name-to-ID
resolution -- but list_org_labels takes org, and Step 1 derived only owner and
repo, so both resolution procedures stalled at the fallback. Fixed once at the
identity step rather than per-procedure. get_user_orgs is outside allowed-tools,
so the failing call is the discriminator: a failure means the owner is a user
account with no org pool, which is an answer, not an error.

Corrects a Gotcha that was false for create_repo_label/create_org_label and
collided with the literal tool name label_write. Same false claim removed from
README.

Refs #99
2026-08-30 12:29:18 +00:00
78015893d4 refactor(gitea-issues): retrofit to the ADR-0020 context contract
Description 827 -> 182 chars, body 902 -> 584 words, Gotchas 7 entries/43% of
body -> 4/23.1%. Clears both size FAILs, the dangling-target FAIL and the Vale
CompositionNote error.

The dangling 'gitea-labels' target is gone with the composition sentence that
carried it -- a YAML fold artifact, not a typo: the '>'-folded scalar joined
'gitea-labels-' and 'milestones' across a line break, leaving the name terminal
and danglable. Both boundary names now sit whole on one source line each, and
both resolve.

Cut the second trigger register, the seven-verb capability enumeration, the
issue_write implementation detail and a gitea-branches boundary that defended
nothing. Two Gotchas deleted as spec restatement carried in references/, one as
a paraphrase of the step below it.

Closes a capability hole rather than papering over it: gitea-labels-milestones
routes label application here and has no issue_write, but no dispatch row
existed for add/replace/remove/clear_labels or get_labels. Both rows added.
The label ID/name Gotcha is now stated per method -- issue_write takes IDs,
list_issues and search_issues filter by name, issue_read 'get' returns names
but 'get_labels' returns full objects with IDs.

Drops a stale quotation of gitea-labels-milestones' description from
references/enrichments.md; that string went with its composition note.

Refs #99
2026-08-30 12:28:54 +00:00
0079f3508c fix(gitea-prs): correct reference drift against gitea-mcp v1.7.0
The reference files were last verified against v1.3.0 -- references/sources.md
still said so. PR #106 re-verified the write side only, so three defects had
accumulated on the read/review side. All three reproduced against the deployed
server before being fixed; get_gitea_mcp_server_version reports v1.7.0.

- reviews.md forbade review_comments on the "get" response and directed callers
  to review_scomments, which does not exist. The upstream slim.go typo was
  corrected; a live pull_request_read on PR #106 returns "review_comments":1 and
  no review_scomments key. review_comments is an integer count, not comment
  objects -- distinct from the get_review_comments method. Also fixed in
  pull-requests.md's response-shape list.
- pull_request_review_write grants seven methods; only four were documented.
  reply_comment, resolve_thread, unresolve_thread and the comment_id parameter
  had zero mentions anywhere in the skill. Documented from the schema, in a
  Comment threads section kept outside the numbered review state machine --
  they are not lifecycle states.
- review_id was documented as required for get_review_comments. It is optional;
  omitting it lists every inline comment on the PR. Confirmed behaviourally:
  get_review without it errors, get_review_comments without it returns [].

sources.md now records v1.7.0 as the last-verified version, so the next reader
knows what these files were checked against.

Refs #99
2026-08-30 12:21:11 +00:00
d578d6b2f2 refactor(gitea-prs): retrofit to the ADR-0020 context contract
Description 709 -> 161 chars, body 683 -> 353 words, Gotchas 8 entries/56% of
body -> 2/24.9%. Clears the description FAIL and both Vale CompositionNote
errors.

Fixes the three stale claims recorded on issue #99, all re-verified against the
deployed gitea-mcp schema during review:

- The description no longer advertises 'reviewers' as an update capability.
  editPullRequestFn never reads reviewers or team_reviewers; only
  add_reviewers/remove_reviewers do.
- milestone is now marked honoured on "update" only, in the Gotcha, the body
  and the dispatch table's create row. On "create" the server discards it and
  omits the key from the response, so the drop is indistinguishable from never
  passing it -- and labels DOES apply on create, so labels landing is no
  evidence the milestone did. The old text told callers to resolve a milestone
  before any write, wasting the lookup on create.
- The superseded un-draft workaround is gone. "update" with draft:false and no
  title makes the server strip the prefix itself, including [WIP],
  case-insensitively -- carried by references/pull-requests.md, corrected in
  PR #106.

The 22-row tool/method table becomes a 5-row dispatch table; all 20 operations
it named remain reachable, including update_branch and the reviewer methods.

Refs #99
2026-08-30 12:10:21 +00:00
d5954d3d99 refactor(gitea-files): retrofit to the ADR-0020 context contract
Description 787 -> 263 chars, body 922 -> 302 words, Gotchas 9 entries/69% of
body -> 3/24.8%. Clears both size FAILs and both Gotchas suggestions.

Deleted the second trigger register outright -- ~300 chars re-quoting the same
six verbs as user phrasings, which ADR-0020 names this skill for specifically.

Split the body on the read/write boundary: one invocation cannot both read and
write, so a dispatch table is mandatory. Six operations collapse into two flow
files rather than six -- create/update/delete share one tool pair and one
SHA-first lifecycle whose preamble would otherwise be triplicated, and the three
read tools share ref selection plus a 'neither listing is a SHA source'
comparison that only exists between them.

references/examples.md removed; all eight of its content blocks and all nine
named parameters carry into references/writing.md, verified against git HEAD.
Two deltas are corrections: the repo tree is now ruled out as a SHA source, and
reusing a SHA captured earlier in the conversation is now forbidden.

Four Gotchas relocated to the flow file that needs them; two promoted to gates
(SHA-as-concurrency-token opens writing.md; owner/repo became ## Inputs).

Refs #99
2026-08-30 12:09:52 +00:00
ee812699a4 refactor(gitea-branches): retrofit to the ADR-0020 context contract
Description 688 -> 282 chars, body 435 -> 293 words, Gotchas 5 entries/54% of
body -> 3/23.5%. Clears the description FAIL and the Gotchas suggestion.

Cut the second trigger register (six re-quoted user phrasings) and the capability
enumeration; both moved to a new Boundaries section in the skill's own README.
Kept all three boundary clauses -- git-branches, git-history and gitea-prs each
defend a real activation steal, and gitea-prs is now the only guard on the
branch/PR collision in either direction since gitea-prs's own retrofit narrowed
its boundary to issues. Written as one arrow per target: the resolver extracts
only the first name per arrow clause, so conjoined targets go unchecked.

Two Gotchas deleted -- one paraphrased the step below it (its non-obvious half,
the get_me/list_my_repos token-scope block, was folded into that step), the other
is carried in full by references/branches.md:40-52.

Repoint two reference pointers the rename broke: branches.md and commits.md named
Gotchas by titles this retrofit changed. Now named by stable descriptors.

Refs #99
2026-08-30 12:08:38 +00:00
dfacf051a8 refactor(gitea-releases): retrofit to the ADR-0020 context contract
Description 500 -> 211 chars, body 676 -> 462 words, Gotchas 5 entries/41% of
body -> 3/21%. Clears the description FAIL and both Gotchas suggestions.

Cut the second trigger register (the re-quoted user phrasings), the doubled
capability enumeration across releases and tags, and the gitea-issues/gitea-prs
boundary, which defended against nothing -- neither was going to win a release
request. Kept the indirect trigger and the one real near-miss, gitea-branches.

Releases and tags are one flow, not two: 'delete the release and its tag' is a
single invocation that takes both branches, which disqualifies mutual
exclusivity, so no dispatch split. Two Gotchas moved to references/ behind
explicit triggers; one deleted as a paraphrase of the step below it.

Refs #99
2026-08-30 12:06:37 +00:00
ef3e981eac docs(context): record that hand-invocation blocks skill-to-skill routing
The Hand-invoked skill entry covered listing visibility and preload tax but not
invocability. Measured: disable-model-invocation: true hard-blocks the Skill
tool, not just the model-visible listing, so a `Call `x`` step in another
skill's body stops working the moment x takes the flag.

ADR-0020's 'Invocation as a design axis' verified only the visibility and slash
directions. Without this half, marking grill-with-docs hand-invoked -- one of
issue #99's four named candidates -- would silently break forge Step 1, which
calls it, plus three other inbound routes.

Refs #99
2026-08-30 11:54:26 +00:00
5f984ce34a chore(apm): refresh lock after SessionStart update
The SessionStart hook found six packages behind the remote default branch and
ran apm update. Committing the resulting lock separately so it does not sit in
the ADR-0020 retrofit diff.
2026-08-30 11:54:18 +00:00