88 Commits

Author SHA1 Message Date
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
192 changed files with 971 additions and 4514 deletions

View File

@@ -11,28 +11,28 @@
{
"name": "kyberforge",
"description": "Skills and agents for creating, maintaining, and managing a Claude Code / Copilot CLI plugin marketplace.",
"version": "1.6.2",
"version": "1.6.1",
"category": "Developer Tools",
"source": "./plugins/kyberforge"
},
{
"name": "bin",
"description": "Skills for everyday AI-assisted development work that is not tied to a single tool, forge or language, and has not yet been split into a focused plugin.",
"version": "1.1.7",
"version": "1.1.6",
"category": "Utilities",
"source": "./plugins/bin"
},
{
"name": "git",
"description": "Skills and agents for working with a local Git clone over the git wire protocol, and for authoring and running the pre-commit hooks that guard it.",
"version": "1.3.7",
"version": "1.3.6",
"category": "Version Control",
"source": "./plugins/git"
},
{
"name": "gitea",
"description": "Skills and agents for working with a Gitea forge through its HTTP API — the forge's own objects, as distinct from the local git clone.",
"version": "1.3.8",
"version": "1.3.7",
"category": "Version Control",
"source": "./plugins/gitea"
},

View File

@@ -11,28 +11,28 @@
{
"name": "kyberforge",
"description": "Skills and agents for creating, maintaining, and managing a Claude Code / Copilot CLI plugin marketplace.",
"version": "1.6.2",
"version": "1.6.1",
"category": "Developer Tools",
"source": "./plugins/kyberforge"
},
{
"name": "bin",
"description": "Skills for everyday AI-assisted development work that is not tied to a single tool, forge or language, and has not yet been split into a focused plugin.",
"version": "1.1.7",
"version": "1.1.6",
"category": "Utilities",
"source": "./plugins/bin"
},
{
"name": "git",
"description": "Skills and agents for working with a local Git clone over the git wire protocol, and for authoring and running the pre-commit hooks that guard it.",
"version": "1.3.7",
"version": "1.3.6",
"category": "Version Control",
"source": "./plugins/git"
},
{
"name": "gitea",
"description": "Skills and agents for working with a Gitea forge through its HTTP API — the forge's own objects, as distinct from the local git clone.",
"version": "1.3.8",
"version": "1.3.7",
"category": "Version Control",
"source": "./plugins/gitea"
},

View File

@@ -254,79 +254,17 @@ repos:
entry: bash
language: system
files: '^plugins/[^/]+/\.apm/skills/[^/]+/SKILL\.md$'
# Pinned by tests/test-skill-frontmatter.sh, which drives this exact
# `bash -c <script> <arg0> <files...>` call shape rather than a copy of
# the script -- the bug below was invisible to any test that did not.
args:
- -c
- |
# Every check reads the FRONTMATTER only, never the whole file. A
# `metadata:` / `name:` / `description:` line inside a body code
# fence is documentation (skill-author quotes exactly such a block)
# and used to satisfy these greps.
for f in "$@"; do
[[ -f "$f" ]] || continue
fm="$(awk '
{ sub(/\r$/, "") }
NR == 1 { sub(/^\357\273\277/, "") }
!opened && /^[[:blank:]]*$/ { next }
!opened {
if ($0 ~ /^---[[:blank:]]*$/) { opened = 1; next }
exit
}
/^---[[:blank:]]*$/ { closed = 1; exit }
{ print }
END { if (!opened || !closed) exit 3 }
' "$f")" || {
echo "ERROR: $f has no closing YAML frontmatter block (expected --- ... --- at the top of the file)"
exit 1
}
missing=""
printf '%s\n' "$fm" | grep -q "^name:" || missing="${missing}name: "
printf '%s\n' "$fm" | grep -q "^description:" || missing="${missing}description: "
# Scoped to the `metadata:` block and stopped at the next
# top-level key, so a `version:` under a following `source:` list
# cannot stand in for it; the `^ version:` anchor is exact, so a
# deeper-nested ` version:` cannot either. No line budget, so a
# long `metadata:` block does not hide the key.
ver="$(printf '%s\n' "$fm" | awk '
/^metadata:/ { inm = 1; next }
inm && /^[A-Za-z]/ { exit }
inm && /^ version:/ {
v = $0
sub(/^ version:[[:blank:]]*/, "", v)
sub(/[[:blank:]]+#.*$/, "", v)
sub(/[[:blank:]]+$/, "", v)
print "found:" v
exit
}
')"
[[ -n "$ver" ]] || missing="${missing}metadata.version "
if [[ -n "$missing" ]]; then
echo "ERROR: $f is missing required frontmatter fields (${missing})"
exit 1
fi
raw="${ver#found:}"
v="$raw"
case "$v" in
\"*\") v="${v#\"}"; v="${v%\"}" ;;
\'*\') v="${v#\'}"; v="${v%\'}" ;;
esac
if [[ ! "$v" =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
echo "ERROR: $f has a malformed frontmatter metadata.version (${raw:-<empty>}) -- expected a three-part semver, e.g. \"1.0.0\""
exit 1
if [[ -f "$f" ]]; then
if ! grep -q "^name:" "$f" || ! grep -q "^description:" "$f"; then
echo "ERROR: $f is missing required frontmatter fields (name: and description:)"
exit 1
fi
fi
done
# arg0 for `bash -c`. WITHOUT it pre-commit's first filename lands in
# $0 and is dropped from "$@" -- so a single-file commit, the normal
# case, ran the loop zero times and reported Passed having checked
# nothing. Do not remove; tests/test-skill-frontmatter.sh pins it.
- skill-frontmatter
- id: skill-size-check
stages: ['pre-commit']
@@ -346,21 +284,6 @@ repos:
# records for Vale warnings. Costs nothing on a clean file: the script
# prints only findings.
- id: check-rtk-prefix
stages: ['pre-commit']
name: ADR-0023 rtk prefix on executable git commands
description: Enforce ADR-0023 clause 1 -- an executable, instructed git command in a shell code fence or a dispatch-table Run cell is written `rtk git`. Clauses 2 and 3 are not machine-decidable; a deliberately bare command opts out with the literal string ADR-0023 on its own line
entry: scripts/check-rtk-prefix.sh
language: script
files: '^plugins/[^/]+/\.apm/(skills/.*\.md|agents/.*\.agent\.md)$'
# README.md is excluded on purpose, not by oversight. A skill-directory
# README is consumer-facing prose that no agent ever loads, and the
# `git clone` lines in the seven tests/README.md files are setup
# instructions for a third party who has no rtk installed. Prefixing
# those would be actively wrong -- see ADR-0023's consumer section.
exclude: '(^|/)README\.md$'
pass_filenames: true
- id: vale-audit-prefilter-skill
stages: ['pre-commit']
name: Vale audit prefilter (SKILL.md)

View File

@@ -15,8 +15,6 @@ This file carries only what applies to **every** session. Setup, prerequisites,
Not everything in a plugin root is generated. `README.md`, `docs/`, `bin/`, `sources.md`, `.mcp.json` and per-plugin extras are hand-authored there with no `.apm/` source — edit those in place. The rule is per-path, not per-directory. But a file placed *inside* a mirrored directory is deleted on the next sync (`sync_dir` runs `rm -rf` before every copy), so plugin-root documentation goes in `docs/`, never in `hooks/` or `skills/`.
`.mcp.json` is hand-authored but it is **not** outside apm. MCP is a first-class apm primitive, and a plugin's `.mcp.json` is how this repo declares one: apm reads the `mcpServers` pointer in the generated `.github/plugin/plugin.json`, resolves it to `.mcp.json`, and injects the result into that package's `dependencies.mcp` when a consumer installs it. Declare MCP servers there and **never** in the plugin's own `apm.yml` — that arms a per-package gate this repo cannot satisfy (`LESSONS.md`, 2026-09-12).
Full model: `docs/spec/architecture.md`.
## Prefer plugin skills over raw shell
@@ -38,7 +36,11 @@ Fall back to raw shell only when no skill covers it.
- **Do not add repo-owned keys to `.claude/settings.json`.** apm treats it as its own deployed artifact and `apm audit --ci` replays the install and diffs, so anything apm would not have written is permanent drift that fails the `apm-audit-ci` pre-push hook. A hook you want here is authored in `plugins/<name>/.apm/hooks/` and deployed by apm, never hand-written into that file. The `SessionStart` entry already in it is exactly that: kyberforge authors it in `plugins/kyberforge/.apm/hooks/hooks.json` and apm merges it in, so it is apm's own output, it is what the replay expects, and it belongs in the commit — do not strip it (ADR-0019). Machine-specific settings go in the gitignored `.claude/settings.local.json`; shared enforcement goes in `.pre-commit-config.yaml`.
- **`apm.lock.yaml` turning up modified is expected, not a bug.** kyberforge's `SessionStart` hook runs `apm outdated` at startup and `apm update --yes` when something is behind, which rewrites the lock. Commit or discard it deliberately.
- **A `.apm/` edit is not live in this session until it is pushed.** The six dependencies resolve from the holocron remote, unpinned against the default branch. `apm install` deploys from the lock; `apm update` is what re-resolves refs.
- **The ADR-0020 skill gates ship hot, with no baseline — and the corpus is now clean.** All 39 skills clear both FAIL tiers: no description over 400 characters, no body over 900 words (counted body-only). Retrofitted plugin by plugin under #99 (see `docs/spec/gates.md`). Because nothing is grandfathered, the gates now bite on first commit — a new skill, or an edit that pushes a description past 400, is blocked until it complies. **No routing target dangles**, and `tests/test-adr0020-targets.sh` pins that set as empty, so a new boundary clause naming a non-existent skill fails the suite rather than joining a backlog. Two blind spots survive: `skill-size-check` does not cover the Vale half, so `Kyberforge.CompositionNote` fires nowhere today but any new description can reintroduce it; and no `references/` file is linted by anything, so prose relocated out of a body to satisfy the word gate lands outside the prose gate. It has two independent causes and closing either alone changes nothing — `docs/spec/gates.md` has both, issue #117 tracks it. Check both gates: `pre-commit run --all-files`.
- **Run `bash tests/run-tests.sh --strict` before considering any change done.** Keep the flag: without it a suite whose dependency is missing exits 77 and is counted SKIPPED rather than failed, so the run goes green having verified less than it claims.
- **Before pushing, rehearse the gate locally:** `pre-commit run --hook-stage pre-push --all-files`. It runs the 14 pre-push hooks this repo authors itself plus pre-commit's 2 `meta` hooks, so it prints 16; `check-release-needed` passes without checking anything, because it needs a real push to `main`. `docs/spec/gates.md` reconciles both.
- **Pushing without a network** needs `SKIP=apm-marketplace-check,apm-pack-check-clean git push` — those two resolve a remote marketplace entry via `git ls-remote`. Skip only those two; the rest are real local checks, and adding one to `SKIP` disarms it silently.
- **Author commits with `git-commits`** — it validates Conventional Commits, which `commit-msg` enforces.
- **This repo and Gitea are the only source of truth.** All project state, decisions, and working conventions live here. Do not use an external memory system for this project — cached state diverges from the repo and you get a split brain. Before answering any design or architecture question, check `docs/adr/` for an existing decision.
## Key documents

View File

@@ -10,14 +10,6 @@ Patterns observed during development of this repo. Three or more entries on the
---
## 2026-09-12 — "Not an `.apm/` primitive" was read as "not an apm primitive", and the review that followed was wrong
`AGENTS.md` and `docs/spec/architecture.md` both listed `.mcp.json` alongside `README.md`, `docs/` and `bin/` as plugin-root material "hand-authored with no `.apm/` source". That is literally true — nothing under `.apm/` produces MCP config — but it reads as "apm has no MCP concept", and a review of PR #132 drew exactly that conclusion and recommended moving the declaration into the plugin's `apm.yml` under `dependencies.mcp`. The recommendation was wrong twice over. It arms `lockfile-exists` in the per-package `apm audit --ci` that the `apm-audit-ci` hook runs in every `plugins/*/`, which then demands the package's whole deployed tree inside the package directory: 93 missing files and 79 drifted paths on `plugins/gitea`. And it was unnecessary, because the `.mcp.json` route already reaches `dependencies.mcp` through the `mcpServers` pointer in the generated Copilot manifest, env references intact.
Two process lessons, not one. First, when a doc says a file is not a primitive **of a specific subsystem**, say which subsystem and what the file actually is instead — the negative claim alone invites the wrong generalisation. Second, the three scratch installs that produced the wrong conclusion all used local `./path` dependencies, where apm skips the plugin-normalisation step that injects `.mcp.json`. The repo consumes its plugins as `git:` + `path:` objects. A scratch test that does not reproduce the real dependency form can invert the result, so reproduce the form, not just the shape.
---
## 2026-05-17 — Workflow documents should prescribe sub-agent usage, not just allow it
When writing workflow documents (like `docs/notes/skill-implementation-workflow.md`), the natural tendency is to describe steps at a high level and leave sub-agent usage as an implementation detail. But if the workflow doesn't explicitly prescribe "spawn a sub-agent here," practitioners default to doing everything in the main context — accumulating token cost and losing the isolation benefit. Fix: make sub-agent usage a named step in the workflow, specifying what the agent receives, what it returns, and why it's isolated. This makes the workflow reproducible rather than dependent on the practitioner remembering to use agents.
@@ -68,21 +60,29 @@ write-eval's process requires presenting the full test plan and waiting for user
The write-skill authoring standard required 8 body sections including Role and When/When not. These were assumed to be agentskills.io requirements. Checking the actual spec revealed the body has no format restrictions at all — recommended sections are step-by-step instructions, examples, and edge cases. Role and When/When not were added by convention without verifying the standard. Fix: before encoding any requirement as part of an authoring standard, check the upstream spec directly. The agentskills.io spec also confirmed that negative triggers belong in the description field — not in a separate body section — which eliminates a persistent duplication pattern across all skills.
## 2026-05-18 — Provenance fields in frontmatter are loaded on every skill scan
Fields like `source:`, `references:`, `version:`, `updated:`, and `when:` in SKILL.md frontmatter are loaded at agent startup alongside `name` and `description` for every installed skill. None of these are used for routing or runtime execution — they are audit and upgrade-cycle records. Loading them at startup violates progressive disclosure and wastes tokens proportional to the number of installed skills. Fix: move all non-routing frontmatter to a separate `META.md` file in the skill directory. Frontmatter keeps only `name`, `description`, `metadata.category`, and `allowed-tools` (when applicable) — the four fields the spec actually uses for routing and discovery.
## 2026-05-18 — Copy-fill is more deterministic than generate for structured skill artifacts
When a skill produces a structured artifact like SKILL.md, the natural approach is to generate it from internalized rules in the Process section. But this means section structure is only as reliable as the agent's instruction-following under token pressure. Copy-fill (copy the template to the target path, then fill in content) separates structure from content: the template mechanically enforces section order and presence, freeing the Process section to focus only on sequencing constraints (what order to decide things) rather than also policing structure. Side benefit: the template is a human-usable artifact that can be adopted independently of the skill. Fix applied in write-skill refactor: SKILL-TEMPLATE.md is the authoritative structure source; the Process section no longer contains a body structure constraint — the template handles it.
When a skill produces a structured artifact like SKILL.md, the natural approach is to generate it from internalized rules in the Process section. But this means section structure is only as reliable as the agent's instruction-following under token pressure. Copy-fill (copy the template to the target path, then fill in content) separates structure from content: the template mechanically enforces section order and presence, freeing the Process section to focus only on sequencing constraints (what order to decide things) rather than also policing structure. Side benefit: the template is a human-usable artifact that can be adopted independently of the skill. Fix applied in write-skill refactor: SKILL-TEMPLATE.md and META-TEMPLATE.md are the authoritative structure sources; the Process section no longer contains a body structure constraint — the template handles it.
## 2026-05-17 — HITL gap: agent delegates confirmation to permission system
The agent-level HITL rule ("require explicit confirmation before irreversible shared-state operations") is being bypassed: the agent calls the tool and lets the permission dialog catch it. This means the rule is not firing in agent reasoning — it's the permission system acting as a safety net. If a user selects "don't ask again," the net disappears. Fix: the HITL rule needs to be framed as "do not call the tool" rather than "ask before proceeding" — the agent must ask first, then act only after explicit confirmation.
## 2026-05-26 — META-TEMPLATE uses YAML comments; META.md output retains them
META-TEMPLATE.md uses YAML `#` comments to explain fields inline. SKILL-TEMPLATE.md uses HTML comments inside XML tags, which the agent strips on fill. The structural difference means SKILL.md output is clean but META.md output retains the explanatory `#` lines — an inconsistency. Fix (deferred): restructure META-TEMPLATE.md so all explanatory guidance is prose above the code block (markdown, never copied into the output YAML), and the code block itself uses `<placeholder>` syntax with no `#` comment lines. This makes META.md fill behaviour deterministic for the same reason SKILL.md fill is: `<...>` markers are unambiguously replaceable; prose above the block is not part of the template. Do not apply until the human/copy-fill tradeoff is resolved — see 2026-05-26 session discussion.
## 2026-05-26 — Overlap checks must scan the deployed directory, not just the source repo
`write-a-skill` existed only in `~/.agents/skills/` (installed from a pre-refactor source) and was invisible during a repo-level scan of `.agents/skills/`. Governance reviews and overlap checks that only look at the source repo will miss skills added by install.sh from other sources or prior runs. Fix: overlap checks must scan the deployed `~/.agents/skills/` directory, not just the repo's `.agents/skills/`.
## 2026-05-26 — `model:` field belongs in SKILL.md frontmatter, not a sidecar file
## 2026-05-26 — `model:` field belongs in SKILL.md frontmatter, not META.md
Claude Code supports `model:` as a provider extension in SKILL.md frontmatter — it overrides the session model for the skill's turn and reverts after. Attempting to move it out to a provenance sidecar was wrong: a sidecar is audit metadata, not runtime config. The boundary: if a field affects agent behaviour at invocation time, it belongs in SKILL.md frontmatter.
Claude Code supports `model:` as a provider extension in SKILL.md frontmatter — it overrides the session model for the skill's turn and reverts after. Attempting to put it in META.md was wrong: META.md is provenance/audit metadata, not runtime config. The boundary: if a field affects agent behaviour at invocation time, it belongs in SKILL.md frontmatter; if it serves upgrade reviews and audit trails, it belongs in META.md.
## 2026-05-26 — Research agents present synthesis as spec fact
@@ -126,7 +126,7 @@ Two forks independently fixed `references/sources.md` with different approaches
## 2026-06-28 — Implementation agents must invoke /skill-author, not write skill files directly
When briefing an agent to implement a new skill, the instinct is to tell it to write the SKILL.md and supporting files directly. This bypasses Step 5 of the skill-author process (provenance), which requires reading all research `sources.md` files and recording every `extracted` slug in the skill's own `references/sources.md`. The `validate-provenance.sh` script catches the gap — but only after the commit, requiring a fix round. This pattern recurred twice in one session (plugin-author and marketplace-author initial implementation, then again in the first round of fix agents). Fix: briefs for implementation agents must explicitly say "invoke `/skill-author` (read and follow `plugins/kyberforge/.apm/skills/skill-author/SKILL.md`)" — not "write the skill files." Invoking the skill is the only reliable way to ensure all process gates, including provenance, run.
When briefing an agent to implement a new skill, the instinct is to tell it to write the SKILL.md and supporting files directly. This bypasses Step 5 of the skill-author process (provenance), which requires reading all research `sources.md` files and recording every `extracted` slug in META.md. The `validate-provenance.sh` script catches the gap — but only after the commit, requiring a fix round. This pattern recurred twice in one session (plugin-author and marketplace-author initial implementation, then again in the first round of fix agents). Fix: briefs for implementation agents must explicitly say "invoke `/skill-author` (read and follow `plugins/kyberforge/.apm/skills/skill-author/SKILL.md`)" — not "write the skill files." Invoking the skill is the only reliable way to ensure all process gates, including provenance, run.
## 2026-07-05 — Repo root is a bare checkout; work happens in worktrees only

View File

@@ -35,17 +35,6 @@ Install all of these before setting up. Each one is a hard dependency of a git h
| `python3` + PyYAML | Required by `scripts/skill-size-check.sh` (the `skill-size-check` pre-commit hook), which reads folded YAML frontmatter | `python3` is usually present — pre-commit is itself a Python application. `pip install pyyaml` if the hook reports PyYAML missing |
| `vale` | Required by the `vale-audit-prefilter-skill` / `-agent` pre-commit hooks and the `check-vale-style-sync` pre-push hook | `brew install vale` (macOS), `snap install vale` (Linux), `choco install vale` (Windows), or https://vale.sh/docs/vale-cli/installation/ |
| `claude` CLI | Required by the `validate-plugins` and `validate-marketplace` pre-push hooks | Claude Code |
| `go` toolchain | The gitea MCP server runs as `go run gitea.com/gitea/gitea-mcp@v1.7.0`, resolved from `PATH`. Without it the server fails to start and every `gitea-*` skill loses its tools | https://go.dev/dl/ — verify with `go version` |
The gitea MCP server additionally needs two environment variables in the shell that launches your agent — referenced as `${GITEA_ACCESS_TOKEN}` and `${GITEA_HOST}` in `plugins/gitea/.mcp.json`, with apm passing those references through to the deployed config unexpanded so the values are resolved at server startup and never committed. Copy `plugins/gitea/.env.example` to `.env` at the repo root, fill in real values, then export it — nothing in this repo auto-loads a `.env` file:
```bash
cp plugins/gitea/.env.example .env
$EDITOR .env
set -a; source .env; set +a
```
Generate the token in Gitea under Settings, Applications. Scope it to the repositories you want the agent to reach. If the server starts but every call returns an authorization error, that token is the first thing to check.
Two notes worth reading before you skip one:

File diff suppressed because it is too large Load Diff

10
apm.yml
View File

@@ -42,7 +42,7 @@ dependencies:
# after a kyberforge release, check this first.
executables:
allow:
kyberforge#1.6.2:
kyberforge#1.6.1:
hooks: true
bin: true
@@ -79,25 +79,25 @@ marketplace:
- name: kyberforge
description: Skills and agents for creating, maintaining, and managing a Claude Code / Copilot CLI plugin marketplace.
source: ./plugins/kyberforge
version: 1.6.2
version: 1.6.1
category: Developer Tools
- name: bin
description: Skills for everyday AI-assisted development work that is not tied to a single tool, forge or language, and has not yet been split into a focused plugin.
source: ./plugins/bin
version: 1.1.7
version: 1.1.6
category: Utilities
- name: git
description: Skills and agents for working with a local Git clone over the git wire protocol, and for authoring and running the pre-commit hooks that guard it.
source: ./plugins/git
version: 1.3.7
version: 1.3.6
category: Version Control
- name: gitea
description: Skills and agents for working with a Gitea forge through its HTTP API — the forge's own objects, as distinct from the local git clone.
source: ./plugins/gitea
version: 1.3.8
version: 1.3.7
category: Version Control
- name: core

View File

@@ -1,78 +0,0 @@
# Every skill's `metadata.version` is mandatory, not a per-plugin option
**Status: accepted (2026-09-07).**
## Context
`metadata.version` is optional SKILL.md frontmatter (`create.md`'s "Optional frontmatter" list:
"uncomment and fill in, or remove entirely"). `skill-author`'s own bump logic was written
conditionally — "with `metadata.version` present, bump the minor version on create... and the
patch version on improve" — which only makes sense if presence is a real per-skill choice.
Adoption never followed a rule; it followed the plugin. Of 39 skills, 12 carry a version:
| Plugin | Has it | Total |
|---|---|---|
| `core` | 3 | 3 |
| `gitea` | 6 | 7 |
| `lint` | 2 | 2 |
| `git` | 1 | 9 |
| `bin` | 0 | 11 |
| `kyberforge` | 0 | 7 |
`core`, `gitea` and `lint` are consistent adopters (`gitea-files` the one gap); `bin` and
`kyberforge` are consistent non-adopters; `git` has one outlier (`git-commits`, versioned for no
plugin-specific reason found on inspection — no comment, no cross-reference, nothing distinguishing
it from its eight siblings). Issue #127 raised this as an undocumented split: two house norms
coexisting with no stated rule for which applies where, the same class of defect as an unstated
`rtk`/bare-`git` convention (#113) found in the same audit pass.
## Decision
**Every skill's frontmatter carries `metadata.version`.** It is no longer optional, and no longer a
per-plugin choice.
- **The 27 skills that never carried one are seeded at `1.0.0`**, not `0.1.0`. `0.1.0` is
`skill-author`'s existing new-skill starting point, chosen for a skill with no revision history to
its name yet. These 27 have all been through the ADR-0020 retrofit and repeated audit passes
without ever tracking a version; crediting them with `0.1.0` would understate that, and there is
no real history to justify seeding higher than a first stable release. `1.0.0` marks "versioned as
of this retrofit," `0.1.0` keeps meaning "created and never yet revised."
- **New skills still start at `0.1.0`.** `skill-author`'s create/improve bump convention is
unchanged; only the presence of the field stops being conditional.
- **The one outlier in the other direction, `git-commits`, keeps its existing value** (`0.1.3`) —
it already had real tracked history under the old conditional rule, and this decision does not
reset skills that were already compliant.
- **`bin/write-docs`'s top-level `version:` moves into `metadata:`, normalized to `1.0.0`.** It is
the one skill that carried a version outside the `metadata:` block, which is why the table above
counts `bin` as 0 — a top-level `version:` is not `metadata.version`, and nothing reads it. #127
raised it alongside the split because "does a skill carry a version" and "where does it live" are
the same question. Its value (`1.0`) is not semver and carries no more real history than the 27
unversioned skills, so it is relocated and reset to the same `1.0.0` seed rather than preserved
like `git-commits`'s tracked `0.1.3`.
- **`skill-frontmatter`'s pre-commit hook gains the check.** It already fails a SKILL.md missing
`name:` or `description:`; a missing `metadata.version` is now the same class of failure, not a
style nit an audit might or might not catch.
## Considered options
**Leave it per-plugin, document the split.** This was the initial framing of #127 and is coherent —
`core`/`gitea`/`lint` keep it, `bin`/`kyberforge` don't, two outliers get normalized to match their
plugin. Rejected on reconsideration: a rule that says "some plugins track this and some don't" is
strictly harder to state, audit and onboard against than "every skill does," for a field whose entire
job is answering "did this change since I last read it" — a question with the same shape everywhere
it's asked, not one that varies by plugin domain.
**Drop the field corpus-wide.** Rejected: `skill-author` already depends on it to decide whether a
create/improve pass owes a bump, so the 12 skills carrying it are not tracking dead weight — removing
it discards real revision signal for no gain.
## Consequences
27 SKILL.md files gain `metadata.version: "1.0.0"`, and a 28th — `bin/write-docs` — reaches the same
value by relocating its top-level `version: "1.0"` into `metadata:`. `skill-author`'s `create.md`
moves the field from "Optional frontmatter" to the required list, citing this ADR. `skill-author`'s
own SKILL.md drops the "with `metadata.version` present" conditional in its bump-rule line, since
presence is no longer in question. `.pre-commit-config.yaml`'s `skill-frontmatter` hook is extended
to require the field, closing the gap #113 and #118 both named in the same audit pass: a stated rule
with nothing enforcing it drifts the same way an unstated one does.

View File

@@ -1,168 +0,0 @@
# The `rtk` prefix marks executable commands only, and is repo-wide
**Status: accepted (2026-09-08).**
## Context
`CLAUDE.md` states the org convention as a golden rule: "Always prefix commands with `rtk`. If RTK
has a dedicated filter, it uses it. If not, it passes through unchanged. This means RTK is always
safe to use." Issue #113 observed that the rule had never been written down for skill *prose*, where
a `git <subcommand>` mention can be either an instruction to execute or a reference to the concept,
and that the corpus had drifted into carrying both spellings with no stated rule. PR #130 swept the
`git` plugin and recorded a two-way split in `plugins/git/README.md`.
Review found two defects in that sweep, and both are in the premise rather than the execution.
**RTK is not output-transparent.** `rtk git --help` enumerates twelve filtered subcommands — `diff`,
`log`, `status`, `show`, `add`, `commit`, `push`, `pull`, `branch`, `fetch`, `stash`, `worktree`.
Everything else is a true passthrough. Inside that set the filter is not a formatting preference; it
changes what the command *reports*. Measured against rtk 0.42.4:
| Command | What rtk does to it |
|---|---|
| `worktree list --porcelain -z` | discards both flags; no NUL separators, no `locked`/`lock_reason` field at all |
| `worktree list -v` | abbreviates `/root/…` to `~/…`, collapses column alignment |
| `branch --list <name>` | emits a phantom `* ` line even on no match |
| `diff --name-only` / `--name-status` | appends a blank line and a `Changes:` trailer |
| `diff --word-diff[=color\|=porcelain]` | emits no `[-removed-] {+added+}` markers; substitutes a diffstat |
| `log -L` | truncates each diff body line at ~72 characters with an ellipsis |
| `stash pop` (on conflict) | prints only `FAILED: git stash pop`, swallowing `CONFLICT`, `Unmerged paths` and the retained-entry notice |
| `stash list` (empty) | prints `No stashes` where git prints nothing |
Every one of those falsified a skill that was written against the bare output. `git-worktrees`'s
Step 2 required `locked` and `lock_reason` from a command whose rtk rendering has never carried
them; `git-log-format.md` documented `[-removed-] {+added+}` markers beside a command that no longer
produces them. The two-way split could not see any of this, because both halves of it are about what
a *sentence* is doing and none of it is about what the *command* does.
**The rule is not `git`-plugin-scoped.** `plugins/git/README.md` claimed the `gitea-*` skills
"contain no `git`/`rtk` mentions at all". Five `gitea-*` SKILL.md files run `git remote get-url
origin` in a fenced ```bash Step block — the README's own canonical example of "executable,
instructed" — plus `git branch --show-current` in a reference file and three `git remote -v` in
`gitea-orchestrate.agent.md`. A convention stated inside one plugin's README is invisible from the
plugin next door, which is how those eight sites stayed bare through the sweep that existed to find
them.
## Decision
**One rule, three clauses, repo-wide** — every `plugins/*/.apm/skills/**` and
`plugins/*/.apm/agents/**` file, not the `git` plugin alone.
1. **Executable and instructed → `rtk git`.** Anything telling the agent to run a command now: an
imperative step, a dispatch-table "Run" cell, a fenced code-block procedure.
`rtk git push -u origin <branch>`.
2. **Illustrative or referential → bare `git`.** Naming a flag's behaviour, quoting a doc's own
heading, describing a command in the abstract, warning against an anti-pattern. "`git switch`
refuses rather than clobbering conflicting local edits."
3. **Machine-parsed or interactive → bare `git`, and say why inline.** A command whose output the
skill parses, where rtk is in the filtered set above; or a command that hands control to an
interactive child process.
Clause 3 is the new one and it looks arbitrary without the table in Context, which is why the
measurements are recorded here rather than left in a PR thread. It is applied per subcommand and per
flag, not per skill: `tag --list` stays prefixed because rtk passes it through byte-identically,
while `branch --list` two words away goes bare because it does not. `git remote get-url origin`,
`git remote -v`, `git branch --show-current`, `git log --oneline -1` and `git add -u` were all
re-measured as byte-identical passthroughs and are therefore prefixed, parsing notwithstanding.
Two consequences of that per-subcommand basis are worth stating, because both are load-bearing and
neither is comfortable:
- **rtk's filtered set is a moving target.** `git rebase` and `git mergetool` are passthroughs on
0.42.4 — verified under `script(1)`, both inherit a real TTY, contradicting an earlier report that
they did not. They stay bare anyway, on the interactive limb: a token filter has nothing to offer a
command that hands control to an editor, and the prefix would only buy exposure to whatever a later
rtk version decides to do with those subcommands. The same reasoning makes the *inner* call in
`` `rtk git remote add origin-push $(git config remote.origin.url)` `` bare while the outer stays
prefixed — `config` passes through cleanly today, but its stdout becomes a remote URL that is then
force-pushed to, and that is not a blast radius to lend to a future filter change.
- **`branch --show-current` sits on the sharp edge.** It is in the filtered set, it is parsed, and it
is prefixed — on a measurement, in a subcommand whose sibling `--list` is exactly the defect clause
3 exists for. If rtk's `branch` filter is ever extended, that is the first site to break. It is
called out rather than hedged, because a rule whose exceptions are unrecorded is the state this ADR
is replacing.
**A clause-3 site says so inline, in a few words.** "bare, not `rtk`: rtk prints a phantom `* ` line
even on no match". Without it the next sweep re-prefixes the command, which is how #113 recurs.
**The rule lives here, and `docs/spec/gates.md` carries the gate.** `plugins/git/README.md` is
reduced to a pointer. It had also cited `git-workflow/references/hard-rules.md` as a place the rule
was written down; that file contains no occurrence of "rtk", and the citation is removed rather than
repaired.
**Clause 1 is enforced by a `check-rtk-prefix` pre-commit hook; clauses 2 and 3 are not enforceable
and are not gated.** The hook checks the two places a `git` mention is unambiguously an instruction —
a line in a shell-tagged code fence, and the opening backticked span of a "Run" column cell — and a
deliberately-bare command opts out with the literal string `ADR-0023` on its own line. Its coverage
limits are recorded in `docs/spec/gates.md`, not smoothed over.
## Considered options
**Add `compatibility:` frontmatter to every skill.** These six plugins are installable by third
parties, and a consumer who installs `git` from the marketplace has no `rtk` on their PATH. Every
prefixed command in the corpus is a plain `git` invocation with a word in front of it, so the prefix
is *droppable*: delete `rtk ` and the command is correct. A `compatibility:` line per skill would
state that in a machine-readable field. Rejected on cost. It is 39 lines of frontmatter restating one
sentence, it is preloaded into every agent's context every session under ADR-0020's budget — the
field is not free the way a line in a doc is — and it has no consumer: nothing reads
`compatibility:`, so the field would be a comment with a colon in it. The consumer situation is
documented here and in `plugins/git/README.md` instead, which is where a human installing a plugin
actually looks. The same two-line note is owed to the other five plugin READMEs and is not yet
written.
**Move rtk to the execution layer entirely.** Skills instruct bare `git` throughout; `CLAUDE.md`'s
session rule handles prefixing at the point of execution. This is the strongest rejected option and
it deserves the space: it closes the consumer gap and all eight output defects at once, because the
executing agent knows what it is about to parse and the skill does not have to predict it. It also
removes clause 3 entirely — there is nothing to except. Rejected because the prefix is lost wherever
an agent copies a command literally, which is the common case for a fenced procedure block and the
whole reason dispatch tables exist. The org convention's value is that the prefix is *already there*
in the text the agent lifts; a rule that relies on the agent remembering to add it is the rule that
produced the drift in the first place. Worth revisiting if rtk ever ships a shell shim, which would
make the execution layer transparent and this trade different.
**Keep the two-way split and fix the eight sites by hand.** Rejected: the split has no vocabulary for
"this command is executable, instructed, and must still be bare", so the eight sites would be
unexplained exceptions and the next sweep re-prefixes them. That is the failure this ADR exists to
stop, not a smaller version of it.
**Gate clauses 2 and 3 as well.** Rejected as undecidable. "Run `git switch <branch>`" and "`git
switch` refuses rather than clobbering local edits" are the same token sequence; separating them is a
judgement about what a sentence is doing. A gate that guessed would fire on correct content, and a
gate that fires on correct content gets added to `SKIP`, which disarms clause 1 along with it.
## The boundary the rule does not decide
Two shapes in the corpus resisted the two-way split. The three-clause rule resolves one and does not
resolve the other; both are recorded so an author meeting a third one knows which kind it is.
**`git-worktrees/SKILL.md`'s tracking row carries both spellings in one Run cell** — `rtk git
worktree add --track -b <branch> <path> <remote>/<branch>` — always correct. `git worktree add
<path> <branch>` expands to exactly this. **Resolved: the clauses apply per mention, not per row,
per cell or per file.** The first is the instruction (clause 1), the second names what the first
expands to (clause 2), and one table cell can hold one of each. The rule needed no change; the
*gate* did, and it checks only a Run cell's opening span for exactly this reason.
**`git-submodules/references/setup-and-update.md:80` has a git command inside a quoted argument to
another command** — `rtk git submodule foreach 'git pull origin main || :'`. **Not resolved: all
three clauses describe a command the reading agent executes, and the inner `git pull` is not one.**
It is the literal text of an argument that `git submodule foreach` hands to a subshell running inside
each submodule's own working tree, where the local convention does not reach. The file already gets
this right and already justifies it in prose two lines below ("the git calls in it are the
submodule's own — that is the one place a bare `git` is correct"). **An author meeting this shape
should do the same: leave the inner command bare and justify it inline.** It is deliberately not
promoted to a fourth clause on one instance. The gate does not decide it either — it happens to pass
this line, because the segment containing the inner command begins with `rtk`, and that is an
accident of the split rather than an understanding of quoting.
## Consequences
Eleven sites in `plugins/git/.apm/skills/**` revert to bare `git` under clause 3, each carrying a
short inline reason. Eight sites across `plugins/gitea/.apm/skills/**` and
`plugins/gitea/.apm/agents/gitea-orchestrate.agent.md` gain the prefix under clause 1, and one in
`pc-run/SKILL.md` that the #130 sweep's grep missed because the backtick opens with `SKIP=` rather
than `git `. `plugins/git/README.md`'s Conventions section becomes a pointer here, minus a paragraph
that was false about the `gitea-*` skills and a citation to a file that does not carry the rule.
A `check-rtk-prefix` pre-commit hook and `tests/test-check-rtk-prefix.sh` land with it; the test runs
the gate against the pre-sweep corpus on `main` and asserts it fails there, because a gate that only
passes on the fixed tree proves nothing about the drift it was written for.

View File

@@ -53,7 +53,8 @@ All skills — new and rebuilt — must follow this standard:
- `name:` — matches directory name
- `description:` — trigger-tested before writing the body (explicit, implicit, negative cases)
- `metadata: category:` — from the category table above
- `metadata: version:` — mandatory for every skill (ADR-0022)
`version:`, `updated:`, `when:`, `source:`, and `references:` are provenance/audit fields — they live in `META.md` alongside the SKILL.md (not in frontmatter). See `META-TEMPLATE.md` in `.agents/skills/write-skill/` for the META.md schema.
**Body required sections:**
- Constraints (highest-ROI element — prevents overengineering)

View File

@@ -103,21 +103,21 @@ Do not write the SKILL.md until the human has confirmed every section. The synth
**c. SKILL.md** (sub-agent)
Once all sections are confirmed, spawn a write agent to produce the SKILL.md using `write-skill` (or hand-write for bootstrap skills). The agent receives: trigger description, per-section decisions from step b, upstream content to incorporate, authoring standard (see below).
**d. Provenance — source and reference records**
Record provenance after upstream review. Two distinct kinds:
- Upstream provenance (repo slug, commit SHA, files adopted with inline comments, updated date). Present only if content was adopted. Absence = self-authored.
- General citations (research papers, documentation, standard specifications). Present only if the skill cites external research.
**c. META.md — `source:` and `references:` fields**
Populate `META.md` after upstream review. Two distinct fields:
- `source:` — upstream provenance tracking (repo slug, commit SHA, files adopted with inline comments, updated date). Present only if content was adopted. Absence = self-authored.
- `references:` — general citations (research papers, documentation, standard specifications). Present only if the skill cites external research.
Both are recorded in the skill's own `references/sources.md`, keyed by the `source_keys:` its SKILL.md and reference files declare. `validate-provenance.sh` checks that chain.
Both fields live in `META.md` alongside the SKILL.md — not in frontmatter. See `META-TEMPLATE.md` in `.agents/skills/write-skill/` for the full schema.
**e. eval.yaml** (sub-agent)
**d. eval.yaml** (sub-agent)
Invoke `write-eval` in two steps to preserve its confirmation gate:
1. Sub-agent proposes test cases and returns the plan to the main conversation.
2. Human confirms the plan; then sub-agent writes the file.
Do not pass pre-designed test cases directly to a write agent — that collapses the plan-then-confirm gate into a single step, bypassing write-eval's own constraint. Co-located at `.agents/evals/<category>/<skill-name>/eval.yaml`. Must contain all five required test types (see Eval schema below).
**f. HITL behavioral test**
**e. HITL behavioral test**
Human opens a fresh Claude session, invokes the skill with its trigger phrase, and verifies output. Do not batch more than 2–3 skills before running behavioral tests — output volume must stay within genuine human review capacity. An approval that cannot be meaningfully evaluated is not an approval.
### Step 6 — Session handoff
@@ -157,11 +157,12 @@ name: skill-name
description: <trigger description — routing only; written and tested first; max 1024 chars>
metadata:
category: <design|factory|implement|test|review|deploy|operate|cross-cutting|iac>
version: <semver — mandatory for every skill; see ADR-0022>
# allowed-tools: <add only when the skill has a narrow, well-defined tool surface; omit otherwise>
---
```
Frontmatter contains only these fields. `version`, `updated`, `when`, `source`, and `references` are provenance/audit fields — they are not used for routing or runtime execution. They live in `META.md` alongside the SKILL.md, loaded only when needed. See `META-TEMPLATE.md` in `.agents/skills/write-skill/` for the META.md schema.
### Body sections
Use `.agents/skills/write-skill/SKILL-TEMPLATE.md` as the authoritative structure reference. The template defines the required sections, correct order, XML grouping, and placeholder comments for each section.
@@ -229,6 +230,6 @@ Upstream review happens per-skill during step 2, not once at chunk start.
## Open decisions carried forward
- **Bidirectional reference convention** — Chunk 4 (reference scanner tooling; reverse map "what files point to X?").
- **Bidirectional reference convention** — Chunk 4 (reference scanner tooling; reverse map "what files point to X?"). The `when:` field itself is resolved — it lives in `META.md` alongside every skill.
- **PRD/issue template scope** — refined during `write-prd` (0020) and `write-issue-spec` (0019) implementation
- **Merging `zoom-out` into architect role** — revisit at Chunk 5 grill

View File

@@ -49,13 +49,6 @@ Two compilers produce the plugin roots you see in the tree:
`.apm/` is the sole hand-edited authoring source for plugin content. An edit made in the flat mirror is discarded by the next sync and is reported as drift by the `check-plugin-content-sync` pre-push hook. Hand-authored material that is not an `.apm/` primitive — `README.md`, `docs/`, `bin/`, `sources.md`, `.mcp.json`, and per-plugin extras such as `plugins/git/config.example.json`, `plugins/gitea/references/` and `plugins/bin/evals/` — lives at the plugin **root** and is untouched by either compiler.
`.mcp.json` is the one entry in that list that is still load-bearing for apm rather than merely ignored by it. MCP is a first-class apm primitive — `dependencies.mcp` sits beside `dependencies.apm` in the manifest schema, and apm tracks deployed servers in `apm.lock.yaml` under `mcp_servers`, `mcp_configs` and `mcp_config_provenance`. A plugin reaches that primitive indirectly. `apm pack` writes the string `".mcp.json"` into the generated `.github/plugin/plugin.json` as its `mcpServers` value, and on install apm resolves the plugin manifest in the order `plugin.json`, `.github/plugin/plugin.json`, `.claude-plugin/plugin.json` — so the Copilot manifest wins, the pointer is followed, and `.mcp.json` is injected into the package's `dependencies.mcp` with any `${VAR}` env references intact. Verified against the real remote: a git-sourced install of `plugins/gitea` deploys the gitea server with both env references unexpanded.
Two consequences follow, and both have bitten already:
- **Do not declare `dependencies.mcp` in a plugin's own `apm.yml`.** It is the schema-correct place and it breaks the build. The `apm-audit-ci` pre-push hook runs `apm audit --ci` inside every `plugins/*/`, so a declared dependency arms `lockfile-exists` there, which then demands an `apm.lock.yaml` in the package plus every file of that package's own deployed tree present inside the package directory. Measured on `plugins/gitea`: 93 missing deployed files and 79 drifted paths.
- **`.claude-plugin/plugin.json` carries an env-stripped copy.** `apm pack` inlines `.mcp.json` there, and its sanitiser drops `env` and `headers` blocks unconditionally at any depth, `${VAR}` indirection included. That copy is inert under apm, which never reaches it, but a native Claude Code plugin install reads exactly that file and would launch the server with no credentials. Anything installed natively rather than through apm needs its MCP env supplied by the host.
That immunity is positional, not by filename. Anything placed *inside* a mirrored directory is destroyed regardless of what it is: `sync_dir` runs `rm -rf "$dst"` before every copy, and `sync_hooks_json` does the same to `hooks/`. A hand-written `README.md` under `plugins/<name>/hooks/` or `plugins/<name>/skills/` is deleted by the next sync with no drift report, because a file with no `.apm/` counterpart is simply absent from the regenerated tree. This has already cost the repo one document — `plugins/kyberforge/hooks/README.md`, since restored to `plugins/kyberforge/docs/hooks.md`. Plugin-root documentation belongs in `docs/`.
## Governance layer

View File

@@ -128,43 +128,12 @@ not an authoring change.
### `skill-frontmatter`, the other hook on that scope
A second `repo: local` pre-commit hook, `skill-frontmatter`, runs on the **same** `files:` pattern at
the same stage. It is a shell loop that, **for the YAML frontmatter block only** — everything between
the opening `---` and the next `---` — asserts four things per file:
the same stage. It is a short shell loop: for each file, `grep -q "^name:"` and
`grep -q "^description:"`, failing with "missing required frontmatter fields" if either is absent.
| Check | Rejects with |
|---|---|
| a `^name:` line is present | "missing required frontmatter fields (name: …)" |
| a `^description:` line is present | "missing required frontmatter fields (description: …)" |
| `metadata:` contains a `^ version:` key, anchored, scanning to the next top-level key | "missing required frontmatter fields (metadata.version)" |
| that version's value is three-part semver (`1.0.0`, quoted or not) | "has a malformed frontmatter metadata.version (…)" |
Every one of those qualifiers is load-bearing, and each replaced a defect that let the hook report
Passed having measured nothing. `tests/test-skill-frontmatter.sh` pins all of them:
- **Frontmatter-scoped, not whole-file.** The checks used to `grep` the entire file, so a `metadata:`
or `name:` block quoted in a **body code fence** satisfied them — `skill-author`'s own docs quote
exactly such a block.
- **Bounded by the next top-level key, not by `-A10`.** The version check was
`grep -A10 "^metadata:" | grep -q " version:"`, which ran ten lines past the end of the block: a
`version:` belonging to a following `source:` list entry counted (`write-docs` and `research` both
have a `source:` list immediately after `metadata:`), while a `metadata:` block with more than ten
lines before its `version:` was reported missing.
- **`^ version:` anchored.** `" version:"` was an unanchored substring, so a deeper-nested
` version:` matched too.
- **The value is asserted, not just the key.** `plugins/bin/.apm/skills/write-docs/SKILL.md` carried
`version: "1.0"` — present, correctly nested, and not a version — through an entire PR under a
presence-only check. Two-part `1.0` is a YAML float, not a version string.
- **The call shape is pinned.** `entry: bash` with `args: ['-c', <script>, …]` needs an explicit
arg0 placeholder after the script: without it `bash -c` puts pre-commit's **first** filename in
`$0`, where `for f in "$@"` never sees it. A single-file commit — the normal case — therefore ran
the loop body zero times and exited 0. The third `args` entry (`skill-frontmatter`) exists solely
to absorb `$0`; do not remove it.
- **An unreadable file is an error, not a pass.** A file with no closing `---` fails with "no closing
YAML frontmatter block" rather than falling through to a green.
**It still overlaps ADR-0020's "description present and non-empty" FAIL, and the overlap is not
clean.** The ADR (`:95-101`) requires that question be decided on the **YAML-folded value** and
nowhere else, precisely because a line regex gets it wrong in both directions. Measured on fixtures:
**It overlaps ADR-0020's "description present and non-empty" FAIL, and the overlap is not clean.**
The ADR (`:95-101`) requires that question be decided on the **YAML-folded value** and nowhere else,
precisely because a line regex gets it wrong in both directions. Measured on fixtures:
| Frontmatter | `skill-frontmatter` | `skill-size-check` |
|---|---|---|
@@ -176,34 +145,10 @@ against, and it is the only one of the two that objects to a quoted key. Neither
currently live in the corpus, and the honest reading is that presence is `skill-size-check`'s
question — the grep's contribution to it is noise on one shape and silence on the other.
What the hook adds that **no** ADR-0020 check reads is two keys: `name:` and `metadata.version`. A
`SKILL.md` missing either passes `skill-size-check` at exit 0. That is its unique coverage, and the
What the grep does add is the `name:` key, which **no** ADR-0020 check reads: a `SKILL.md` with no
`name:` passes `skill-size-check` at exit 0. That is its real and only unique coverage, and the
reason not to fold it into the size gate on the grounds of redundancy.
#### Why this one stays a shell parser
[`python3` and PyYAML are hard requirements](#python3-and-pyyaml-are-hard-requirements) below records
that a hand-rolled frontmatter reader on this exact `files:` scope was **deliberately deleted**,
because "a reader that mis-parses an unfamiliar scalar shape reports a clean pass on a file it never
measured." That reasoning is about `skill-size-check` and does **not** transfer here. Do not delete
this hook citing it. Three differences:
1. **It answers a strictly narrower question.** `skill-size-check` must know the *folded value* of a
`>`-block scalar to count its characters, which is where a line reader diverges from a parser —
one corpus description measured 270 characters parsed and 412 unparsed. This hook asks only
whether a key is on a line and whether one short **plain scalar** matches `N.N.N`. There is no
folding, no multi-line value, and no measurement to get subtly wrong.
2. **It is frontmatter-scoped.** The failure mode that killed the old fallback was silently reading
past or short of the block. This one extracts the block explicitly and errors out when it cannot
find a closing marker, so "could not parse" is a red, never a green.
3. **It is pinned by tests.** `tests/test-skill-frontmatter.sh` drives the hook through pre-commit's
real `bash -c <script> <arg0> <files…>` invocation and asserts each defect class above. The
deleted fallback had no such suite; that is how its disagreement with a real parser survived.
The trade it buys is that the hook stays repo-local. Moving it to a script would change the
externally exposed `.pre-commit-hooks.yaml` contract for consumers, for a check that has no need of a
YAML parser.
### Two independent gate families, neither replaced the other
**Family 1 — agentskills.io spec backstop** (unchanged, conformance not quality):
@@ -468,15 +413,6 @@ reader that mis-parses an unfamiliar scalar shape reports a clean pass on a file
which is the exact vacuous-green failure the `python3` check exists to avoid. `pip install pyyaml`
(or `python3 -m pip install PyYAML`, or the distro's `python3-yaml`) if the hook reports it missing.
**Neither requirement generalises to every hook on this scope, and one deliberate exception sits
right next to it.** [`skill-frontmatter`](#skill-frontmatter-the-other-hook-on-that-scope) runs on the
same `files:` pattern as a **shell** parser, on purpose — it asks only whether a key is on a line and
whether one short plain scalar matches `N.N.N`, with no folding to get wrong, and moving it to a
script would change the externally exposed `.pre-commit-hooks.yaml` contract for consumers. That
section carries the full argument. A reader arriving here first should not read this one as
condemning it. `check-rtk-prefix` needs `python3` but **not** PyYAML: it reads the markdown body and
never touches frontmatter, so it has no scalar to fold.
## Agent files take the description gates, not the body gate
`check-apm-agents-valid` runs agent-audit's `validate.sh` over every real
@@ -561,91 +497,6 @@ pre-commit run --all-files # size AND Vale
Scoping a retrofit off `skill-size-check` output alone leaves you blocked at the second gate.
## The `rtk` prefix gate (ADR-0023)
`check-rtk-prefix` is a `repo: local` pre-commit hook running `scripts/check-rtk-prefix.sh` over
`^plugins/[^/]+/\.apm/(skills/.*\.md|agents/.*\.agent\.md)$`, with `README.md` excluded. It enforces
**ADR-0023 clause 1 and nothing else**: an executable, instructed local git command in plugin skill
or agent content is written `rtk git`.
It is wider in file scope than the ADR-0020 hooks — every markdown file under a plugin's
`.apm/skills/` and `.apm/agents/`, not `SKILL.md` alone — because the rule it enforces is about
commands an agent runs, and most of those live in `references/`, which the ADR-0020 gates do not
reach ([the `references/` blind spot](#the-blind-spot-references-is-unlinted-for-two-independent-reasons)).
### What it can decide, and what it declines to
ADR-0023 has three clauses and only the first is a pattern:
| Clause | Rule | Gated |
|---|---|---|
| 1 | executable + instructed → `rtk git` | yes |
| 2 | illustrative / referential → bare `git` | no — undecidable |
| 3 | machine-parsed or interactive → bare `git` | no — opt-out marker |
Clause 2 is a judgement about what a sentence is *doing*. "Run `git switch <branch>`" and "`git
switch` refuses rather than clobbering local edits" are the same token sequence. A gate that guessed
would fire on correct prose, and **a gate that fires on correct content gets added to `SKIP`** —
which disarms clause 1 along with it. So the hook looks only at the two contexts where a `git`
mention is unambiguously an instruction to execute:
- a line inside a fenced code block whose info string names a shell — `bash`, `sh`, `shell`, `zsh`,
`console`, `shell-session`. Fences tagged `text`, `yaml`, `json`, or tagged with nothing, are **not**
checked;
- the **opening** backticked span of a "Run" column cell in a markdown dispatch table, and only the
opening span.
That last narrowing is not fussiness. A Run cell routinely carries a command followed by prose about
it, and the prose is clause 2. `git-worktrees/SKILL.md` has both shapes on adjacent rows — one cell
reading `` `rtk git worktree add --track …` `` — always correct. `` `git worktree add <path>
<branch>` `` expands to exactly this (instruction, then reference), and a `**Never** …` row whose Run
cell is entirely explanation containing a bare `git push`. Checking every backticked span flags both;
checking only a leading span flags neither, and still catches the ordinary
`` | List | `git worktree list -v` | `` case the gate exists for.
### The clause-3 opt-out
A command that is deliberately bare — because rtk rewrites the output the skill parses, or because
the command is interactive — is exempted by putting the literal string `ADR-0023` **on the same
line**: in a shell comment for a code line, in the cell text for a table row.
Per line, never per block. A fenced procedure routinely mixes `rtk git` steps with one deliberately
bare command (`git-remotes/references/push.md` does exactly that), and a block-level marker would
silently disarm every checked line around the marked one. The cost is a repeated `# bare per
ADR-0023` in the three blocks of `git-log-format.md` where every line is deliberately bare; that
repetition is the price of the marked line being the only line the marker speaks for.
The marker is a plain substring match, so a line that mentions `ADR-0023` for an unrelated reason is
also exempt. Accepted deliberately: the marker records an author's opt-out, it is not a security
boundary, and a stricter form would only move the same trust to a different string.
### What it deliberately does not cover
- **Clause 2.** Nothing checks that an illustrative mention stayed bare. A sweep that re-prefixes a
referential `git` passes this gate. The inline reasons ADR-0023 requires on clause-3 sites are the
only defence, and they are prose.
- **Prose bullets.** Most of `branch-operations.md`, `merging.md` and `rewrite-history.md` instruct
in list items, not fences. Those are clause-1 sites the gate cannot see, because it cannot
distinguish them from clause-2 mentions in the same list.
- **`README.md`, excluded by pattern.** A skill-directory README is consumer-facing prose no agent
loads, and the `git clone https://github.com/bats-core/…` lines in the seven `tests/README.md`
files are setup instructions for a third party who has no `rtk`. Prefixing those would be actively
wrong, not merely noisy — see ADR-0023's consumer section.
- **Quoting.** The line splitter breaks on `;`, `|`, `&&`, `||`, `$(` and backticks without tracking
quotes, so a git command inside a quoted argument is decided by accident.
`rtk git submodule foreach 'git pull origin main || :'` passes because the segment holding the
inner command begins with `rtk` — the right answer for the wrong reason. Write
`foreach 'git a; git b'` and the second inner command is a false positive needing the marker.
ADR-0023 records this shape as one the rule itself does not decide.
- **Non-git commands.** Only `git` is checked. `rtk` fronts `gh`, `docker`, `kubectl` and others; no
gate covers those, and the corpus does not currently instruct them.
`tests/test-check-rtk-prefix.sh` pins all of it, including the false-positive cases. Its first case
reconstructs the plugin corpus as it stood on `main` before the #113 sweep and asserts the gate
fails there with at least 20 findings, one of them the `gitea-*` `git remote get-url origin` drift
the sweep missed — a gate that only passes on the already-fixed tree proves nothing about the drift
it was written for.
## Vale
Install the `vale` binary — `brew install vale` (macOS), `snap install vale` (Linux),

View File

@@ -4,8 +4,6 @@ disable-model-invocation: true
description: >
Ultra-compressed output mode that drops articles, filler and pleasantries while
keeping technical substance exact, cutting token usage by roughly 75%.
metadata:
version: "1.0.0"
---
Respond terse like smart caveman. All technical substance stay. Only fluff die.

View File

@@ -4,8 +4,6 @@ description: >
Use when the user says "diagnose this" or "debug this", reports something
broken, throwing, or failing, or says something got slow. Not filing or
triaging a reported bug -> `triage`. Not test-first feature work -> `tdd`.
metadata:
version: "1.0.0"
---
# Diagnose

View File

@@ -5,8 +5,6 @@ description: >
relentless interview — one question at a time, down each branch of the
decision tree. Not a plan to challenge against `CONTEXT.md` and ADRs ->
`grill-with-docs`.
metadata:
version: "1.0.0"
---
Interview me relentlessly about every aspect of this plan until we reach a shared understanding. Walk down each branch of the design tree, resolving dependencies between decisions one-by-one. For each question, provide your recommended answer.

View File

@@ -33,5 +33,5 @@ Describe the plan or design. Expect questions one at a time, each with a recomme
| File | Purpose |
|------|---------|
| `SKILL.md` | The interview instruction plus the domain-awareness rules: file layout discovery, the five during-session behaviours, and the three-part ADR test |
| `references/context-format.md` | Cited when a term is resolved: the structure of a `CONTEXT.md` and how to write a Language entry |
| `references/adr-format.md` | Cited when an ADR is offered: `docs/adr/` naming, sequential numbering, and the ADR template |
| `CONTEXT-FORMAT.md` | Skill-root document, cited when a term is resolved: the structure of a `CONTEXT.md` and how to write a Language entry |
| `ADR-FORMAT.md` | Skill-root document, cited when an ADR is offered: `docs/adr/` naming, sequential numbering, and the ADR template |

View File

@@ -4,8 +4,6 @@ description: >
Use when a plan should be stress-tested against the project's domain model —
the interview challenges terms against `CONTEXT.md` and writes decisions into
it and into ADRs as they land. Not a plain interview -> `grill-me`.
metadata:
version: "1.0.0"
---
<what-to-do>
@@ -76,7 +74,7 @@ When the user states how something works, check whether the code agrees. If you
### Update CONTEXT.md inline
When a term is resolved, update `CONTEXT.md` right there. Don't batch these up — capture them as they happen. Use the format in [context-format.md](references/context-format.md).
When a term is resolved, update `CONTEXT.md` right there. Don't batch these up — capture them as they happen. Use the format in [CONTEXT-FORMAT.md](./CONTEXT-FORMAT.md).
Don't couple `CONTEXT.md` to implementation details. Only include terms that are meaningful to domain experts.
@@ -88,6 +86,6 @@ Only offer to create an ADR when all three are true:
2. **Surprising without context** — a future reader will wonder "why did they do it this way?"
3. **The result of a real trade-off** — there were genuine alternatives and you picked one for specific reasons
If any of the three is missing, skip the ADR. Use the format in [adr-format.md](references/adr-format.md).
If any of the three is missing, skip the ADR. Use the format in [ADR-FORMAT.md](./ADR-FORMAT.md).
</supporting-info>

View File

@@ -1,6 +1,6 @@
# Deepening
How to deepen a cluster of shallow modules safely, given its dependencies. Assumes the vocabulary in [language.md](language.md) — **module**, **interface**, **seam**, **adapter**.
How to deepen a cluster of shallow modules safely, given its dependencies. Assumes the vocabulary in [LANGUAGE.md](LANGUAGE.md) — **module**, **interface**, **seam**, **adapter**.
## Dependency categories

View File

@@ -2,7 +2,7 @@
When the user wants to explore alternative interfaces for a chosen deepening candidate, use this parallel sub-agent pattern. Based on "Design It Twice" (Ousterhout) — your first idea is unlikely to be the best.
Uses the vocabulary in [language.md](language.md) — **module**, **interface**, **seam**, **adapter**, **leverage**.
Uses the vocabulary in [LANGUAGE.md](LANGUAGE.md) — **module**, **interface**, **seam**, **adapter**, **leverage**.
## Process
@@ -11,7 +11,7 @@ Uses the vocabulary in [language.md](language.md) — **module**, **interface**,
Before spawning sub-agents, write a user-facing explanation of the problem space for the chosen candidate:
- The constraints any new interface would need to satisfy
- The dependencies it would rely on, and which category they fall into (see [deepening.md](deepening.md))
- The dependencies it would rely on, and which category they fall into (see [DEEPENING.md](DEEPENING.md))
- A rough illustrative code sketch to ground the constraints — not a proposal, just a way to make the constraints concrete
Show this to the user, then immediately proceed to Step 2. The user reads and thinks while the sub-agents work in parallel.
@@ -20,21 +20,21 @@ Show this to the user, then immediately proceed to Step 2. The user reads and th
Spawn 3+ sub-agents in parallel using the Agent tool. Each must produce a **radically different** interface for the deepened module.
Prompt each sub-agent with a separate technical brief (file paths, coupling details, dependency category from [deepening.md](deepening.md), what sits behind the seam). The brief is independent of the user-facing problem-space explanation in Step 1. Give each agent a different design constraint:
Prompt each sub-agent with a separate technical brief (file paths, coupling details, dependency category from [DEEPENING.md](DEEPENING.md), what sits behind the seam). The brief is independent of the user-facing problem-space explanation in Step 1. Give each agent a different design constraint:
- Agent 1: "Minimize the interface — aim for 1–3 entry points max. Maximise leverage per entry point."
- Agent 2: "Maximise flexibility — support many use cases and extension."
- Agent 3: "Optimise for the most common caller — make the default case trivial."
- Agent 4 (if applicable): "Design around ports & adapters for cross-seam dependencies."
Include both [language.md](language.md) vocabulary and CONTEXT.md vocabulary in the brief so each sub-agent names things consistently with the architecture language and the project's domain language.
Include both [LANGUAGE.md](LANGUAGE.md) vocabulary and CONTEXT.md vocabulary in the brief so each sub-agent names things consistently with the architecture language and the project's domain language.
Each sub-agent outputs:
1. Interface (types, methods, params — plus invariants, ordering, error modes)
2. Usage example showing how callers use it
3. What the implementation hides behind the seam
4. Dependency strategy and adapters (see [deepening.md](deepening.md))
4. Dependency strategy and adapters (see [DEEPENING.md](DEEPENING.md))
5. Trade-offs — where leverage is high, where it's thin
### 3. Present and compare

View File

@@ -10,7 +10,7 @@ Looks for places where a codebase is hard to understand, hard to test, or hard f
2. **Present candidates.** A numbered list, each with files, problem, solution and benefits — benefits stated in terms of *locality* and *leverage* and of how tests would improve. No interfaces are proposed yet; the user picks one.
3. **Grilling loop.** Walks the design tree for the chosen candidate, with documentation side effects landing inline as decisions crystallise.
The skill is opinionated about vocabulary, and that is the point: **module, interface, implementation, depth, seam, adapter, leverage, locality**, used exactly, with no drift into "component", "service", "API" or "boundary". Domain nouns come from `CONTEXT.md`, architecture nouns from `references/language.md` — so a proposal reads as "the Order intake module", never "the FooBarHandler".
The skill is opinionated about vocabulary, and that is the point: **module, interface, implementation, depth, seam, adapter, leverage, locality**, used exactly, with no drift into "component", "service", "API" or "boundary". Domain nouns come from `CONTEXT.md`, architecture nouns from `LANGUAGE.md` — so a proposal reads as "the Order intake module", never "the FooBarHandler".
ADRs are treated as decisions not to be re-litigated. A candidate that contradicts one is surfaced only when the friction is real enough to warrant reopening it, and is marked as such.
@@ -31,6 +31,6 @@ Point at a codebase or an area of one. Expect a numbered candidate list and a "w
| File | Purpose |
|------|---------|
| `SKILL.md` | Condensed glossary, key principles, and the three-stage process |
| `references/language.md` | Cited throughout `SKILL.md`: full definitions of every term, the words each one replaces, and the full principle list |
| `references/interface-design.md` | Read at stage 3 when the user wants alternative interfaces explored: the parallel sub-agent "Design It Twice" pattern, framing the problem space, and the per-agent design constraints |
| `references/deepening.md` | Cited from `references/interface-design.md`: how to deepen a cluster of shallow modules safely, the four dependency categories (in-process, local-substitutable, remote-but-owned, true external), seam discipline, and the replace-don't-layer testing strategy |
| `LANGUAGE.md` | Skill-root document, cited throughout `SKILL.md`: full definitions of every term, the words each one replaces, and the full principle list |
| `INTERFACE-DESIGN.md` | Skill-root document, read at stage 3 when the user wants alternative interfaces explored: the parallel sub-agent "Design It Twice" pattern, framing the problem space, and the per-agent design constraints |
| `DEEPENING.md` | Skill-root document, cited from `INTERFACE-DESIGN.md`: how to deepen a cluster of shallow modules safely, the four dependency categories (in-process, local-substitutable, remote-but-owned, true external), seam discipline, and the replace-don't-layer testing strategy |

View File

@@ -6,8 +6,6 @@ description: >
testable and AI-navigable — deepening opportunities that turn shallow modules
into deep ones, informed by `CONTEXT.md` and `docs/adr/`. Not debugging a
failure -> `diagnose`.
metadata:
version: "1.0.0"
---
# Improve Codebase Architecture
@@ -33,7 +31,7 @@ Key principles:
- **The interface is the test surface.**
- **One adapter = hypothetical seam. Two adapters = real seam.**
If a term or principle above is ambiguous in the case in front of you, or you need the definitions and the principles the two lists leave out, read `references/language.md`.
If a term or principle above is ambiguous in the case in front of you, or you need the definitions and the principles the two lists leave out, read `LANGUAGE.md`.
This skill is _informed_ by the project's domain model. The domain language gives names to good seams; ADRs record decisions the skill should not re-litigate.
@@ -74,7 +72,7 @@ Once the user picks a candidate, drop into a grilling conversation. Walk the des
Side effects happen inline as decisions crystallize:
- **Naming a deepened module after a concept not in `CONTEXT.md`?** Add the term to `CONTEXT.md` — same discipline as `grill-with-docs`, in the format `grill-with-docs`'s `references/context-format.md` defines. Create the file lazily if it doesn't exist.
- **Naming a deepened module after a concept not in `CONTEXT.md`?** Add the term to `CONTEXT.md` — same discipline as `grill-with-docs`, in the format `grill-with-docs`'s `CONTEXT-FORMAT.md` defines. Create the file lazily if it doesn't exist.
- **Sharpening a fuzzy term during the conversation?** Update `CONTEXT.md` right there.
- **User rejects the candidate with a load-bearing reason?** Offer an ADR, framed as: _"Want me to record this as an ADR so future architecture reviews don't re-suggest it?"_ Only offer when the reason would actually be needed by a future explorer to avoid re-suggesting the same thing — skip ephemeral reasons ("not worth it right now") and self-evident ones. See `grill-with-docs`'s `references/adr-format.md`.
- **Want to explore alternative interfaces for the deepened module?** Read `references/interface-design.md`.
- **User rejects the candidate with a load-bearing reason?** Offer an ADR, framed as: _"Want me to record this as an ADR so future architecture reviews don't re-suggest it?"_ Only offer when the reason would actually be needed by a future explorer to avoid re-suggesting the same thing — skip ephemeral reasons ("not worth it right now") and self-evident ones. See `grill-with-docs`'s `ADR-FORMAT.md`.
- **Want to explore alternative interfaces for the deepened module?** Read `INTERFACE-DESIGN.md`.

View File

@@ -5,8 +5,6 @@ description: >
a data model, state machine or business logic, or to mock up a UI in several
variations. Not production code -> `tdd`. Not talking a design through ->
`grill-me`.
metadata:
version: "1.0.0"
---
# Prototype

View File

@@ -6,7 +6,6 @@ description: >-
documentation written from existing code or specs -> `write-docs`. Not a bug
or incident -> `diagnose`.
metadata:
version: "1.0.0"
category: research
allowed-tools:
- Grep

View File

@@ -25,8 +25,8 @@ Describe the feature or bug. Expect the skill to ask what the public interface s
| File | Purpose |
|------|---------|
| `SKILL.md` | Philosophy, the horizontal-slicing anti-pattern, the four-stage workflow, and the per-cycle checklist |
| `references/tests.md` | Cited from Philosophy: worked good and bad test examples |
| `references/mocking.md` | Cited from Philosophy: mock at system boundaries only, and what not to mock |
| `references/deep-modules.md` | Cited from stage 1: what a deep module is (small interface, large implementation) and why it is the design to aim for |
| `references/interface-design.md` | Cited from stage 1: designing interfaces for testability, starting with accepting dependencies rather than creating them |
| `references/refactoring.md` | Cited from stage 4: the refactor-candidate checklist — duplication, long methods, shallow modules, feature envy, primitive obsession |
| `tests.md` | Skill-root document, cited from Philosophy: worked good and bad test examples |
| `mocking.md` | Skill-root document, cited from Philosophy: mock at system boundaries only, and what not to mock |
| `deep-modules.md` | Skill-root document, cited from stage 1: what a deep module is (small interface, large implementation) and why it is the design to aim for |
| `interface-design.md` | Skill-root document, cited from stage 1: designing interfaces for testability, starting with accepting dependencies rather than creating them |
| `refactoring.md` | Skill-root document, cited from stage 4: the refactor-candidate checklist — duplication, long methods, shallow modules, feature envy, primitive obsession |

View File

@@ -4,8 +4,6 @@ description: >
Use when the user wants a feature built or a bug fixed test-first, in a strict
red-green-refactor loop, one behaviour at a time. Not diagnosing an existing
bug -> `diagnose`. Not throwaway exploratory code -> `prototype`.
metadata:
version: "1.0.0"
---
# Test-Driven Development
@@ -18,7 +16,7 @@ metadata:
**Bad tests** are coupled to implementation. They mock internal collaborators, test private methods, or verify through external means (like querying a database directly instead of using the interface). The warning sign: your test breaks when you refactor, but behavior hasn't changed. If you rename an internal function and tests fail, those tests were testing implementation, not behavior.
If you need worked examples of the difference — a behaviour-level test beside the implementation-coupled version of the same check — read `references/tests.md`. If a test needs a collaborator faked, read `references/mocking.md` before reaching for a mock.
If you need worked examples of the difference — a behaviour-level test beside the implementation-coupled version of the same check — read `tests.md`. If a test needs a collaborator faked, read `mocking.md` before reaching for a mock.
## Anti-Pattern: Horizontal Slices
@@ -55,8 +53,8 @@ Before writing any code:
- [ ] Confirm with user what interface changes are needed
- [ ] Confirm with user which behaviors to test (prioritize)
- [ ] Identify opportunities for [deep modules](references/deep-modules.md) (small interface, deep implementation)
- [ ] Design interfaces for [testability](references/interface-design.md)
- [ ] Identify opportunities for [deep modules](deep-modules.md) (small interface, deep implementation)
- [ ] Design interfaces for [testability](interface-design.md)
- [ ] List the behaviors to test (not implementation steps)
- [ ] Get user approval on the plan
@@ -93,7 +91,7 @@ Rules:
### 4. Refactor
After all tests pass, look for [refactor candidates](references/refactoring.md):
After all tests pass, look for [refactor candidates](refactoring.md):
- [ ] Extract duplication
- [ ] Deepen modules (move complexity behind simple interfaces)

View File

@@ -31,5 +31,5 @@ Then describe what you want in natural language — "show me anything that needs
| File | Purpose |
|------|---------|
| `SKILL.md` | The roles and state machine, the three invocation modes, the needs-info template, and how to resume a prior session |
| `references/agent-brief.md` | Cited when an issue moves to `ready-for-agent` (and reused for `ready-for-human`): how to write a brief that stays durable for weeks while the codebase moves under it — describe interfaces and behavioural contracts, not line numbers |
| `references/out-of-scope.md` | Cited when an enhancement is closed `wontfix` and when checking for prior rejections: how the `.out-of-scope/` knowledge base is laid out and what it is for — institutional memory, and deduplication against re-litigated requests |
| `AGENT-BRIEF.md` | Skill-root document, cited when an issue moves to `ready-for-agent` (and reused for `ready-for-human`): how to write a brief that stays durable for weeks while the codebase moves under it — describe interfaces and behavioural contracts, not line numbers |
| `OUT-OF-SCOPE.md` | Skill-root document, cited when an enhancement is closed `wontfix` and when checking for prior rejections: how the `.out-of-scope/` knowledge base is laid out and what it is for — institutional memory, and deduplication against re-litigated requests |

View File

@@ -4,8 +4,6 @@ description: >
Use when the user wants an issue created, triaged, or moved through the
tracker's triage states, or an issue prepared for an AFK agent. Not debugging
the bug itself -> `diagnose`. Not fleshing out a design -> `grill-with-docs`.
metadata:
version: "1.0.0"
---
# Triage
@@ -20,8 +18,8 @@ Every comment or issue posted to the issue tracker during triage **must** start
## Reference docs
- [agent-brief.md](references/agent-brief.md) — how to write durable agent briefs
- [out-of-scope.md](references/out-of-scope.md) — how the `.out-of-scope/` knowledge base works
- [AGENT-BRIEF.md](AGENT-BRIEF.md) — how to write durable agent briefs
- [OUT-OF-SCOPE.md](OUT-OF-SCOPE.md) — how the `.out-of-scope/` knowledge base works
## Roles
@@ -74,11 +72,11 @@ Show counts and a one-line summary per issue. Let the maintainer pick.
4. **Grill (if needed).** If the issue needs fleshing out, run a `/grill-with-docs` session.
5. **Apply the outcome:**
- `ready-for-agent` — post an agent brief comment ([agent-brief.md](references/agent-brief.md)).
- `ready-for-agent` — post an agent brief comment ([AGENT-BRIEF.md](AGENT-BRIEF.md)).
- `ready-for-human` — same structure as an agent brief, but note why it can't be delegated (judgment calls, external access, design decisions, manual testing).
- `needs-info` — post triage notes (template below).
- `wontfix` (bug) — polite explanation, then close.
- `wontfix` (enhancement) — write to `.out-of-scope/`, link to it from a comment, then close ([out-of-scope.md](references/out-of-scope.md)).
- `wontfix` (enhancement) — write to `.out-of-scope/`, link to it from a comment, then close ([OUT-OF-SCOPE.md](OUT-OF-SCOPE.md)).
- `needs-triage` — apply the role. Optional comment if there's partial progress.
## Quick state override

View File

@@ -6,10 +6,10 @@ description: >
module", "create docs for this feature", "write a README for this". Not an ADR
or other decision record -> `grill-with-docs`. Not an external tool researched
from its docs -> `research`.
version: "1.0"
updated: 2026-05-17
when: invoked by explicit trigger ("write docs for X", "document this module", "create docs for this feature") or implicit request to produce technical documentation from code or spec
metadata:
version: "1.0.0"
category: implement
source:
- repo: anthropics/skills

View File

@@ -2,8 +2,6 @@
name: zoom-out
description: Tell the agent to zoom out and give broader context or a higher-level perspective. Use when you're unfamiliar with a section of code or need to understand how it fits into the bigger picture.
disable-model-invocation: true
metadata:
version: "1.0.0"
---
I don't know this area of code well. Go up a layer of abstraction. Give me a map of all the relevant modules and callers, using the project's domain glossary vocabulary.

View File

@@ -1,6 +1,6 @@
{
"name": "bin",
"version": "1.1.7",
"version": "1.1.6",
"description": "Skills for everyday AI-assisted development work that is not tied to a single tool, forge or language, and has not yet been split into a focused plugin.",
"author": {
"name": "Defame1297",

View File

@@ -1,6 +1,6 @@
{
"name": "bin",
"version": "1.1.7",
"version": "1.1.6",
"description": "Skills for everyday AI-assisted development work that is not tied to a single tool, forge or language, and has not yet been split into a focused plugin.",
"author": {
"name": "Defame1297",

View File

@@ -1,5 +1,5 @@
name: bin
version: 1.1.7
version: 1.1.6
description: Skills for everyday AI-assisted development work that is not tied to a single tool, forge or language, and has not yet been split into a focused plugin.
author:
name: Defame1297

View File

@@ -4,8 +4,6 @@ disable-model-invocation: true
description: >
Ultra-compressed output mode that drops articles, filler and pleasantries while
keeping technical substance exact, cutting token usage by roughly 75%.
metadata:
version: "1.0.0"
---
Respond terse like smart caveman. All technical substance stay. Only fluff die.

View File

@@ -4,8 +4,6 @@ description: >
Use when the user says "diagnose this" or "debug this", reports something
broken, throwing, or failing, or says something got slow. Not filing or
triaging a reported bug -> `triage`. Not test-first feature work -> `tdd`.
metadata:
version: "1.0.0"
---
# Diagnose

View File

@@ -5,8 +5,6 @@ description: >
relentless interview — one question at a time, down each branch of the
decision tree. Not a plan to challenge against `CONTEXT.md` and ADRs ->
`grill-with-docs`.
metadata:
version: "1.0.0"
---
Interview me relentlessly about every aspect of this plan until we reach a shared understanding. Walk down each branch of the design tree, resolving dependencies between decisions one-by-one. For each question, provide your recommended answer.

View File

@@ -33,5 +33,5 @@ Describe the plan or design. Expect questions one at a time, each with a recomme
| File | Purpose |
|------|---------|
| `SKILL.md` | The interview instruction plus the domain-awareness rules: file layout discovery, the five during-session behaviours, and the three-part ADR test |
| `references/context-format.md` | Cited when a term is resolved: the structure of a `CONTEXT.md` and how to write a Language entry |
| `references/adr-format.md` | Cited when an ADR is offered: `docs/adr/` naming, sequential numbering, and the ADR template |
| `CONTEXT-FORMAT.md` | Skill-root document, cited when a term is resolved: the structure of a `CONTEXT.md` and how to write a Language entry |
| `ADR-FORMAT.md` | Skill-root document, cited when an ADR is offered: `docs/adr/` naming, sequential numbering, and the ADR template |

View File

@@ -4,8 +4,6 @@ description: >
Use when a plan should be stress-tested against the project's domain model —
the interview challenges terms against `CONTEXT.md` and writes decisions into
it and into ADRs as they land. Not a plain interview -> `grill-me`.
metadata:
version: "1.0.0"
---
<what-to-do>
@@ -76,7 +74,7 @@ When the user states how something works, check whether the code agrees. If you
### Update CONTEXT.md inline
When a term is resolved, update `CONTEXT.md` right there. Don't batch these up — capture them as they happen. Use the format in [context-format.md](references/context-format.md).
When a term is resolved, update `CONTEXT.md` right there. Don't batch these up — capture them as they happen. Use the format in [CONTEXT-FORMAT.md](./CONTEXT-FORMAT.md).
Don't couple `CONTEXT.md` to implementation details. Only include terms that are meaningful to domain experts.
@@ -88,6 +86,6 @@ Only offer to create an ADR when all three are true:
2. **Surprising without context** — a future reader will wonder "why did they do it this way?"
3. **The result of a real trade-off** — there were genuine alternatives and you picked one for specific reasons
If any of the three is missing, skip the ADR. Use the format in [adr-format.md](references/adr-format.md).
If any of the three is missing, skip the ADR. Use the format in [ADR-FORMAT.md](./ADR-FORMAT.md).
</supporting-info>

View File

@@ -1,6 +1,6 @@
# Deepening
How to deepen a cluster of shallow modules safely, given its dependencies. Assumes the vocabulary in [language.md](language.md) — **module**, **interface**, **seam**, **adapter**.
How to deepen a cluster of shallow modules safely, given its dependencies. Assumes the vocabulary in [LANGUAGE.md](LANGUAGE.md) — **module**, **interface**, **seam**, **adapter**.
## Dependency categories

View File

@@ -2,7 +2,7 @@
When the user wants to explore alternative interfaces for a chosen deepening candidate, use this parallel sub-agent pattern. Based on "Design It Twice" (Ousterhout) — your first idea is unlikely to be the best.
Uses the vocabulary in [language.md](language.md) — **module**, **interface**, **seam**, **adapter**, **leverage**.
Uses the vocabulary in [LANGUAGE.md](LANGUAGE.md) — **module**, **interface**, **seam**, **adapter**, **leverage**.
## Process
@@ -11,7 +11,7 @@ Uses the vocabulary in [language.md](language.md) — **module**, **interface**,
Before spawning sub-agents, write a user-facing explanation of the problem space for the chosen candidate:
- The constraints any new interface would need to satisfy
- The dependencies it would rely on, and which category they fall into (see [deepening.md](deepening.md))
- The dependencies it would rely on, and which category they fall into (see [DEEPENING.md](DEEPENING.md))
- A rough illustrative code sketch to ground the constraints — not a proposal, just a way to make the constraints concrete
Show this to the user, then immediately proceed to Step 2. The user reads and thinks while the sub-agents work in parallel.
@@ -20,21 +20,21 @@ Show this to the user, then immediately proceed to Step 2. The user reads and th
Spawn 3+ sub-agents in parallel using the Agent tool. Each must produce a **radically different** interface for the deepened module.
Prompt each sub-agent with a separate technical brief (file paths, coupling details, dependency category from [deepening.md](deepening.md), what sits behind the seam). The brief is independent of the user-facing problem-space explanation in Step 1. Give each agent a different design constraint:
Prompt each sub-agent with a separate technical brief (file paths, coupling details, dependency category from [DEEPENING.md](DEEPENING.md), what sits behind the seam). The brief is independent of the user-facing problem-space explanation in Step 1. Give each agent a different design constraint:
- Agent 1: "Minimize the interface — aim for 1–3 entry points max. Maximise leverage per entry point."
- Agent 2: "Maximise flexibility — support many use cases and extension."
- Agent 3: "Optimise for the most common caller — make the default case trivial."
- Agent 4 (if applicable): "Design around ports & adapters for cross-seam dependencies."
Include both [language.md](language.md) vocabulary and CONTEXT.md vocabulary in the brief so each sub-agent names things consistently with the architecture language and the project's domain language.
Include both [LANGUAGE.md](LANGUAGE.md) vocabulary and CONTEXT.md vocabulary in the brief so each sub-agent names things consistently with the architecture language and the project's domain language.
Each sub-agent outputs:
1. Interface (types, methods, params — plus invariants, ordering, error modes)
2. Usage example showing how callers use it
3. What the implementation hides behind the seam
4. Dependency strategy and adapters (see [deepening.md](deepening.md))
4. Dependency strategy and adapters (see [DEEPENING.md](DEEPENING.md))
5. Trade-offs — where leverage is high, where it's thin
### 3. Present and compare

View File

@@ -10,7 +10,7 @@ Looks for places where a codebase is hard to understand, hard to test, or hard f
2. **Present candidates.** A numbered list, each with files, problem, solution and benefits — benefits stated in terms of *locality* and *leverage* and of how tests would improve. No interfaces are proposed yet; the user picks one.
3. **Grilling loop.** Walks the design tree for the chosen candidate, with documentation side effects landing inline as decisions crystallise.
The skill is opinionated about vocabulary, and that is the point: **module, interface, implementation, depth, seam, adapter, leverage, locality**, used exactly, with no drift into "component", "service", "API" or "boundary". Domain nouns come from `CONTEXT.md`, architecture nouns from `references/language.md` — so a proposal reads as "the Order intake module", never "the FooBarHandler".
The skill is opinionated about vocabulary, and that is the point: **module, interface, implementation, depth, seam, adapter, leverage, locality**, used exactly, with no drift into "component", "service", "API" or "boundary". Domain nouns come from `CONTEXT.md`, architecture nouns from `LANGUAGE.md` — so a proposal reads as "the Order intake module", never "the FooBarHandler".
ADRs are treated as decisions not to be re-litigated. A candidate that contradicts one is surfaced only when the friction is real enough to warrant reopening it, and is marked as such.
@@ -31,6 +31,6 @@ Point at a codebase or an area of one. Expect a numbered candidate list and a "w
| File | Purpose |
|------|---------|
| `SKILL.md` | Condensed glossary, key principles, and the three-stage process |
| `references/language.md` | Cited throughout `SKILL.md`: full definitions of every term, the words each one replaces, and the full principle list |
| `references/interface-design.md` | Read at stage 3 when the user wants alternative interfaces explored: the parallel sub-agent "Design It Twice" pattern, framing the problem space, and the per-agent design constraints |
| `references/deepening.md` | Cited from `references/interface-design.md`: how to deepen a cluster of shallow modules safely, the four dependency categories (in-process, local-substitutable, remote-but-owned, true external), seam discipline, and the replace-don't-layer testing strategy |
| `LANGUAGE.md` | Skill-root document, cited throughout `SKILL.md`: full definitions of every term, the words each one replaces, and the full principle list |
| `INTERFACE-DESIGN.md` | Skill-root document, read at stage 3 when the user wants alternative interfaces explored: the parallel sub-agent "Design It Twice" pattern, framing the problem space, and the per-agent design constraints |
| `DEEPENING.md` | Skill-root document, cited from `INTERFACE-DESIGN.md`: how to deepen a cluster of shallow modules safely, the four dependency categories (in-process, local-substitutable, remote-but-owned, true external), seam discipline, and the replace-don't-layer testing strategy |

View File

@@ -6,8 +6,6 @@ description: >
testable and AI-navigable — deepening opportunities that turn shallow modules
into deep ones, informed by `CONTEXT.md` and `docs/adr/`. Not debugging a
failure -> `diagnose`.
metadata:
version: "1.0.0"
---
# Improve Codebase Architecture
@@ -33,7 +31,7 @@ Key principles:
- **The interface is the test surface.**
- **One adapter = hypothetical seam. Two adapters = real seam.**
If a term or principle above is ambiguous in the case in front of you, or you need the definitions and the principles the two lists leave out, read `references/language.md`.
If a term or principle above is ambiguous in the case in front of you, or you need the definitions and the principles the two lists leave out, read `LANGUAGE.md`.
This skill is _informed_ by the project's domain model. The domain language gives names to good seams; ADRs record decisions the skill should not re-litigate.
@@ -74,7 +72,7 @@ Once the user picks a candidate, drop into a grilling conversation. Walk the des
Side effects happen inline as decisions crystallize:
- **Naming a deepened module after a concept not in `CONTEXT.md`?** Add the term to `CONTEXT.md` — same discipline as `grill-with-docs`, in the format `grill-with-docs`'s `references/context-format.md` defines. Create the file lazily if it doesn't exist.
- **Naming a deepened module after a concept not in `CONTEXT.md`?** Add the term to `CONTEXT.md` — same discipline as `grill-with-docs`, in the format `grill-with-docs`'s `CONTEXT-FORMAT.md` defines. Create the file lazily if it doesn't exist.
- **Sharpening a fuzzy term during the conversation?** Update `CONTEXT.md` right there.
- **User rejects the candidate with a load-bearing reason?** Offer an ADR, framed as: _"Want me to record this as an ADR so future architecture reviews don't re-suggest it?"_ Only offer when the reason would actually be needed by a future explorer to avoid re-suggesting the same thing — skip ephemeral reasons ("not worth it right now") and self-evident ones. See `grill-with-docs`'s `references/adr-format.md`.
- **Want to explore alternative interfaces for the deepened module?** Read `references/interface-design.md`.
- **User rejects the candidate with a load-bearing reason?** Offer an ADR, framed as: _"Want me to record this as an ADR so future architecture reviews don't re-suggest it?"_ Only offer when the reason would actually be needed by a future explorer to avoid re-suggesting the same thing — skip ephemeral reasons ("not worth it right now") and self-evident ones. See `grill-with-docs`'s `ADR-FORMAT.md`.
- **Want to explore alternative interfaces for the deepened module?** Read `INTERFACE-DESIGN.md`.

View File

@@ -5,8 +5,6 @@ description: >
a data model, state machine or business logic, or to mock up a UI in several
variations. Not production code -> `tdd`. Not talking a design through ->
`grill-me`.
metadata:
version: "1.0.0"
---
# Prototype

View File

@@ -6,7 +6,6 @@ description: >-
documentation written from existing code or specs -> `write-docs`. Not a bug
or incident -> `diagnose`.
metadata:
version: "1.0.0"
category: research
allowed-tools:
- Grep

View File

@@ -25,8 +25,8 @@ Describe the feature or bug. Expect the skill to ask what the public interface s
| File | Purpose |
|------|---------|
| `SKILL.md` | Philosophy, the horizontal-slicing anti-pattern, the four-stage workflow, and the per-cycle checklist |
| `references/tests.md` | Cited from Philosophy: worked good and bad test examples |
| `references/mocking.md` | Cited from Philosophy: mock at system boundaries only, and what not to mock |
| `references/deep-modules.md` | Cited from stage 1: what a deep module is (small interface, large implementation) and why it is the design to aim for |
| `references/interface-design.md` | Cited from stage 1: designing interfaces for testability, starting with accepting dependencies rather than creating them |
| `references/refactoring.md` | Cited from stage 4: the refactor-candidate checklist — duplication, long methods, shallow modules, feature envy, primitive obsession |
| `tests.md` | Skill-root document, cited from Philosophy: worked good and bad test examples |
| `mocking.md` | Skill-root document, cited from Philosophy: mock at system boundaries only, and what not to mock |
| `deep-modules.md` | Skill-root document, cited from stage 1: what a deep module is (small interface, large implementation) and why it is the design to aim for |
| `interface-design.md` | Skill-root document, cited from stage 1: designing interfaces for testability, starting with accepting dependencies rather than creating them |
| `refactoring.md` | Skill-root document, cited from stage 4: the refactor-candidate checklist — duplication, long methods, shallow modules, feature envy, primitive obsession |

View File

@@ -4,8 +4,6 @@ description: >
Use when the user wants a feature built or a bug fixed test-first, in a strict
red-green-refactor loop, one behaviour at a time. Not diagnosing an existing
bug -> `diagnose`. Not throwaway exploratory code -> `prototype`.
metadata:
version: "1.0.0"
---
# Test-Driven Development
@@ -18,7 +16,7 @@ metadata:
**Bad tests** are coupled to implementation. They mock internal collaborators, test private methods, or verify through external means (like querying a database directly instead of using the interface). The warning sign: your test breaks when you refactor, but behavior hasn't changed. If you rename an internal function and tests fail, those tests were testing implementation, not behavior.
If you need worked examples of the difference — a behaviour-level test beside the implementation-coupled version of the same check — read `references/tests.md`. If a test needs a collaborator faked, read `references/mocking.md` before reaching for a mock.
If you need worked examples of the difference — a behaviour-level test beside the implementation-coupled version of the same check — read `tests.md`. If a test needs a collaborator faked, read `mocking.md` before reaching for a mock.
## Anti-Pattern: Horizontal Slices
@@ -55,8 +53,8 @@ Before writing any code:
- [ ] Confirm with user what interface changes are needed
- [ ] Confirm with user which behaviors to test (prioritize)
- [ ] Identify opportunities for [deep modules](references/deep-modules.md) (small interface, deep implementation)
- [ ] Design interfaces for [testability](references/interface-design.md)
- [ ] Identify opportunities for [deep modules](deep-modules.md) (small interface, deep implementation)
- [ ] Design interfaces for [testability](interface-design.md)
- [ ] List the behaviors to test (not implementation steps)
- [ ] Get user approval on the plan
@@ -93,7 +91,7 @@ Rules:
### 4. Refactor
After all tests pass, look for [refactor candidates](references/refactoring.md):
After all tests pass, look for [refactor candidates](refactoring.md):
- [ ] Extract duplication
- [ ] Deepen modules (move complexity behind simple interfaces)

View File

@@ -31,5 +31,5 @@ Then describe what you want in natural language — "show me anything that needs
| File | Purpose |
|------|---------|
| `SKILL.md` | The roles and state machine, the three invocation modes, the needs-info template, and how to resume a prior session |
| `references/agent-brief.md` | Cited when an issue moves to `ready-for-agent` (and reused for `ready-for-human`): how to write a brief that stays durable for weeks while the codebase moves under it — describe interfaces and behavioural contracts, not line numbers |
| `references/out-of-scope.md` | Cited when an enhancement is closed `wontfix` and when checking for prior rejections: how the `.out-of-scope/` knowledge base is laid out and what it is for — institutional memory, and deduplication against re-litigated requests |
| `AGENT-BRIEF.md` | Skill-root document, cited when an issue moves to `ready-for-agent` (and reused for `ready-for-human`): how to write a brief that stays durable for weeks while the codebase moves under it — describe interfaces and behavioural contracts, not line numbers |
| `OUT-OF-SCOPE.md` | Skill-root document, cited when an enhancement is closed `wontfix` and when checking for prior rejections: how the `.out-of-scope/` knowledge base is laid out and what it is for — institutional memory, and deduplication against re-litigated requests |

View File

@@ -4,8 +4,6 @@ description: >
Use when the user wants an issue created, triaged, or moved through the
tracker's triage states, or an issue prepared for an AFK agent. Not debugging
the bug itself -> `diagnose`. Not fleshing out a design -> `grill-with-docs`.
metadata:
version: "1.0.0"
---
# Triage
@@ -20,8 +18,8 @@ Every comment or issue posted to the issue tracker during triage **must** start
## Reference docs
- [agent-brief.md](references/agent-brief.md) — how to write durable agent briefs
- [out-of-scope.md](references/out-of-scope.md) — how the `.out-of-scope/` knowledge base works
- [AGENT-BRIEF.md](AGENT-BRIEF.md) — how to write durable agent briefs
- [OUT-OF-SCOPE.md](OUT-OF-SCOPE.md) — how the `.out-of-scope/` knowledge base works
## Roles
@@ -74,11 +72,11 @@ Show counts and a one-line summary per issue. Let the maintainer pick.
4. **Grill (if needed).** If the issue needs fleshing out, run a `/grill-with-docs` session.
5. **Apply the outcome:**
- `ready-for-agent` — post an agent brief comment ([agent-brief.md](references/agent-brief.md)).
- `ready-for-agent` — post an agent brief comment ([AGENT-BRIEF.md](AGENT-BRIEF.md)).
- `ready-for-human` — same structure as an agent brief, but note why it can't be delegated (judgment calls, external access, design decisions, manual testing).
- `needs-info` — post triage notes (template below).
- `wontfix` (bug) — polite explanation, then close.
- `wontfix` (enhancement) — write to `.out-of-scope/`, link to it from a comment, then close ([out-of-scope.md](references/out-of-scope.md)).
- `wontfix` (enhancement) — write to `.out-of-scope/`, link to it from a comment, then close ([OUT-OF-SCOPE.md](OUT-OF-SCOPE.md)).
- `needs-triage` — apply the role. Optional comment if there's partial progress.
## Quick state override

View File

@@ -6,10 +6,10 @@ description: >
module", "create docs for this feature", "write a README for this". Not an ADR
or other decision record -> `grill-with-docs`. Not an external tool researched
from its docs -> `research`.
version: "1.0"
updated: 2026-05-17
when: invoked by explicit trigger ("write docs for X", "document this module", "create docs for this feature") or implicit request to produce technical documentation from code or spec
metadata:
version: "1.0.0"
category: implement
source:
- repo: anthropics/skills

View File

@@ -2,8 +2,6 @@
name: zoom-out
description: Tell the agent to zoom out and give broader context or a higher-level perspective. Use when you're unfamiliar with a section of code or need to understand how it fits into the bigger picture.
disable-model-invocation: true
metadata:
version: "1.0.0"
---
I don't know this area of code well. Go up a layer of abstraction. Give me a map of all the relevant modules and callers, using the project's domain glossary vocabulary.

View File

@@ -28,5 +28,5 @@ This skill is reached two ways: invoked directly by a user, and composed into by
once it has written or updated the repo's `AGENTS.md`. Behave identically either way — do not
assume a caller skill exists. Detect the provider file yourself, confirm `AGENTS.md` yourself, and
run the closeout validator yourself, rather than treating any step as already done by the caller or
as something the caller will do afterwards. No handshake exists to rely on, and no state is
passed in beyond the file paths.
as something the caller will do afterwards. There is no handshake to rely on and no state passed
in beyond the file paths.

View File

@@ -28,5 +28,5 @@ This skill is reached two ways: invoked directly by a user, and composed into by
once it has written or updated the repo's `AGENTS.md`. Behave identically either way — do not
assume a caller skill exists. Detect the provider file yourself, confirm `AGENTS.md` yourself, and
run the closeout validator yourself, rather than treating any step as already done by the caller or
as something the caller will do afterwards. No handshake exists to rely on, and no state is
passed in beyond the file paths.
as something the caller will do afterwards. There is no handshake to rely on and no state passed
in beyond the file paths.

View File

@@ -9,7 +9,6 @@ description: >
Not a Gitea remote's branches -> `gitea-branches`.
metadata:
version: "1.0.1"
category: git
source_keys:
- context7-git-htmldocs
@@ -21,7 +20,7 @@ metadata:
## Gotchas
- **Uncommitted changes abort a switch.** `git switch` refuses rather than clobbering conflicting local edits. Offer to stash and retry — forcing the checkout past it is how work disappears.
- **A branch and a tag can carry the same name.** Detect it before acting — `git branch --list <name>` (bare, not `rtk`: rtk prints a phantom `* ` line even on no match, which reports every name as ambiguous — ADR-0023) and `rtk git tag --list <name>`; output from both means the name is ambiguous. Prefer `git switch` over `git checkout`, and where a command accepts either ref, disambiguate with `refs/heads/<name>` or `refs/tags/<name>`.
- **A branch and a tag can carry the same name.** Detect it before acting — `git branch --list <name>` and `git tag --list <name>`; output from both means the name is ambiguous. Prefer `git switch` over `git checkout`, and where a command accepts either ref, disambiguate with `refs/heads/<name>` or `refs/tags/<name>`.
- **`main` and `master` are a refusal, not a gate.** Force-pushing, force-deleting, or renaming them is rejected even when the caller passes `confirm: true` — no flag makes the remote's history recoverable. Offer a new branch instead.
## Step 1 — Determine the branching pattern
@@ -52,7 +51,7 @@ These gates are passable. The `main`/`master` refusal in Gotchas is not.
## Step 4 — Set tracking
A new branch's first push must be `rtk git push -u origin <branch>`. The push itself is `git-remotes`' — every remote-side gate lives there, which is why Step 2's remote-delete row hands off the same way — but the upstream requirement originates here, so carry it in the handoff. Without an upstream, later pushes and pulls either fail or silently target the wrong remote branch, and the caller has no way to tell which happened.
A new branch's first push must be `git push -u origin <branch>`. The push itself is `git-remotes`' — every remote-side gate lives there, which is why Step 2's remote-delete row hands off the same way — but the upstream requirement originates here, so carry it in the handoff. Without an upstream, later pushes and pulls either fail or silently target the wrong remote branch, and the caller has no way to tell which happened.
## Step 5 — Return a structured result

View File

@@ -8,21 +8,21 @@ source_keys:
One command per action. Where two forms exist, the first is the default and the second the escape
hatch.
- **create** — `rtk git switch -c <branch> <base>`. Base comes from the config's `base_branch`
- **create** — `git switch -c <branch> <base>`. Base comes from the config's `base_branch`
(`main` under GitHub Flow, usually `develop` under Gitflow).
- **switch** — `rtk git switch <branch>` moves to an existing local branch; it aborts rather than
clobbering conflicting local changes. `rtk git switch -` returns to the previous branch.
- **delete (local)** — `rtk git branch -d <branch>` refuses when the branch holds unmerged commits,
which is why it is the default. `rtk git branch -D <branch>` forces the deletion and discards that
- **switch** — `git switch <branch>` moves to an existing local branch; it aborts rather than
clobbering conflicting local changes. `git switch -` returns to the previous branch.
- **delete (local)** — `git branch -d <branch>` refuses when the branch holds unmerged commits,
which is why it is the default. `git branch -D <branch>` forces the deletion and discards that
work — only after the destructive-operation gates pass and `confirm: true` is set.
- **delete (remote)** — not this skill's. Deleting a remote branch is a push, and every remote-side
gate lives in `git-remotes`; hand it there rather than running the push from here. Its
`references/push.md` carries the command and the refspec form.
- **rename** — `rtk git branch -m <old> <new>`.
- **list** — `rtk git branch` (local), `-a` (local plus remote-tracking), `-r` (remote-tracking only),
- **rename** — `git branch -m <old> <new>`.
- **list** — `git branch` (local), `-a` (local plus remote-tracking), `-r` (remote-tracking only),
`--merged` / `--no-merged` (filter by merge status into the current branch).
- **track** — `rtk git branch --set-upstream-to=origin/<branch>` sets an upstream without pushing.
`rtk git branch -vv` shows the tracking state of every local branch.
- **track** — `git branch --set-upstream-to=origin/<branch>` sets an upstream without pushing.
`git branch -vv` shows the tracking state of every local branch.
## get-intent
@@ -40,20 +40,17 @@ intent is worse than one built on none.
A switch aborts rather than clobbering conflicting local changes (see Gotchas). Stash is the way
past it: it shelves the working tree and index so the branch pointer can move.
- **save** — `rtk git stash push -m "<message>"`. Add `-u` to include untracked files; verified on Git
- **save** — `git stash push -m "<message>"`. Add `-u` to include untracked files; verified on Git
2.39.5, a plain `push` leaves them in place, and a plain `push` with *only* untracked changes
reports `No local changes to save` and stashes nothing. Bare `git stash` is `push` with no message.
- **restore** — `git stash pop` applies the newest entry and deletes it. Bare, not `rtk`: on a
conflict rtk prints only `FAILED: git stash pop` and swallows the conflict report the paragraph
below tells you to read (ADR-0023). `rtk git stash apply stash@{n}`
- **restore** — `git stash pop` applies the newest entry and deletes it. `git stash apply stash@{n}`
applies without deleting, for replaying one shelf onto more than one branch.
- **list** — `git stash list` — bare, not `rtk`: rtk prints `No stashes` where git prints nothing,
so an empty-output test misfires (ADR-0023). `rtk git stash show -p stash@{n}` prints that entry's diff.
- **drop** — `rtk git stash drop stash@{n}` deletes one entry. `rtk git stash clear` deletes all of them
- **list** — `git stash list`; `git stash show -p stash@{n}` prints that entry's diff.
- **drop** — `git stash drop stash@{n}` deletes one entry. `git stash clear` deletes all of them
and nothing recovers them — confirm before running it.
- **branch from a stash** — `rtk git stash branch <branch> stash@{n}` creates a branch at the commit the
- **branch from a stash** — `git stash branch <branch> stash@{n}` creates a branch at the commit the
stash was taken from and pops it there. Use it when the stash no longer applies to the current tip.
**A conflicting `pop` keeps the entry.** Verified on 2.39.5: it exits 1, writes conflict markers,
prints "The stash entry is kept in case you need it again", and `git stash list` still shows it.
Resolve, `rtk git add`, then `rtk git stash drop` the entry by hand — otherwise it silently accumulates.
Resolve, `git add`, then `git stash drop` the entry by hand — otherwise it silently accumulates.

View File

@@ -8,9 +8,9 @@ source_keys:
The two-dot and three-dot forms mean different things and are easy to swap by accident — check the
direction before reporting a result.
- `rtk git log main..feature` — commits on `feature` that are not on `main`.
- `rtk git log feature..main` — the reverse direction: commits on `main` not on `feature`.
- `rtk git log --left-right main...feature` — both diverging sets at once (symmetric difference).
- `rtk git diff main...feature` — the diff from the common ancestor to `feature`'s tip, which is what
- `git log main..feature` — commits on `feature` that are not on `main`.
- `git log feature..main` — the reverse direction: commits on `main` not on `feature`.
- `git log --left-right main...feature` — both diverging sets at once (symmetric difference).
- `git diff main...feature` — the diff from the common ancestor to `feature`'s tip, which is what
a reviewer sees, rather than the diff between the two tips.
- `rtk git merge-base main feature` — print the common ancestor commit.
- `git merge-base main feature` — print the common ancestor commit.

View File

@@ -9,23 +9,22 @@ source_keys:
Scope is fast-forward and merge-commit mechanics plus conflict resolution. Rebase and cherry-pick
belong to `git-commits`; revert to `git-history`.
- **Fast-forward** — `rtk git merge <branch>` advances the pointer with no merge commit when the
- **Fast-forward** — `git merge <branch>` advances the pointer with no merge commit when the
target has not diverged.
- **True merge** — `rtk git merge --no-ff <branch>` forces a merge commit even when a fast-forward is
- **True merge** — `git merge --no-ff <branch>` forces a merge commit even when a fast-forward is
possible. Gitflow requires it on every supporting-branch merge.
- **Squash merge** — `rtk git merge --squash <branch>` stages the combined diff without committing.
Follow it with a `rtk git commit`.
- **Octopus merge** — `rtk git merge branch-a branch-b branch-c` merges more than two branches at
- **Squash merge** — `git merge --squash <branch>` stages the combined diff without committing.
Follow it with a `git commit`.
- **Octopus merge** — `git merge branch-a branch-b branch-c` merges more than two branches at
once, but fails outright on any conflict. Use sequential two-way merges when conflicts are
likely.
## Conflict resolution
When Git cannot auto-merge it writes conflict markers and stops mid-merge. Run `rtk git status` to
list the conflicted files, edit each to resolve its markers, then `rtk git add <file>` and
`rtk git merge --continue`.
When Git cannot auto-merge it writes conflict markers and stops mid-merge. Run `git status` to
list the conflicted files, edit each to resolve its markers, then `git add <file>` and
`git merge --continue`.
- `rtk git merge --abort` restores the pre-merge state.
- `git mergetool` opens the configured merge tool — bare, not `rtk`: it hands control to an
interactive child process, and a token filter has nothing to offer there (ADR-0023).
- `rtk git diff --diff-filter=U` shows only the still-conflicted files.
- `git merge --abort` restores the pre-merge state.
- `git mergetool` opens the configured merge tool.
- `git diff --diff-filter=U` shows only the still-conflicted files.

View File

@@ -8,7 +8,7 @@ description: >
Not branch lifecycle -> `git-branches`.
metadata:
version: "0.1.4"
version: "0.1.3"
category: git
source_keys:
- conventional-commits-spec
@@ -21,7 +21,7 @@ allowed-tools: Bash
## Gotchas
- **Run git as `rtk git <subcommand>`, never bare `git`** — org convention, in `&&` chains too. Exceptions: ADR-0023 clause 3.
- **Run git as `rtk git <subcommand>`, never bare `git`** — org convention, in `&&` chains too.
- **Refuse to force-push `main`/`master`** — a rewrite leaves the branch diverged and the reflex is to force it back; safe only where nobody else has based work on it.
- **`reset --hard` is a confirmation gate, not a default.** It overwrites the working tree, and uncommitted edits it discards were never in git, so no reflog recovers them. Name what will be lost and offer a stash first.
- **Never add `--no-verify`** — using it when a hook fails bypasses the QA gate the pipeline depends on. Only on the user's explicit demand, with a warning.

View File

@@ -21,9 +21,9 @@ Prefer this whenever a commit is written to be folded, because git does the mark
1. `rtk git commit --fixup=<commit>` keeps the target's message; `rtk git commit --squash=<commit>` lets you edit the combined message later. Both prefix the message with `fixup!`/`squash!` and name the target commit.
2. Get explicit approval — the rebase still rewrites history.
3. Run `git rebase -i --autosquash HEAD~N` — bare, not `rtk`: `-i` opens an interactive sequence editor (ADR-0023). Git pre-fills the todo list with the tagged commits already reordered against their targets; save it unchanged to apply.
3. Run `rtk git rebase -i --autosquash HEAD~N`. Git pre-fills the todo list with the tagged commits already reordered against their targets; save it unchanged to apply.
**`-i` is not optional here.** On Git 2.39.5, `git rebase --autosquash HEAD~N` without `-i` prints `Successfully rebased and updated refs/heads/<branch>.` and exits 0 while leaving the `fixup!` commit in place at its original SHA — `--autosquash` is honoured only by the interactive machinery, and the false success is the trap: the fold is reported as done, and the surviving `fixup!` subject then fails the Conventional Commits `commit-msg` hook. Later Git versions taught the non-interactive rebase to honour the flag, but `-i --autosquash` is correct on every version, so always write that.
**`-i` is not optional here.** On Git 2.39.5, `rtk git rebase --autosquash HEAD~N` without `-i` prints `Successfully rebased and updated refs/heads/<branch>.` and exits 0 while leaving the `fixup!` commit in place at its original SHA — `--autosquash` is honoured only by the interactive machinery, and the false success is the trap: the fold is reported as done, and the surviving `fixup!` subject then fails the Conventional Commits `commit-msg` hook. Later Git versions taught the non-interactive rebase to honour the flag, but `-i --autosquash` is correct on every version, so always write that.
## Squash by hand (interactive rebase)
@@ -31,7 +31,7 @@ Use this when the commits were not tagged at commit time. **Interactive rebase h
1. Identify the commits to squash — typically the last N on the current branch.
2. Get explicit approval.
3. Run `git rebase -i HEAD~N` — bare, not `rtk`, for the same interactive-editor reason — marking the older commits `squash` to keep their messages for editing, or `fixup` to discard them.
3. Run `rtk git rebase -i HEAD~N`, marking the older commits `squash` to keep their messages for editing, or `fixup` to discard them.
4. Compose the combined message when the rebase stops to ask. For a non-trivial combined message, follow the structure in `references/commit-template.md`.
## When a rebase halts on a conflict

View File

@@ -8,7 +8,6 @@ description: >
`git-commits`. Not a Gitea server's history -> `gitea-branches`.
metadata:
version: "1.0.1"
category: git
source_keys:
- git-scm-bisect-docs
@@ -35,13 +34,13 @@ allowed-tools: Bash
## Step 2 — Query the log
Default to `rtk git log --oneline`, then narrow by whatever is known:
Default to `git log --oneline`, then narrow by whatever is known:
- **Content**: `rtk git log -S"string"`, or `-G"regex"` to match any diff line. `--pickaxe-regex` makes the `-S` argument a POSIX ERE; `--pickaxe-all` shows every file in a matching changeset.
- **A line or function**: `git log -L <start>,<end>:<file>` or `git log -L :<function>:<file>` — bare, not `rtk`: rtk truncates each diff line at ~72 characters (ADR-0023). Confirm the range resolves before reporting on it — an off-by-one silently omits the target.
- **A file across renames**: `rtk git log --follow -- <file>`. Without `--follow` the history stops at the rename boundary.
- **Content**: `git log -S"string"`, or `-G"regex"` to match any diff line. `--pickaxe-regex` makes the `-S` argument a POSIX ERE; `--pickaxe-all` shows every file in a matching changeset.
- **A line or function**: `git log -L <start>,<end>:<file>` or `git log -L :<function>:<file>`. Confirm the range resolves before reporting on it — an off-by-one silently omits the target.
- **A file across renames**: `git log --follow -- <file>`. Without `--follow` the history stops at the rename boundary.
- **Mainline only**: `--first-parent` follows the integration branch and skips commits merged in from side branches.
- **Structured output**: `rtk git log --format="%h | %s | %an (%ar)"`.
- **Structured output**: `git log --format="%h | %s | %an (%ar)"`.
If you need the placeholder catalogue, format presets, `--diff-filter` letters, full `-L` syntax, ancestry filters, pickaxe binary-file behaviour, or `git diff` output-control flags such as `--stat`, `--word-diff` and the whitespace options, read `references/git-log-format.md`.
@@ -50,8 +49,8 @@ If you need the placeholder catalogue, format presets, `--diff-filter` letters,
Offer the operation and its consequence; run it only once the user has chosen.
- Backporting the commit to another branch is a cherry-pick, and cherry-pick is `git-commits`' — it owns the destination-branch check, the `rtk git` wrapper and the `--abort` path. Hand it the SHA; do not run `git cherry-pick` from here.
- `rtk git revert <commit>` adds a new commit undoing it — for un-applying merged work without rewriting history.
- `rtk git blame <file>` attributes each line to the commit that last touched it, when the question is which commit introduced one specific line.
- `git revert <commit>` adds a new commit undoing it — for un-applying merged work without rewriting history.
- `git blame <file>` attributes each line to the commit that last touched it, when the question is which commit introduced one specific line.
For diff output control on the located commit, read `references/git-log-format.md`.

View File

@@ -12,43 +12,43 @@ or line range to search the log for. Binary search reduces the trials from O(N)
## Manual flow
```bash
rtk git bisect start
rtk git bisect bad [HEAD] # mark current (or specified) as broken
rtk git bisect good <commit> # mark known-good baseline
git bisect start
git bisect bad [HEAD] # mark current (or specified) as broken
git bisect good <commit> # mark known-good baseline
# Git checks out the midpoint; test it
rtk git bisect good # test passes
rtk git bisect bad # test fails
git bisect good # test passes
git bisect bad # test fails
# Repeat until git reports "X is the first bad commit"
rtk git bisect reset # return to the original HEAD
git bisect reset # return to the original HEAD
```
## Automated
With a test command available, use `rtk git bisect run <cmd>`. Git reads the exit code: `0` good,
With a test command available, use `git bisect run <cmd>`. Git reads the exit code: `0` good,
`1`–`124` bad, `125` skip (build broken), `126`–`127` POSIX shell errors, treated as bad, and
`128` or above aborts the session outright rather than marking the commit bad.
## Untestable commits
`rtk git bisect skip` excludes a commit that cannot be built or tested without deciding good or bad
`git bisect skip` excludes a commit that cannot be built or tested without deciding good or bad
for it. When the first bad commit is adjacent to a skipped range, bisect reports that it cannot
pinpoint the culprit and lists the candidates — that is the precise answer the skip range allows,
not a failure.
## Undoing a wrong good/bad call
`rtk git bisect log` prints the session's decision history. Save it, edit out the mistaken entry, and
`git bisect log` prints the session's decision history. Save it, edit out the mistaken entry, and
resume from the corrected log rather than restarting the search:
```bash
rtk git bisect log > bisect.log
git bisect log > bisect.log
# edit bisect.log, removing the wrong decision
rtk git bisect reset && rtk git bisect replay bisect.log
git bisect reset && git bisect replay bisect.log
```
## Narrowing and speeding up
- `rtk git bisect start HEAD v1.2 -- src/` restricts bisection to a path, cutting the trial count.
- `git bisect start HEAD v1.2 -- src/` restricts bisection to a path, cutting the trial count.
- `--no-checkout` updates the `BISECT_HEAD` ref instead of checking out a working tree — useful
for tests that do not need one, and automatic in bare repos.
- `--first-parent` follows only first parents at merges, finding the integration commit that
@@ -56,12 +56,12 @@ rtk git bisect reset && rtk git bisect replay bisect.log
## Inspecting the remaining candidates
`rtk git bisect visualize` (alias `view`) opens the suspects in gitk, falling back to `git log` when
`git bisect visualize` (alias `view`) opens the suspects in gitk, falling back to `git log` when
no graphical display is detected. Add `--stat` or `-p` for a diffstat or full patches.
## Hunting a non-bug property change
`rtk git bisect start --term-new <new> --term-old <old>` searches for any property change — a
`git bisect start --term-new <new> --term-old <old>` searches for any property change — a
performance regression, say — instead of a bug. Use the custom terms in place of `good` and `bad`
for the rest of the session.

View File

@@ -116,15 +116,15 @@ source_keys:
**`-S<string>`** — finds commits where the **count** of `<string>` changed (i.e. the string was added or removed net). Does not match commits where the string merely appears in a diff hunk without a count change.
```bash
rtk git log -S"my_function"
rtk git log -S"my_function" --pickaxe-regex # treat as POSIX ERE
rtk git log -S"my_function" --pickaxe-all # show all files in matching changesets
git log -S"my_function"
git log -S"my_function" --pickaxe-regex # treat as POSIX ERE
git log -S"my_function" --pickaxe-all # show all files in matching changesets
```
**`-G<regex>`** — finds commits where any added or removed **line** in the patch matches `<regex>`. Broader than `-S`: matches whenever the pattern appears in diff text regardless of count.
```bash
rtk git log -G"frotz\(nitfol"
git log -G"frotz\(nitfol"
```
**Critical distinction:** given a diff that removes one occurrence of `foo` and adds one occurrence of `foo` (net change = 0):
@@ -151,8 +151,8 @@ Selects commits (in `git log`) or files (in `git diff`) by change type:
Lowercase letters **exclude** that type:
```bash
rtk git log --diff-filter=ad # exclude added and deleted files
rtk git log --diff-filter=M # only show commits with modified files
git log --diff-filter=ad # exclude added and deleted files
git log --diff-filter=M # only show commits with modified files
```
`C` and `R` only appear when copy/rename detection is enabled (`-C`, `-M` flags or `diff.renames` config).
@@ -161,15 +161,11 @@ rtk git log --diff-filter=M # only show commits with modified files
Traces the evolution of a specific range of lines or a named function through commits. Implies `--patch`.
Bare `git`, not `rtk git`, on every `-L` form below: rtk truncates each diff body
line at roughly 72 characters with an ellipsis, on the one query whose whole point
is showing line content.
```bash
git log -L 10,20:file.txt # bare per ADR-0023
git log -L /start_pattern/,/end_pattern/:file.txt # bare per ADR-0023
git log -L :myfunction:src/app.c # bare per ADR-0023
git log -L /init/,+15:config.py # bare per ADR-0023; 15 lines after first /init/ match
git log -L 10,20:file.txt
git log -L /start_pattern/,/end_pattern/:file.txt
git log -L :myfunction:src/app.c
git log -L /init/,+15:config.py # 15 lines after first match of /init/
```
Range formats:
@@ -186,12 +182,12 @@ Limitations: incompatible with `--raw`, `--numstat`, `--shortstat`, `--name-only
## Graph and Ancestry Filters
```bash
rtk git log --first-parent # at merges, follow only first parent (mainline evolution)
rtk git log --merges # only merge commits (≥2 parents); equivalent to --min-parents=2
rtk git log --no-merges # only non-merge commits; equivalent to --max-parents=1
rtk git log --ancestry-path D..M # only commits actually on the path from D to M
rtk git log --min-parents=<n> # include only commits with ≥ n parents
rtk git log --max-parents=<n> # include only commits with ≤ n parents
git log --first-parent # at merges, follow only first parent (mainline evolution)
git log --merges # only merge commits (≥2 parents); equivalent to --min-parents=2
git log --no-merges # only non-merge commits; equivalent to --max-parents=1
git log --ancestry-path D..M # only commits actually on the path from D to M
git log --min-parents=<n> # include only commits with ≥ n parents
git log --max-parents=<n> # include only commits with ≤ n parents
```
`--ancestry-path` is significant: without it, `D..M` includes all commits reachable from M but not D — including side branches that merged into the path. With it, only commits directly between D and M are shown.
@@ -201,34 +197,28 @@ rtk git log --max-parents=<n> # include only commits with ≤ n parents
### --stat
```bash
rtk git diff --stat # diffstat: file names + ± bar
rtk git diff --stat=<width>,<name-width>,<count>
rtk git diff --compact-summary # alongside --stat: shows new/gone, +x/-x (executable), +l (symlink)
rtk git diff --numstat # machine-readable: <added>\t<deleted>\t<path>; - for binary
git diff --stat # diffstat: file names + ± bar
git diff --stat=<width>,<name-width>,<count>
git diff --compact-summary # alongside --stat: shows new/gone, +x/-x (executable), +l (symlink)
git diff --numstat # machine-readable: <added>\t<deleted>\t<path>; - for binary
```
### --name-only / --name-status
Bare `git`, not `rtk git`: rtk appends a blank line and a `Changes:` trailer, so
the output is no longer one record per line.
```bash
git diff --name-only # bare per ADR-0023; only filenames, one per line
git diff --name-status # bare per ADR-0023; status letter + filename per line
git diff --name-only # only filenames, one per line
git diff --name-status # status letter + filename per line
```
`--name-status` uses the same status letters as `--diff-filter`.
### --word-diff
Bare `git`, not `rtk git`: rtk replaces the word-diff with its own diffstat
renderer and emits none of the `[-removed-] {+added+}` markers.
```bash
git diff --word-diff # bare per ADR-0023; inline word-level diff, [-removed-] {+added+} markers
git diff --word-diff=color # bare per ADR-0023; color only, no markers
git diff --word-diff=porcelain # bare per ADR-0023; machine-readable: +/- prefixed lines, ~ for newlines
git diff --word-diff-regex=<re> # bare per ADR-0023; define what counts as a "word"
git diff --word-diff # inline word-level diff with [-removed-] {+added+} markers
git diff --word-diff=color # color only, no markers
git diff --word-diff=porcelain # machine-readable: +/- prefixed lines, ~ for newlines
git diff --word-diff-regex=<re> # define what counts as a "word"
```
### Whitespace Flags

View File

@@ -10,7 +10,6 @@ description: >
Not submodule pointers -> `git-submodules`.
metadata:
version: "1.0.1"
category: git
source_keys:
- git-scm-remote-docs
@@ -28,7 +27,7 @@ metadata:
## Step 1 — Clear the force-push gate
`main` and `master` are a hard refusal: decline a force-push targeting either, whatever confirmation accompanies it, because no local approval can restore what the remote loses. On any other branch, `rtk git push --force` and `-f` run only after the caller passes `confirm: true` for that specific push — for a human caller, prompt instead of failing.
`main` and `master` are a hard refusal: decline a force-push targeting either, whatever confirmation accompanies it, because no local approval can restore what the remote loses. On any other branch, `git push --force` and `-f` run only after the caller passes `confirm: true` for that specific push — for a human caller, prompt instead of failing.
## Step 2 — Dispatch

View File

@@ -11,19 +11,19 @@ Fetch **with no refspec** updates remote-tracking branches (`refs/remotes/<name>
That safety comes from the default refspec, not from `fetch` itself. Give it an explicit one and it writes to local branches: verified on Git 2.39.5, `git fetch origin main:probe` fast-forwarded the local `probe` branch, and a `+` prefix force-updates the destination, discarding whatever commits it held. Treat any `fetch` carrying a `<src>:<dst>` refspec as a branch update, not a read.
- **One remote**: `rtk git fetch <remote>` — all branches
- **One branch**: `rtk git fetch <remote> <branch>` — the result lands in `FETCH_HEAD`, not a tracking ref
- **All remotes**: `rtk git fetch --all`
- **Prune properly**: `rtk git fetch --all --prune --prune-tags` cleans stale branches *and* tags
- **Auto-prune**: `rtk git config --global fetch.prune true` (or `remote.<name>.prune` to scope it to one remote), and `fetch.pruneTags true` for tags
- **One remote**: `git fetch <remote>` — all branches
- **One branch**: `git fetch <remote> <branch>` — the result lands in `FETCH_HEAD`, not a tracking ref
- **All remotes**: `git fetch --all`
- **Prune properly**: `git fetch --all --prune --prune-tags` cleans stale branches *and* tags
- **Auto-prune**: `git config --global fetch.prune true` (or `remote.<name>.prune` to scope it to one remote), and `fetch.pruneTags true` for tags
## Shallow and partial fetch
```bash
rtk git fetch --depth=<n> # deepen history, or create a shallow clone
rtk git fetch --unshallow # convert a shallow clone to full history
rtk git fetch --update-shallow # allow the fetch to update the shallow boundary
rtk git fetch --refmap='' <remote> <branch> # fetch without updating any tracking ref (FETCH_HEAD only)
git fetch --depth=<n> # deepen history, or create a shallow clone
git fetch --unshallow # convert a shallow clone to full history
git fetch --update-shallow # allow the fetch to update the shallow boundary
git fetch --refmap='' <remote> <branch> # fetch without updating any tracking ref (FETCH_HEAD only)
```
## Default fetch refspec

View File

@@ -9,11 +9,11 @@ source_keys:
Default strategy: `--ff-only`. It fails on divergence, which forces a conscious choice instead of an accidental merge commit.
- **Fast-forward only**: `rtk git pull --ff-only` — the recommended default
- **Rebase**: `rtk git pull --rebase` replays your commits on top for linear history, but rewrites SHAs. Verify nothing being replayed has been pushed: rebasing published commits breaks everyone downstream.
- **Merge**: `rtk git pull --no-rebase` — three-way merge commit, preserves original commits, non-linear
- **Rebase preserving merges**: `rtk git pull --rebase=merges` keeps intentional local merge commits during the replay
- **Stage without committing**: `rtk git pull --squash` collapses incoming commits into staged changes; you write the message
- **Fast-forward only**: `git pull --ff-only` — the recommended default
- **Rebase**: `git pull --rebase` replays your commits on top for linear history, but rewrites SHAs. Verify nothing being replayed has been pushed: rebasing published commits breaks everyone downstream.
- **Merge**: `git pull --no-rebase` — three-way merge commit, preserves original commits, non-linear
- **Rebase preserving merges**: `git pull --rebase=merges` keeps intentional local merge commits during the replay
- **Stage without committing**: `git pull --squash` collapses incoming commits into staged changes; you write the message
- **Merge strategy**: Git 2.34+ defaults to `ort` (`recursive` is now an alias for it). Strategy options such as `-X ours`, `-X theirs`, `-X ignore-space-change` pass through unchanged.
- **Submodules**: `--recurse-submodules` only fetches submodules already checked out. Newly added ones are not initialized — use the `git-submodules` skill for those.
@@ -37,7 +37,7 @@ Highest wins:
4. `branch.autoSetupRebase` (set automatically when the tracking branch was created)
```bash
rtk git config pull.ff only # deterministic default across Git versions
rtk git config --global pull.rebase true
rtk git config branch.develop.rebase false # develop always merges, regardless of the global default
git config pull.ff only # deterministic default across Git versions
git config --global pull.rebase true
git config branch.develop.rebase false # develop always merges, regardless of the global default
```

View File

@@ -9,14 +9,14 @@ source_keys:
Default: safe push to the same-named branch on the remote.
- **Force-push**: never bare `--force`. Use `rtk git push --force-with-lease --force-if-includes <remote> <branch>`, after the SKILL.md Step 1 gate.
- **Basic**: `rtk git push <remote> <branch>`
- **Set upstream**: `rtk git push -u <remote> <branch>` — push and configure tracking
- **Multi-remote**: push sequentially (`rtk git push origin develop`, `rtk git push staging develop`), or add a second push URL with `rtk git remote set-url --add <name> <url>` to reach both in one command
- **Delete a remote branch**: `rtk git push <remote> --delete <branch>` — clearer than the `:<branch>` form
- **Bulk**: `rtk git push --all` (all local branches), `rtk git push --tags` (all tags), `rtk git push origin <tag>` (one tag)
- **Delete remote branches with no local counterpart**: `rtk git push --prune origin 'refs/heads/*:refs/heads/*'`
- **Force only part of a multi-ref push**: prefix the one refspec that needs it with `+` — `rtk git push origin +release develop` forces `release` while safe-pushing `develop`. A `+` prefix is a force-push and passes the SKILL.md Step 1 gate like any other.
- **Force-push**: never bare `--force`. Use `git push --force-with-lease --force-if-includes <remote> <branch>`, after the SKILL.md Step 1 gate.
- **Basic**: `git push <remote> <branch>`
- **Set upstream**: `git push -u <remote> <branch>` — push and configure tracking
- **Multi-remote**: push sequentially (`git push origin develop`, `git push staging develop`), or add a second push URL with `git remote set-url --add <name> <url>` to reach both in one command
- **Delete a remote branch**: `git push <remote> --delete <branch>` — clearer than the `:<branch>` form
- **Bulk**: `git push --all` (all local branches), `git push --tags` (all tags), `git push origin <tag>` (one tag)
- **Delete remote branches with no local counterpart**: `git push --prune origin 'refs/heads/*:refs/heads/*'`
- **Force only part of a multi-ref push**: prefix the one refspec that needs it with `+` — `git push origin +release develop` forces `release` while safe-pushing `develop`. A `+` prefix is a force-push and passes the SKILL.md Step 1 gate like any other.
## Refspec syntax — `[+]<src>[:<dst>]`
@@ -48,21 +48,19 @@ Two mitigations:
# Option 1 — dedicated push-only remote: background tools fetch `origin`, you push
# through a separate remote that nothing else touches, so its tracking ref can't be
# poisoned by an unrelated fetch.
# The inner `git config` is bare: its stdout becomes a remote URL, so any
# output rewriting would poison the remote silently.
rtk git remote add origin-push $(git config remote.origin.url) # inner bare per ADR-0023
rtk git push --force-with-lease origin-push
git remote add origin-push $(git config remote.origin.url)
git push --force-with-lease origin-push
# Option 2 — explicit SHA via a local tag, unaffected by tracking-branch state
rtk git fetch
rtk git tag base master
git rebase -i master # bare, not `rtk` (ADR-0023): interactive sequence editor
rtk git push --force-with-lease=master:base master:master
git fetch
git tag base master
git rebase -i master
git push --force-with-lease=master:base master:master
```
`--force-if-includes` adds a second check on top of bare `--force-with-lease`: it verifies the remote-tracking tip actually appears in your local branch's reflog, i.e. you genuinely integrated it before rewriting. It is a no-op without `--force-with-lease`, and has no effect with the `--force-with-lease=<ref>:<sha>` form, which already pins an exact SHA.
Safest combination: `rtk git push --force-with-lease --force-if-includes origin`.
Safest combination: `git push --force-with-lease --force-if-includes origin`.
## Server-side policy

View File

@@ -9,31 +9,31 @@ source_keys:
Which remotes exist, where they point, and what they track.
`rtk git remote show <name>` needs network access — use `-n` for cached data offline, or `rtk git remote -v`, which lists URLs without querying.
`git remote show <name>` needs network access — use `-n` for cached data offline, or `git remote -v`, which lists URLs without querying.
## Add, remove, rename, inspect
- **Add**: `rtk git remote add <name> <url>`, or `-f` to fetch immediately
- **Remove**: `rtk git remote remove <name>` — deletes the remote, all its tracking refs, and its config
- **Rename**: `rtk git remote rename <old> <new>`
- **Inspect**: `rtk git remote -v` (URLs, offline) or `rtk git remote show <name>` (live tracking status)
- **Effective URLs**: `rtk git remote get-url <name>` shows the URL after `insteadOf` rewrites; `rtk git remote get-url --push --all <name>` lists every push URL
- **Add**: `git remote add <name> <url>`, or `-f` to fetch immediately
- **Remove**: `git remote remove <name>` — deletes the remote, all its tracking refs, and its config
- **Rename**: `git remote rename <old> <new>`
- **Inspect**: `git remote -v` (URLs, offline) or `git remote show <name>` (live tracking status)
- **Effective URLs**: `git remote get-url <name>` shows the URL after `insteadOf` rewrites; `git remote get-url --push --all <name>` lists every push URL
## Tracking, mirroring, housekeeping
- **Track one branch**: `rtk git remote add -t <branch> <name> <url>` (repeatable); `--no-tags` suppresses tag import entirely
- **Track one branch**: `git remote add -t <branch> <name> <url>` (repeatable); `--no-tags` suppresses tag import entirely
- **Mirror**: `--mirror=fetch` mirrors all refs locally (bare repos only); `--mirror=push` makes every push behave like `--mirror`
- **Prune stale tracking refs without fetching**: `rtk git remote prune <name>`, with `--dry-run` to preview
- **Default branch pointer**: `rtk git remote set-head <name> -a` (auto-detect, needs a prior fetch), `... <branch>` (explicit), `... -d` (delete `refs/remotes/<name>/HEAD`)
- **Prune stale tracking refs without fetching**: `git remote prune <name>`, with `--dry-run` to preview
- **Default branch pointer**: `git remote set-head <name> -a` (auto-detect, needs a prior fetch), `... <branch>` (explicit), `... -d` (delete `refs/remotes/<name>/HEAD`)
## `set-url` — full form
```bash
rtk git remote set-url <name> <newurl> # replace the first fetch URL
rtk git remote set-url <name> <newurl> <oldurl-regex> # replace only the URL matching regex
rtk git remote set-url --push <name> <url> # change push URL only (must point at same repo)
rtk git remote set-url --add <name> <url> # add an extra push URL (push to multiple remotes)
rtk git remote set-url --delete <name> <regex> # remove URLs matching regex
git remote set-url <name> <newurl> # replace the first fetch URL
git remote set-url <name> <newurl> <oldurl-regex> # replace only the URL matching regex
git remote set-url --push <name> <url> # change push URL only (must point at same repo)
git remote set-url --add <name> <url> # add an extra push URL (push to multiple remotes)
git remote set-url --delete <name> <regex> # remove URLs matching regex
```
`--push` changes only where pushes go — fetch and push URLs must still reference the same repository. For genuine fetch-from-A / push-to-B workflows, use two separate named remotes instead; `--push` cannot do this.

View File

@@ -9,7 +9,6 @@ description: >
Not the superproject's own remotes -> `git-remotes`.
metadata:
version: "1.0.0"
category: git
source_keys:
- git-scm-submodule-docs

View File

@@ -8,7 +8,6 @@ description: >
agent caller -> `git-orchestrate`. Not Gitea -> `gitea-workflow`.
metadata:
version: "1.0.0"
category: git
source_keys:
- nvie-gitflow-post

View File

@@ -8,7 +8,6 @@ description: >
Not interactive multi-step git guidance -> `git-workflow`.
metadata:
version: "1.0.1"
category: git
source_keys:
- git-scm-worktree-docs
@@ -17,7 +16,7 @@ metadata:
## Gotchas
- **A branch can be checked out in only one worktree at a time.** `git worktree add` on an already-checked-out branch fails; `--force` is the only override, so use it only deliberately.
- **Never `rm -rf` a worktree directory.** That strands metadata in `$GIT_DIR/worktrees/`. Use `rtk git worktree remove`, or `rtk git worktree prune` afterwards.
- **Never `rm -rf` a worktree directory.** That strands metadata in `$GIT_DIR/worktrees/`. Use `git worktree remove`, or `git worktree prune` afterwards.
- **Submodules break worktree support.** A worktree containing submodules cannot be moved at all, and needs `--force` to remove.
- **`extensions.worktreeConfig = true` is a one-way door.** Without it, `git config --worktree` errors; with it, that flag writes to the worktree's own `config.worktree` file, and `core.bare`/`core.worktree` are forced there too. It also breaks older Git. Leave it off unless per-worktree config is needed.
@@ -25,19 +24,19 @@ metadata:
| Operation | Run |
|---|---|
| Create on a branch that already exists locally | `rtk git worktree add <path> <branch>` |
| Create on a new branch | `rtk git worktree add -b <branch> <path>` |
| Create on the branch named after the path basename | `rtk git worktree add <path>` — checks that branch out if it exists, else creates it from HEAD |
| Create and reset an existing branch to HEAD — discards its commits | `rtk git worktree add -B <branch> <path>` |
| Create a local branch tracking a remote one | `rtk git worktree add --track -b <branch> <path> <remote>/<branch>` — always correct. `git worktree add <path> <branch>` expands to exactly this, but **only** under the conditions in `references/worktrees.md` |
| Throwaway experiment, no branch | `rtk git worktree add -d <path>` — detached HEAD |
| Create on a branch that already exists locally | `git worktree add <path> <branch>` |
| Create on a new branch | `git worktree add -b <branch> <path>` |
| Create on the branch named after the path basename | `git worktree add <path>` — checks that branch out if it exists, else creates it from HEAD |
| Create and reset an existing branch to HEAD — discards its commits | `git worktree add -B <branch> <path>` |
| Create a local branch tracking a remote one | `git worktree add --track -b <branch> <path> <remote>/<branch>` — always correct. `git worktree add <path> <branch>` expands to exactly this, but **only** under the conditions in `references/worktrees.md` |
| Throwaway experiment, no branch | `git worktree add -d <path>` — detached HEAD |
| **Never** `git worktree add <path> <remote>/<branch>` | That ref resolves, so the shortcut never fires and you get **a detached HEAD, no branch, no upstream**. Commits there go unreachable once HEAD moves, and `git push` needs an explicit refspec. Use the tracking row above |
| List | `git worktree list -v` to read, or `git worktree list --porcelain -z` to parse — both bare per ADR-0023: rtk re-renders the output and drops the porcelain flags |
| Lock or unlock | `rtk git worktree lock [--reason <str>] <path>` / `rtk git worktree unlock <path>` |
| Move | `rtk git worktree move <from> <to>` |
| Remove | `rtk git worktree remove <path>` |
| Prune stale metadata | `rtk git worktree prune --dry-run`, then without the flag |
| Repair after a manual move | `rtk git worktree repair` — in the main worktree if *it* moved, or inside a linked worktree that moved. `rtk git worktree repair <path>...` — from any worktree, naming each moved linked worktree's new path |
| List | `git worktree list -v`, or `--porcelain -z` to parse |
| Lock or unlock | `git worktree lock [--reason <str>] <path>` / `git worktree unlock <path>` |
| Move | `git worktree move <from> <to>` |
| Remove | `git worktree remove <path>` |
| Prune stale metadata | `git worktree prune --dry-run`, then without the flag |
| Repair after a manual move | `git worktree repair` — in the main worktree if *it* moved, or inside a linked worktree that moved. `git worktree repair <path>...` — from any worktree, naming each moved linked worktree's new path |
If the operation needs anything the table does not carry — the full `add` flag
table, orphan branches, sparse-checkout, locking for removable media, remote
@@ -49,7 +48,7 @@ Gates:
- **`move`, `remove` — the main worktree cannot be moved or removed.** Only linked worktrees, the ones `git worktree add` created, are candidates.
- **`add`, `move`, `remove` — escalate force flags one step at a time.** `-f` overrides a safeguard such as an unclean tree; `move` and `remove` need `-ff` on top of that when the worktree is locked. Confirm with the user before either — both discard state.
- **`add` — lock at creation, not after.** `rtk git worktree add --lock` is atomic, where add-then-`lock` leaves a window in which the worktree is unprotected.
- **`add` — lock at creation, not after.** `git worktree add --lock` is atomic, where add-then-`lock` leaves a window in which the worktree is unprotected.
## Step 2 — Report
@@ -62,7 +61,6 @@ worktrees:
lock_reason: <reason or empty>
```
Derive those fields from `git worktree list --porcelain -z` — bare, not `rtk`:
rtk drops both flags and never emits `locked`/`lock_reason` (ADR-0023). For a single
Derive those fields from `git worktree list --porcelain -z`. For a single
operation, report its outcome instead — `created: true`, `moved: true`,
`removed: true`.

View File

@@ -23,17 +23,17 @@ that are usable.
## `add` forms
```bash
rtk git worktree add <path> <branch> # <branch> exists locally: check it out — non-destructive
rtk git worktree add -b <branch> <path> # create a new branch; fails if it exists
rtk git worktree add <path> # branch named after $(basename <path>): checked out
git worktree add <path> <branch> # <branch> exists locally: check it out — non-destructive
git worktree add -b <branch> <path> # create a new branch; fails if it exists
git worktree add <path> # branch named after $(basename <path>): checked out
# if it exists, else created from HEAD
rtk git worktree add -B <branch> <path> # create the branch, or reset an existing one to HEAD,
git worktree add -B <branch> <path> # create the branch, or reset an existing one to HEAD,
# discarding the commits it carried
rtk git worktree add --track -b <branch> <path> <remote>/<branch>
git worktree add --track -b <branch> <path> <remote>/<branch>
# new local branch tracking the remote — always works
rtk git worktree add <path> <branch> # <branch> absent locally and in exactly one remote:
git worktree add <path> <branch> # <branch> absent locally and in exactly one remote:
# Git expands this to the --track -b form above
rtk git worktree add -d <path> # detached HEAD, no branch
git worktree add -d <path> # detached HEAD, no branch
```
The same `git worktree add <path> <branch>` spelling appears twice above and does two
@@ -65,19 +65,19 @@ Using `-` as `<commit-ish>` is shorthand for `@{-1}` (the branch checked out bef
## New unborn branch
```bash
rtk git worktree add --orphan -b <branch> <path>
git worktree add --orphan -b <branch> <path>
```
Creates an empty branch with no commits. **`--orphan` needs Git 2.42 or later** — it was added
upstream in 2.42, and on 2.39.5 this fails with `error: unknown option 'orphan'` and exit 129.
Check `rtk git --version` before reaching for it.
Check `git --version` before reaching for it.
Fallback on older Git, verified on 2.39.5 — detach first, then orphan the linked worktree in place,
which leaves the main worktree on its own branch throughout:
```bash
rtk git worktree add -d <path> # linked worktree, detached HEAD
git worktree add -d <path> # linked worktree, detached HEAD
cd <path>
rtk git switch --orphan <branch> # unborn branch: empty index, empty working tree
git switch --orphan <branch> # unborn branch: empty index, empty working tree
```
`git worktree list` then shows the new worktree at `0000000 [<branch>]` until its first commit.
@@ -88,25 +88,25 @@ the disruption worktrees exist to avoid.
Suppress the initial checkout to configure sparse-checkout first:
```bash
rtk git worktree add --no-checkout ../sparse main
git worktree add --no-checkout ../sparse main
cd ../sparse
rtk git sparse-checkout init --cone
rtk git sparse-checkout set src/
rtk git checkout main
git sparse-checkout init --cone
git sparse-checkout set src/
git checkout main
```
## Worktree on removable media
```bash
rtk git worktree add --lock --reason "external SSD" <path> <branch>
rtk git worktree unlock <path> # when reconnected
git worktree add --lock --reason "external SSD" <path> <branch>
git worktree unlock <path> # when reconnected
```
## Remote-branch disambiguation
```bash
rtk git worktree add --track -b <branch> <path> <remote>/<branch> # explicit: no guessing at all
rtk git worktree add <path> <branch> # shortcut: needs one clear remote
git worktree add --track -b <branch> <path> <remote>/<branch> # explicit: no guessing at all
git worktree add <path> <branch> # shortcut: needs one clear remote
```
**The bare-name shortcut needs exactly one remote.** It fires only when `<branch>` is not found
locally, none of `-b`/`-B`/`--detach` were given, and a tracking branch of that name exists in
@@ -122,10 +122,10 @@ exactly one remote has it, and marks that branch as upstream. Its default comes
## Repair after a manual move
```bash
rtk git worktree repair # the MAIN worktree moved: run it there to reconnect every linked
git worktree repair # the MAIN worktree moved: run it there to reconnect every linked
# worktree back to the main worktree
rtk git worktree repair # a LINKED worktree moved: run it inside that recently-moved worktree
rtk git worktree repair <path>... # reconnect a specific linked worktree — runnable from any worktree,
git worktree repair # a LINKED worktree moved: run it inside that recently-moved worktree
git worktree repair <path>... # reconnect a specific linked worktree — runnable from any worktree,
# naming each moved tree's new path
```
@@ -133,10 +133,10 @@ Which form applies depends on what moved:
| What moved | Remedy |
|---|---|
| The main worktree (or bare repo) | `rtk git worktree repair` in the main worktree |
| One linked worktree | `rtk git worktree repair` inside that worktree |
| Several linked worktrees | `rtk git worktree repair <path>...` from any worktree, listing each new path |
| Both main and linked worktrees | `rtk git worktree repair <path>...` in the main worktree, naming each linked worktree's new path — this restores the connections in both directions |
| The main worktree (or bare repo) | `git worktree repair` in the main worktree |
| One linked worktree | `git worktree repair` inside that worktree |
| Several linked worktrees | `git worktree repair <path>...` from any worktree, listing each new path |
| Both main and linked worktrees | `git worktree repair <path>...` in the main worktree, naming each linked worktree's new path — this restores the connections in both directions |
Only the no-argument form is tied to the current directory. The `<path>...` form is not — it
reestablishes the connection to every path you name, run from any worktree.
@@ -157,20 +157,20 @@ reestablishes the connection to every path you name, run from any worktree.
untouched throughout:
```bash
rtk git worktree add -b emergency-fix ../temp main
git worktree add -b emergency-fix ../temp main
cd ../temp
# fix, then commit
rtk git commit -a -m "fix: critical production bug"
git commit -a -m "fix: critical production bug"
cd -
rtk git worktree remove ../temp
git worktree remove ../temp
```
**Review a PR branch alongside your own work** — both branches stay checked out, so there is no
context switch:
```bash
rtk git worktree add ../review-pr-123 origin/feature-xyz # detached HEAD — read-only review
rtk git worktree add --track -b feature-xyz ../review-pr-123 origin/feature-xyz # if you will commit
git worktree add ../review-pr-123 origin/feature-xyz # detached HEAD — read-only review
git worktree add --track -b feature-xyz ../review-pr-123 origin/feature-xyz # if you will commit
# open ../review-pr-123 in a second editor window or terminal
```

View File

@@ -6,7 +6,6 @@ description: >
shellcheck"). Not running, installing, or updating hooks -> `pc-run`.
allowed-tools: Bash Read Write Edit
metadata:
version: "1.0.0"
category: devtools
source_keys:
- context7-pre-commit-com

View File

@@ -15,7 +15,7 @@ flow's file is not needed here. `SKILL.md`'s three common gates still apply.
inferring them from the project's name or README:
```bash
rtk git ls-files | grep -oE '\.[a-z]+$' | sort | uniq -c | sort -rn
git ls-files | grep -oE '\.[a-z]+$' | sort | uniq -c | sort -rn
```
2. Read `references/hooks-by-language.md` and map the detected extensions to recommended hooks.

View File

@@ -17,7 +17,7 @@ Read the existing `.pre-commit-config.yaml` before editing. Note any stale `rev`
1. Run a shallow extension scan, so the addition is judged against the languages actually present:
```bash
rtk git ls-files | grep -oE '\.[a-z]+$' | sort | uniq -c | sort -rn
git ls-files | grep -oE '\.[a-z]+$' | sort | uniq -c | sort -rn
```
2. Read `references/hooks-by-language.md` for the correct repo URL, `rev` and recommended args

View File

@@ -8,7 +8,6 @@ description: >
compatibility: Requires pre-commit installed and available on PATH.
metadata:
version: "1.0.1"
category: devtools
source_keys:
- context7-pre-commit-com
@@ -19,9 +18,9 @@ allowed-tools: Bash Read
## Gotchas
- The `SKIP` env var takes exact hook `id` values, comma-separated with no spaces: `SKIP=check-yaml,gitleaks rtk git commit -m "msg"`. A space after a comma silently skips nothing instead of erroring.
- The `SKIP` env var takes exact hook `id` values, comma-separated with no spaces: `SKIP=check-yaml,gitleaks git commit -m "msg"`. A space after a comma silently skips nothing instead of erroring.
- Never bypass a failing hook with `git commit --no-verify` (or `-n`). Hooks are the automated QA gate, so a bypassed commit pushes the failure downstream where it costs more — diagnose it instead.
- `- files were modified by this hook` is not a bug. A fixer hook rewrote a staged file, so the staged snapshot is stale and the commit is blocked on purpose. Re-stage and re-run the same commit: `rtk git add -u && rtk git commit`. Do NOT reach for `pre-commit install -f` here — it overwrites `.git/hooks/` and has nothing to do with re-staging.
- `- files were modified by this hook` is not a bug. A fixer hook rewrote a staged file, so the staged snapshot is stale and the commit is blocked on purpose. Re-stage and re-run the same commit: `git add -u && git commit`. Do NOT reach for `pre-commit install -f` here — it overwrites `.git/hooks/` and has nothing to do with re-staging.
## Gate — `pre-commit clean`

View File

@@ -14,8 +14,8 @@ Cause: A fixer hook (e.g. `trailing-whitespace`, `end-of-file-fixer`, `pretty-fo
Fix: Re-stage and recommit.
```bash
rtk git add -u
rtk git commit -m "same message"
git add -u
git commit -m "same message"
```
Do NOT reach for `pre-commit install -f` here. That flag overwrites existing hook files in `.git/hooks/`; it has nothing to do with re-staging.

View File

@@ -1,6 +1,6 @@
{
"name": "git",
"version": "1.3.7",
"version": "1.3.6",
"description": "Skills and agents for working with a local Git clone over the git wire protocol, and for authoring and running the pre-commit hooks that guard it.",
"author": {
"name": "Defame1297",

View File

@@ -1,6 +1,6 @@
{
"name": "git",
"version": "1.3.7",
"version": "1.3.6",
"description": "Skills and agents for working with a local Git clone over the git wire protocol, and for authoring and running the pre-commit hooks that guard it.",
"author": {
"name": "Defame1297",

View File

@@ -1,48 +0,0 @@
# git
Skills and agents for working with a local Git clone over the git wire protocol, and for authoring and running the pre-commit hooks that guard it.
## Install
**Claude Code:**
```bash
claude plugin marketplace add <owner>/<repo>
claude plugin install git@<marketplace-name>
```
**GitHub Copilot CLI:**
```bash
copilot plugin marketplace add <owner>/<repo>
copilot plugin install git
```
**Local (development):**
```bash
# Claude Code
claude --plugin-dir ./plugins/git
# GitHub Copilot CLI
copilot plugin install ./plugins/git
```
## Conventions
Skills here run local git commands through the org's `rtk` wrapper. **When a command is prefixed, when it stays bare, and why some executable commands must stay bare are all decided by ADR-0023** (`docs/adr/0023-rtk-prefix-marks-executable-commands-only.md`), which is repo-wide and not specific to this plugin. `check-rtk-prefix` enforces the part of it that is machine-decidable.
Installing this plugin without `rtk`? Every prefixed command is a plain `git` invocation with a word in front of it — drop the `rtk ` and it is correct.
## Contents
| Component | Path | Description |
|---|---|---|
| Skills | `.apm/skills/` → `skills/` | `git-commits`, `git-branches`, `git-history`, `git-remotes`, `git-submodules`, `git-workflow`, `git-worktrees`, `pc-author`, `pc-run` |
| Agents | `.apm/agents/` → `agents/` | `git-orchestrate` |
`.apm/` is the authoring source; `skills/`/`agents/` are the generated mirrors plugin hosts scan (ADR-0017) — edit only under `.apm/`.
## Author
Defame1297

View File

@@ -1,5 +1,5 @@
name: git
version: 1.3.7
version: 1.3.6
description: Skills and agents for working with a local Git clone over the git wire protocol, and for authoring and running the pre-commit hooks that guard it.
author:
name: Defame1297

Some files were not shown because too many files have changed in this diff Show More