refactor(skills): retrofit the 39 skills to the ADR-0020 context contract #99
Reference in New Issue
Block a user
Delete Branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
ADR-0020 sets a blocking description/body contract for skills and agents. The gates ship hot with no baseline file, so any edit to a non-compliant skill is blocked at commit until that skill is retrofitted. This issue tracks doing it deliberately rather than lazily.
Non-compliant today
gitea-workflow1012,agentsmd-author960,agentsmd-audit944,gitea-labels-milestones835,provider-adapter-author833,gitea-issues827,apm-workflow817.---):git-remotes1217,diagnose1126,git-branches1124,git-commits1102,forge1093,git-history1044,git-submodules1011,gitea-files922,gitea-issues902.Eight skills fail both gates and need a description cut and a dispatch split in one commit — partial improvement does not clear the gate.
kyberforge is itself 3 of 7 non-compliant
The plugin that owns the contract retrofitted only its four author/audit skills. These remain:
forgeskill-authorhands clean-context recheck to it.apm-workflowskill-author/references/contract.mdwhile failing the gate.apm-installRetrofit
apm-workflowandforgeearly — one is the model other retrofits imitate, the other is the router.Per-skill work
Not <thing> → <skill-name>; move composition notes toREADME.md.gitea-filesandgitea-issuesare the worst.references/behind "read X when Y".Follow
skill-author/references/retrofit.md— it carries the ordered cut procedure, the definition of "mutually exclusive flows", reference-file conventions, a mandatory collateral checklist, and an inline before/after worked example. It exists because four dry-run retrofits each invented six to ten different answers without it.Also decide per skill whether it is hand-invoked (
disable-model-invocation: true) — candidates:grill-me,grill-with-docs,caveman,prototype.Traps
INFO boundary-target resolution DID NOT RUNand exits 0. That reads as a pass and is not one.Kyberforge.CompositionNotefires 10 times acrossgitea-issues,gitea-labels-milestones,gitea-prsandgitea-workflow.pre-commit run --all-filesis red on prose as well as size.## Gotchasover five entries,## Gotchasover 25% of body words.gitea-workflowneeds a content decision, not just a cut. Its boundary clause routes togit-workflowin thegitplugin. That resolves in this monorepo but not in a gitea-only install, and apm cannot express the dependency at plugin granularity without committing a ~192-file deployed tree. Agreed resolution: keep the exclusion, drop the explicit cross-plugin route. Fold it into that skill's retrofit.Success
Preload tax down from 21,005 chars (~5,250 tokens) to ~12,000 (~3,000), and
pre-commit run --all-filesgreen on bothskill-size-checkandvale-audit-prefilter-skill.Starting command:
pre-commit run skill-size-check --all-filesreports every violation with its measured value.Reference:
docs/adr/0020-skill-description-and-body-context-contract.md. Related: #100 (dangling routing targets), #101 (mergeskill-audit+agent-audit).The ADR-0020 gates and the kyberforge-side retrofit have landed on
refactor/trim-skills-agents-context, which changes the numbers in this issue's body.Corrected scope. The body says 30 descriptions and 14 bodies are non-compliant. Measured against the branch as it stands, it is 26 descriptions over the 400-char FAIL and 9 bodies over the 900-word FAIL. Two reasons for the difference: the original body count was taken whole-file rather than body-only (the gate measures body-only, so the correct pre-change figure was 13, not 14), and the four kyberforge author/audit skills have since been retrofitted in the same change.
Already done, so out of this issue's scope:
skill-authoragent-authoragent-auditskill-auditRemaining preload tax: 21,190 chars (~5,600 tokens), down from 23,612. The bulk of the remaining reduction is in the
gitea-*andgit-*families and incore.Useful starting command:
pre-commit run skill-size-check --all-filesreports every current violation with its measured value.Reference implementations to copy:
skill-authorandskill-auditare now the house examples of the dispatch pattern — body carries the dispatch table plus common gates, each branch self-contained inreferences/.apm-workflowremains the original exemplar.Known trap: the gates are hot with no baseline file, so any edit to a non-compliant skill requires retrofitting it first. Batch by plugin rather than fixing skills one at a time.
Body rewritten against measured ground truth. What changed since the comment above, and why:
Numbers that were wrong in that comment. It reported the remaining preload tax as 21,190 chars (~5,600 tokens) down from 23,612. Both figures were carried over rather than re-measured. Measured now: 21,005 chars (~5,250 tokens), down from 23,427 at the branch base. The 23,612 headline appeared in the ADR too and has been corrected there; it was not reproducible under the method the ADR itself documents (~4 chars/token), which is how it was caught.
The retrofit table was also a few chars out per row (
skill-author613 not 614,agent-author902 not 903,agent-audit862 not 863,skill-audit983 not 984) and its body figures have since moved —skill-authoris now 595 andskill-audit580, because both absorbed additional content during review and paid for it by cutting elsewhere rather than by borrowing headroom.apm-workflowis cited as the exemplar and fails the gate. The comment above recommends it as the pattern to copy without noting its description is 817 chars. That is now called out in the body, along withforge(the router, failing both gates) andapm-install. kyberforge retrofitted four of its seven skills and left three — worth knowing before you copy from one of them.New material in the body, none of which existed when this issue was written:
skill-author/references/retrofit.mdis now the procedure to follow. Four dry-run retrofits established that the previous instruction mandated a retrofit and supplied no method, so each agent invented six to ten decisions. One dry run tookgit-historyfrom 450 chars / 1,044 words to 189 / 247 with both validators green by following it literally.INFO boundary-target resolution DID NOT RUNand exits 0, which reads as a pass.Kyberforge.CompositionNoteis a second hot gate, red on fourgitea-*skills.gitea-workflowneeds a content decision about its cross-plugin boundary target, not just a length cut.The original body's "14 of 39 bodies" listed whole-file counts under a body-only heading — the precise conflation the ADR argues against. That is fixed rather than annotated, since this issue is the working document for the retrofit and the wrong numbers would be acted on.
Collateral for the
gitea-prsretrofit: three stale claims inSKILL.mdPR #106 (fixing #104) corrected
references/pull-requests.mdon whichpull_request_writeparameters each method actually honours. It deliberately stopped at the reference file, because touchingSKILL.mdpulls in this retrofit. That leavesSKILL.mdcontradicting the reference it points at, with the stale version in the file that is preloaded every session.Recording the three edits here so the retrofit does not land without them.
1.
SKILL.md:6— description advertises a capability that does not existThe description says the skill covers "updating title/body/reviewers".
editPullRequestFnnever readsreviewersorteam_reviewers; onlyadd_reviewers/remove_reviewersdo. This is in the always-loaded description, so it is both a factual error and preload tax — it should disappear during the 709 → ~250 char rewrite anyway. Just don't reintroduce "reviewers" as anupdatecapability.2.
SKILL.md:41— the instruction that walks an agent into the bugNo method qualifier. An agent creating a PR from
SKILL.mdalone follows this, resolves a milestone ID, passes it on"create", and the server silently discards it — no error, and the response omits the key, so the drop is indistinguishable from never having passed it.labelsdoes apply on create, so a caller who verifies the labels landed will reasonably assume the milestone did too.Needs a clause:
milestoneis honoured on"update"only. The dispatch table'sCreate a PRrow (SKILL.md:55) carries no warning either.3.
SKILL.md:32— superseded draft workaroundStill works, but unnecessary:
applyDraftPrefixmeans"update"withdraft: falseand notitlemakes the server fetch the stored title and strip the prefix itself. Also strips[WIP], case-insensitively.Gate status for this skill
Measured on
plugins/gitea/.apm/skills/gitea-prs/SKILL.md:skill-size-check: ERROR — description 709 chars (over the 400 ceiling). Body is 683 words, a SUGGESTION only, under the 900 hard fail — so this skill needs a description rewrite, not a dispatch split.vale-audit-prefilter-skill: 2 errors —Kyberforge.CompositionNoteat 4:310 (Composes) and 4:438 (rather than duplicating), both inside the description, both removed by the same rewrite.So the blocking work here is one description rewrite; the three corrections above ride along with it. Cheaper than most entries on this issue — worth taking early, since the #2 gap is live and reachable by any agent that dispatches on
SKILL.mdwithout opening the reference.Verified against
gitea-mcpv1.6.0 source (operation/pull/pull.go), the versionget_gitea_mcp_server_versionreports as running. Independently confirmed in review of #106.Folded in from #100 (closed): two dangling routing targets, both hostage to this retrofit
#100 tracked four skill descriptions whose boundary clauses named targets that do not resolve. Two are already fixed on
mainas forced consequences of earlier work (skill-audit→/skill-improve;agent-author→ "examine agent files manually"). The remaining two are not separable bugs — both files are currentskill-size-checkFAILs on description length, and the hook isstages: ['pre-commit']withpass_filenames: true, so it fires on exactly the files you touch. There is no edit to either file that does not pull the retrofit in. Closing #100 and recording the residue here.1.
gitea-issues→gitea-labels— no decision needed, the retrofit deletes itMeasured on
plugins/gitea/.apm/skills/gitea-issues/SKILL.md: description 827 chars (ERROR), body 902 words (ERROR, 2 over the ceiling), Gotchas 391/902 = 43%, 7 entries.The dangling target is a YAML fold artifact. The description contains:
The
>-folded scalar joins those lines intogitea-labels- milestones, leavinggitea-labels-terminal and therefore danglable. The checker reports it as an unresolvable target namedgitea-labels.This needs no separate fix. The sentence is a composition note — exactly what step 2 of this issue says to move to
README.md, and exactly whatKyberforge.CompositionNotealready flags. Cut it during the 827 → ~250 rewrite and the dangling target goes with it. Do not "fix" it by re-hyphenatinggitea-labels-milestonesinline; that preserves a composition note the prose gate rejects.Note this skill fails both size gates, so it needs a description cut and a dispatch split in one commit — it is one of the eight in this issue's body that cannot be partially improved.
2.
research→neuledge-context— needs a content decisionMeasured on
plugins/bin/.apm/skills/research/SKILL.md: description 583 chars (ERROR), body 854 words (SUGGESTION only, under the 900 hard fail). So this one is a description rewrite, not a dispatch split.The clause reads:
neuledge-contextis not a phantom — it is a corpse. Commit6146120(2026-06-23, "chore: remove neuledge-context skill and multiple kyberforge skills") deleted.agents/skills/neuledge-context/—SKILL.md,META.md, and threereferences/files — alongsidemarketplace-architect,plugin-create,promptfooandwrite-agent. The description was never updated to follow. No skill in the repo has owned MCP-server installation since.Three options, with the recommendation:
apm-install, which explicitly scopes itself to the apm CLI and agent runtimes, not MCP servers. The gate would pass and the route would be a lie — strictly worse than a dangle, because it is undetectable.Whichever is chosen, do not compress this boundary to
Not MCP setup → neuledge-contextper step 2 of this issue. That preserves the dead target in compressed form. The gate would catch it, but it wastes a retrofit cycle.Collateral
The test pins are self-documenting — both files carry inline instructions to update together, so they will not be silently lost, but they must land in the same commit as the retrofit or the suite goes red:
tests/test-adr0020-targets.sh:424—EXPECTED_DANGLINGhard-codes exactly{gitea-labels, neuledge-context}. Update it; do not delete the assertion. An empty expected set is valid and still pins that no new dangling target appeared.tests/test-skill-size-check.sh:557— a two-element probe loop asserting each target is individually detected. Drop the probe whose fixture was retrofitted. The file's own comment explains why apass "SKIP: …"branch is not acceptable here: it is an assertion-free result counted in the totals, which makes the suite look one test stronger than it is.The one item no gate and no test covers:
plugins/bin/evals/research/research/eval.yaml:30— eval casenegative-neuledge, "Negative — MCP server setup goes to neuledge-context", query"Install the neuledge context server and set it up",should_trigger: false. It asserts a route that no longer exists. Under option 1 it should be deleted; leaving it makes the eval pass for the wrong reason (the skill does not trigger because the query is unrelated, not because it routes elsewhere). Under option 2, rename it so it no longer claims a destination.Classification note
#100 carried
Kind/Bug; this issue isKind/Enhancement. Two live routing defects are no longer visible to a bug query. Accepted deliberately — both are detected automatically by the resolvable-target check, sopre-commit run skill-size-check --all-filesremains the authority on whether they still exist.Wave 1 complete — the
giteaplugin (7 skills). Handoff for waves 2–4.Branch
refactor/adr0020-skill-retrofit, 13 commits, not pushed. Clean tree.Measured state
research→neuledge-context)Kyberforge.CompositionNotetests/run-tests.sh --strictNote the issue body's 21,005 baseline re-measured as 21,033 — 28 chars of since-landed drift, not a discrepancy worth chasing.
Per-skill result
gitea-prsgitea-issuesgitea-releasesgitea-labels-milestonesgitea-filesgitea-branchesgitea-workflowA 70% cut on the family the ADR predicted would sit at the FAIL tier permanently. Four of seven landed under the 250 SUGGESTION target.
The way of working — validated, and worth repeating verbatim
Orchestrator → parallel author agents (one per skill) → parallel clean-context audit agents (one per skill) → orchestrator triages and re-dispatches. This is
forgeStep 3's two-tier verification pattern, borrowed rather than invoked (forge's own description forbids using it when the artifact type is already known, and its Step 1 would recursively callgrill-with-docs).Author agent contract — every constraint here was load-bearing:
skill-size-checkand Vale lines as the improvement signal.skill-author's improve flow (references/improve.mdStep 1) stops dead without one.skill-author→ improve flow →references/retrofit.md, then/skill-auditinline in its own context..git/index. This deliberately overridesskill-authorStep 4's commit-verification; the orchestrator commits one per skill.skill-author/SKILL.md:23names worktree teardown as a live hazard to uncommitted drafts.INFO boundary-target resolution DID NOT RUNand exits 0.README.md(28 exist, one per skill), never the plugin-root one.Exemplars named in every prompt:
skill-author(225/599),agent-author(224/616),skill-audit(239/581),agent-audit(250/691).Cap: 3 rounds, then revert and park for a human decision.
Decisions taken
cavemanonly fordisable-model-invocation— see the finding below.research: delete theneuledge-contextclause outright; delete eval casenegative-neuledgeatplugins/bin/evals/research/research/eval.yaml:30with it. (still to do, wave 3)gitea-workflow: exclusion kept, cross-plugin route togit-workflowdropped. Done.What the second audit layer was actually worth
Every skill passed both deterministic gates before its first audit. Five of seven then failed on auditor-tier defects. Not size — the gates were green for all of them:
gitea-issues: a label-application capability with no dispatch row, whilegitea-labels-milestonesroutes label application to it and holds noissue_write. A closed loop with no executable branch.gitea-labels-milestones: the org-label fallback named anorgparameter the skill never derived, so the documented procedure stalled. Two rounds each fixed one half.gitea-issues: one Gotcha rewritten three times — each rewrite correct on the branch it targeted and wrong on another.gitea-workflow: the description's grammar orphaned a third of the skill's behaviour; a round-2 fix relocated a directive and left its fact behind, creating a fresh restatement FAIL.This is direct evidence for the ADR's own claim that the deterministic tier is a backstop, not the mechanism. Budget for it: roughly 2 audit agents per skill, and expect a second round on the majority.
Audits going beyond brief paid off too — one queried the live gitea-mcp v1.7.0 server and found
references/reviews.mdforbiddingreview_comments(the field that works) in favour ofreview_scomments(which no longer exists), plus three undocumentedpull_request_review_writemethods. Landed as0079f35.Findings that changed scope
disable-model-invocation: truehard-blocks the Skill tool — tested, not inferred:ADR-0020 verified only the listing-visibility and slash directions. No other skill can route to a hand-invoked skill. Of this issue's four named candidates, three have inbound routes:
prototype←diagnose;grill-me←write-docs,skill-author/references/create.md;grill-with-docs←improve-codebase-architecture,triage,write-docs,forgeStep 1 (mandatory). Onlycavemanhas none. Recorded inCONTEXT.md(ef3e981).Three gate defects filed: #107 (resolver checks only the first target per arrow clause — confirmed 4×, once by executing the extracted resolver), #108 (boundary check fires on hand-invoked skills), #109 (
body-discipline.mdcontradicts the ADR's cited exemplar).Remaining work
git(9):git-commitsgit-branchesgit-worktreesgit-remotesgit-workflowgit-submodulesgit-historypc-runpc-authorbin/core/lint(8):researchprototypediagnose·agentsmd-authoragentsmd-auditprovider-adapter-author·vale-configvale-runkyberforge(3):apm-workflowforgeapm-installcavemanflag ·researcheval case ·EXPECTED_DANGLING→ empty set onceresearchlands · final mirror sync · full gate +--strictrundiagnoseis a 27th skill in scope not visible in the "26 descriptions" figure — its description is compliant, its body is 1,126 words.Traps confirmed the hard way
scripts/sync-plugin-content.sh --allonce per wave, at the end.sync_dirdoesrm -rfbefore every copy; running it while agents edit the same plugin races them. Agents must be told not to sync.tests/test-adr0020-targets.shEXPECTED_DANGLINGis now{neuledge-context}; both probe loops carry ashellcheck disable=SC2043waiver because one entry is the expected steady state.AGENTS.md:39carries live gate counts and goes stale every wave. Updated to 19/7 inb8c36c3; update it again as waves land..apm/edit is not live until pushed. Filing these issues loaded the pre-retrofitgitea-issuesfrom.claude/skills/.Sequencing note
Resolve #109 before wave 4.
apm-workflowis both the exemplar in that contradiction and an unretrofitted target — retrofit it first and the agent must guess which convention to follow, and its guess becomes the new de-facto exemplar.gitea-workflowcurrently carries the duplication knowingly, pending that decision.Wave 2 complete — the
gitplugin, 9 skillsBranch
refactor/adr0020-skill-retrofit, 24 commits, clean tree, nothing pushed.Measured state
research→neuledge-context)Kyberforge.CompositionNotetests/run-tests.sh --strictThe
gitfamily went 5,057 → 2,258 chars of description, a 55% cut. Corpus preload is down 31% from the start.Per skill
git-commitsf3b4860git-branches0fde892git-worktrees3dd5387git-remotes38eb074git-workflow3c74bebgit-submodules261e5b5git-history7cb8e95pc-run15ff741pc-author3cd3f33Three deliberate SUGGESTION-tier overages, all justified in their commit messages:
git-branches273 andgit-history266 (each the price of keeping a capability reachable by natural language), andgit-remotesGotchas at 27% of body (the ratio rose purely because the denominator shrank; padding the body to clear it would game the metric).What the two-layer loop bought
All 9 passed the deterministic gates before their first audit. 8 of 9 then failed the clean-context audit — 16 must-fix findings. Six shared one shape: a safety gate surviving redistribution as text while losing its binding force. That is now the characteristic defect of this retrofit, and it is invisible to every script.
The three worth carrying forward:
git-worktreeshad no dispatch row for a worktree on an existing local branch. That request fell to the adjacent-Brow, which resets the branch to HEAD and discards its commits. The correct non-destructive command was one row away. This is the cost of the dispatch-table-as-flow shape: because the rows are the flows, a missing row is a missing capability.git-commitsmoved the secret scan into the create flow only. Dispatch loads exactly one flow file, so the amend/squash path could commit a credential with no check in its loaded context — a grep forsecretreturned one hit on a path two of three branches never reach.pc-runadvertised diagnosing why a hook "never fires" but routed only from the failure path. When hooks never fire the manual run succeeds, so the request fell to the ambiguity default, reported green, and masked the cause.Two author judgment calls were overturned by auditors using measurement rather than assertion (
pc-run's "flows too small to extract" — the house already ships ~100-word branch files;git-remotes' weakest boundary target). One author claimed content was "already present verbatim" in a reference when the same pass had just added it there — outcome correct, reasoning not. Verify before deleting, not after.Systematic hazard for waves 3–4
The Gotchas-ratio SUGGESTION fired three times purely from the denominator shrinking. Clearing two ERRORs routinely hands you a new SUGGESTION on a section you never touched. Two authors caught it mid-flight and rebalanced by relocating a branch-specific Gotcha to its point of use; one correctly declined to pad. Expect it, and do not resolve it by deleting a Gotcha that earns its place.
Collateral fixed this wave
skill-audit's ownreferences/body-discipline.mdused pre-retrofitgit-commitsas its worked negative example. Every figure was correct for v0.1.2 and none survived into v0.1.3 — so every wave-3 auditor would have read a rubric contradicted by the file it names. Worse, its row four called the secrets Gotcha a paraphrase FAIL, which is the exact reasoning that produced thegit-commitscredential hole.Fixed in
a2ebdaf: the example is pinned to commit5e23250and marked not to be refreshed againstHEAD, and 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; relocate a multi-branch safety gate into the body rather than dropping it. A sweep of both audit skills found no other stale example (apm-workflow,git-orchestrate,gitea-orchestrate,apm-orchestratecitations all verified exact).Issues filed from this wave
skill-size-check'sBOUNDARY_ARROWregex cannot cross a., so a boundary clause naming a dotted filename between "Not" and the arrow is invisible and misreported as missing.validate-provenance.shhas no way to declare a reference file house-authored;source_keys: []parses identically to a missing key, so the only alternatives to a permanent INFO are false provenance claims.git-branchesdelegates togit-history;git-commitsclaims it and ownsreferences/cherry-pick.md;git-historyexecutes it). Pre-existing, held out of scope. The rebase half of the same tangle was resolved in0fde892.rtk gitvs baregitis inconsistent across the plugin. Pre-existing; needs one normalising pass, not nine per-skill calls.Earlier gate defects remain open: #107, #108, #109.
Remaining work
Wave 3 —
bin/core/lint(8):research(583),prototype(426),diagnose(body 1126 only),agentsmd-author(960),agentsmd-audit(944),provider-adapter-author(833),vale-config(657),vale-run(654)Wave 4 —
kyberforge(3):apm-workflow(817),forge(648/1093),apm-install(514)Sequencing: resolve #109 before wave 4 —
apm-workflowis both the exemplar in that contradiction and an unretrofitted target.Orchestrator close-out: set
disable-model-invocation: trueoncaveman; delete eval casenegative-neuledgeatplugins/bin/evals/research/research/eval.yaml:30; reduceEXPECTED_DANGLINGto empty and drop the last probe from bothtests/test-adr0020-targets.shandtests/test-skill-size-check.shonceresearchlands; finalscripts/sync-plugin-content.sh --all;pre-commit run --all-files;bash tests/run-tests.sh --strict;pre-commit run --hook-stage pre-push --all-files(prints 16); update theAGENTS.mdgate counts again (currently "10 of 39 / 2 of 39", corrected inafbeaff).The agent contract — reuse verbatim
Author agents. Hand each its own measured
skill-size-checkand Vale lines —skill-author's improve flow (references/improve.mdStep 1) stops dead without an improvement signal. Then: invokeskill-author→ improve flow →references/retrofit.md(the ordered cut procedure) → run/skill-auditinline, in its own context.Audit agents. Fresh, not forked, one per skill. Tell them the deterministic gates already pass and their value is in what a script cannot check: behaviour loss, dispatch correctness, Gotcha paraphrase, description grammar as rendered, boundary-target quality, internal consistency. Give each the specific judgment call the author hedged on.
Repair agents. Fresh again, handed the audit findings split into must-fix and judgment call, with the reasoning — not just the diffs.
Hard constraints in every prompt:
.git/index. The orchestrator commits. This deliberately overridesskill-authorStep 4's commit-verification requirement.skill-author/SKILL.md:23names worktree teardown as a live hazard to uncommitted drafts.forgeStep 3.scripts/sync-plugin-content.sh—sync_dirrunsrm -rfbefore every copy and will delete directories other agents are writing. Orchestrator syncs once at wave end.INFO boundary-target resolution DID NOT RUNand exits 0 — reads as a pass, is not one.Content rules: one arrow per boundary target; never let a hyphenated skill name straddle a line in a
>-folded scalar; composition notes go in the skill's ownREADME.md. Anyreferences/file added, removed, or renamed requires updating the skillREADME.mdfile table,references/README.md, andreferences/sources.md"Contributing files".Exemplars:
skill-author(225/599),agent-author(224/616),skill-audit(239/581),agent-audit(250/691).Budget: ~1 audit agent per skill, and expect a repair round on most. Wave 2 ran 9 + 9 + 8 = 26 agents for 9 skills.
Accidental benefit worth keeping
Because the flat mirror is synced only at wave end, it still holds the pre-retrofit content while the audits run. Three auditors used it to diff real before/after rather than inferring what was lost — which is how the
git-submodulesregressions and thegit-remotesredistribution audit were both settled on evidence. Keep syncing at wave end, not per skill.Wave 3 complete —
bin/core/lint, 8 skillsBranch
refactor/adr0020-skill-retrofit, 34 commits, clean tree, nothing pushed.Measured state
Kyberforge.CompositionNotetests/run-tests.sh --strictpre-commit run --hook-stage pre-push --all-filesThe preload tax is already under the 12,000-char success criterion, with wave 4 still outstanding — the three remaining FAILs are 1,979 chars between them and should land near 750.
Every remaining ERROR is in
kyberforge:apm-workflow(817),forge(648 + 1,093-word body),apm-install(514). That is wave 4 exactly.Per skill
agentsmd-authore42c055agentsmd-auditf0526b3provider-adapter-authorc59e4bfvale-config45cd260vale-runff187efresearchee6b040prototypef03bfa8diagnose00c1e6bPlus
7e80c09(resolves #109 and its collateral ingitea-workflow) andd8dfba9(AGENTS.md counts).What the two-layer loop bought, again
8 of 8 skills passed every deterministic gate and then failed the clean-context audit, on roughly 20 must-fix findings. Wave 2 was 8 of 9. The ratio is not improving, which is the point: the audit layer is not catching sloppiness, it is catching a defect class the scripts structurally cannot see.
Wave 2's characteristic defect repeated exactly — a rule surviving redistribution as text while losing its binding force — but wave 3 sharpened it into two distinguishable shapes:
agentsmd-author's provider-file prohibition became a justification clause on the false branch of a conditional, so the common path never read a sentence binding it. WithWriteandEditgranted and the bundled validator's own remediation text saying "Move provider-agnostic content into AGENTS.md", a size FAIL actively invited the prohibited edit.diagnoseput its loop-quality bar behind "if you do not already have a signal" — inverted, so the most common entry skipped it.vale-run's CI-failure answer moved into a file no CI-entered invocation loads.Three findings were worse than quality defects:
researchsilently broke the provenance chain. Thereferences/file-format.mdpointer was dropped at the point of use, andvalidate-provenance.shshort-circuits when theStatusfield is absent —rd_status != "\extracted`"→continue, reporting nothing. Asources.md` written from the retrofitted step 6 was unparseable and check 8 passed anyway.vale-configshipped a false statement. Gotcha 1's kernel lost the qualifier that only package styles need fetching, contradicting the same file twice. An agent adding a custom style would have added a spuriousPackagesentry and brokenvale syncoutright.provider-adapter-authorasserted behaviour its validator does not have — see #115.On scoping: two of my own briefs were wrong
Worth recording, because both were orchestrator errors that the agents surfaced rather than absorbed.
diagnose. I scoped the description out while mandating a boundary clause. Adding one to a 290-char description cannot land under 400 — it landed at 379, twenty-one characters from a hard FAIL. The auditor also showed the capability chain was inaccurate, naming "minimise" as a phase that does not exist while omitting the phase the body itself calls "This is the skill". Final: 220.provider-adapter-author. I claimed a false statement had propagated intoreferences/provider-matrix.md. The repair agent checked, found it had not, and declined to manufacture an edit to satisfy the instruction. Correct call.A third correction went the other way:
agentsmd-audit's author dropped a governance trigger against a measured budget of "~9 spare chars". The real cost was ~49 — and 250 is the SUGGESTION tier, not a ceiling. The trade was never "restore it or bust the budget."The
.claude/skills/A/B went vacuous the moment the corpus got healthytests/test-adr0020-targets.shasserts that a deployed tree does not leak into the resolver's universe, and built its bait by deriving it from the live dangling set. Fixing the last dangling target left nothing to deploy, and the test said so out loud rather than passing — it had been written with that failure message in place.Rebuilt on an explicit synthetic target, so the A/B distinguishes something on every run regardless of corpus health. The old form depended on the corpus staying broken to prove anything.
Both probe loops are removed, not emptied — a loop over an empty list is an assertion-free result counted in the totals, which is the vacuous-pass shape those files explicitly reject.
Issues filed this wave
prototype'sLOGIC.md/UI.mdsit at the skill root instead ofreferences/; held out of the wave deliberately.validate-adapter.sh's--no-import-syntaxis a no-op. Both branches reduce to the same expression, so the flag defends nothing and omitting it costs nothing.researchmandated subagent fan-out itsallowed-toolsnever granted. Resolved conservatively (serial), but the real question is whether fan-out belongs to an agent rather than a skill.#109's premise was half wrong
There is no Vale conflict.
PaddingPhrase.ymlmatches onlysee references?/? for (more )?(info|information|details)and never fired onapm-workflow's closing line. The contradiction was purely the prose rule that LLM auditors apply literally. One file changed, no Vale rule touched.Relatedly, one agent hypothesised that the Vale prefilter never fires on folded-scalar descriptions and is a vacuous green. Tested and false — bare
valedoes miss them, butvale-wrap.shexists to fix exactly that and flattens the scalar in a scratch copy first. Planted violation: barevale0 errors, wrapper 1 error. The gate is real.Contract additions for wave 4
Everything from the wave-2 contract still holds. Four things to add:
pre-commit run --filesshells out to git and can stash — it races parallel agents on.git/indexjust asgititself does. One wave-3 agent noticed and ranvalidate.sh/validate-provenance.sh/vale-wrap.shdirectly instead. Put this in every prompt; only one of eight worked it out unprompted.yaml.safe_load+re.findall(r'[a-z]- [a-z]', desc)reported it; none found a break. Cheap, and it retires thegitea-labels- milestonesfailure mode by construction.gotcha_stats()slices from after the## Gotchasheading. A naive count includes the heading's two words and reads ~0.6pp tighter than the gate is.Wave 4 —
kyberforge(3)apm-workflow(817),forge(648 / 1,093),apm-install(514 / Gotchas 47%).#109 is resolved, so
apm-workflowno longer has to guess which convention to follow — that was the sequencing reason to do it first, and it is done.Note
forgeandapm-workfloware both used by this retrofit process. Retrofitting the tooling that runs the retrofit is the one place worth pausing before dispatching.Orchestrator close-out still outstanding
caveman— setdisable-model-invocation: true(zero inbound routes; still shows a 263-char description and no boundary clause)skill-size-check.sh:458-460— theARROW_BOUNDARYcarve-out is live (sole extractor for three real targets in kyberforge's audit skills) but its stated justification is retired, sincediagnose's process chain is gone. Update the comment; do not delete the branch.scripts/sync-plugin-content.sh --all,pre-commit run --all-files,bash tests/run-tests.sh --strict,pre-commit run --hook-stage pre-push --all-filesAGENTS.mdcounts once moreWave 4 pre-flight — two findings that change the brief
1.
apm-workflowis cited by hard numbers in live guidance, and wave 4 invalidates all of themEvery figure below is exact as of now — verified, not recalled:
skill-audit/references/body-discipline.md:83apm-workflow: a 421-word body dispatching to roughly 3,000 words… whole-file count is 554"skill-author/references/contract.md:123apm-workflowskill — a 421-word body dispatching to 3,006 words of references"Wave 4 breaks both. The description goes 817 → ~250, which moves the whole-file count directly; and the Gotchas ratio (253/421 = 60%, the worst in the corpus) can only be fixed by moving content out of the body, which moves the 421.
This is the same rot this retrofit already hit once —
body-discipline.md's worked negative example cited agit-commitsthat the wave-2 retrofit had rewritten, and every figure in it was correct for a version that no longer existed. Fixed ina2ebdafby pinning to a commit SHA.The fix here is different, and the difference matters. The
git-commitscase was a negative example: it had to stay pinned to the broken snapshot, because the whole point was the defect.apm-workflowis a positive exemplar — and after retrofit it will be a better one, not a worse one. So these two citations must be refreshed to the new measured figures, not pinned to a stale snapshot.Mandatory collateral for the
apm-workflowretrofit: re-measure body words, whole-file words, and thereferences/total, then update both citations in the same commit. Measure; do not estimate.2. ADR-0020 needs no action — and must not be touched
ADR-0020 also cites
apm-workflowby number (:75) and by line range (:168citesSKILL.md:33-41), and:48uses its description as a defect example ("re-quoting the same six as user phrasings") that the retrofit will make false.None of that needs fixing, because the ADR pins itself in its own opening paragraph:
An ADR records evidence as it stood when the decision was taken. Tell the wave-4 agents explicitly not to "helpfully" update it — refreshing an ADR's figures to match later reality destroys the record of why the decision was made. This is the one file in the blast radius where the correct action is to leave stale-looking numbers alone.
3. The "retrofitting the tooling that runs the retrofit" concern was overstated
I flagged this at the end of wave 3. Checked, and it does not hold:
skill-authorandskill-auditonly mention wave-4 skills in prose — a hazard note about/forge's outer loop (skill-author/SKILL.md:23), a/apm-workflow configurepointer in the create flow (references/create.md:42, which retrofits do not take), and the exemplar citation above. No wave-4 skill is invoked by the retrofit loop. Wave 4 can be dispatched exactly like waves 2 and 3.Also done in this pass
scripts/skill-size-check.sh's bare-arrow carve-out comment justified itself withdiagnose'sfix -> regression-testprocess chain — which wave 3 cut. The gate is now unexercised corpus-wide. The comment is corrected rather than the branch deleted: it guards prose no one has written yet, 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.Wave 4 complete — kyberforge, and the corpus is clean
apm-workflow1e4aab5forge4aab9d3apm-installaa982b9Corpus state, measured
Kyberforge.CompositionNote51% cut, under the 12,000-char success criterion.
pre-commit run --all-filesgreen on bothskill-size-checkandvale-audit-prefilter-skill;tests/run-tests.sh --strict24/0;pre-commit run --hook-stage pre-push --all-files16/16.forgewas the last body FAIL in the corpus. It was not trimmed to fit — it routes four artifact types a single invocation classifies between, so it got a dispatch table plus common gates, with routes inreferences/author-routes.md,apm-routes.mdandversion-bump.md.The two-layer loop again justified itself, and the defect class changed
All three skills passed every deterministic gate. All three then failed the clean-context audit — 13 must-fix findings total. Waves 1–3 ran 8/9, 8/8, 8/8; wave 4 is 3/3. The ratio has never improved, which is the finding: the audit catches something scripts structurally cannot.
What changed this wave is the shape. Waves 2–3 were dominated by lost content — rules demoted or stranded when they moved. Wave 4 was dominated by invented content:
apm-installclaimed the piped installer "ignores"VERSIONandGITHUB_URL. The installer reads both from the environment and its own usage header documentsVERSIONworking through the pipe.apm-workflowclaimed apm checkstype:"going forward". No source supports it;agent-primitive-schema.md:54explicitly calls undocumented constraints "authoring guidance rather than enforced checks".forgeclaimedapm audit"already runs inside apm-workflow's own flow" and told the agent to confirm it ran clean. It never runs on those routes — and that was the routes' only completion check.forge's provenance asserted a source contributed to a file it had no hand in.Cutting text is safe. Rewriting text to be shorter is where invention enters, and every gate here is a counter — none reads for truth. One agent also reported folding a Gotcha into "restructured Step 1 / Step 2 headings" that already existed pre-retrofit; only the audit's diff against the unsynced mirror caught that the compensating structure was never built.
Stranding still appeared once, and instructively:
apm-workflowmoved theexperimental enable registriesprecondition intoconfigure.mdwhile keeping a configure-only rule in the body — the placement rule applied exactly backwards.compile.mdpublishes to a registry andinstall.mdresolves through one; neither points atconfigure.md, and declaring a registry without the precondition is a silent no-op.Every repair got a fresh verification pass. Two rewrote factual claims, so both were re-checked against primary sources — one auditor fetched the 782-line installer and confirmed the new text line by line.
Collateral landed
body-discipline.md:83andcontract.md:123citeapm-workflowby hard number as the exemplar dispatch shape. Refreshed to measured figures (body 421→237, whole-file 554→304, references 3,006→3,222) in264a5db. The quoted closing line had also changed and is now quoted with an ellipsis. These are positive exemplars in live guidance, so they are refreshed — the opposite treatment from thegit-commitsnegative example in the same file, pinned to5e23250precisely so it keeps describing pre-retrofit state.f9b919d; refreshing its figures would destroy the record of what the decision was taken against.AGENTS.mdsession rule rewritten (b25412b): nothing is grandfathered any more, so the gates bite on first commit.79c6071— I broke the shared-resolver byte-identity invariant in915eb09by editing the carve-out comment in one of three copies, and reported the suite green without re-running it.tests/test-adr0020-contract.shcaught it. Restored; comment-only, no behaviour change.Filed this wave
KyberforgeVale style is scoped[**/SKILL.md], so everyreferences/file corpus-wide is unlinted. This compounds with ADR-0020: the contract's remedy is to move prose intoreferences/, i.e. out of the prose gate.forgemoved ~900 words that way and the moved prose carried a realSentenceOpenerThereIserror.vale-wrap.shreportsin 0 files, which reads as clean.validate-provenance.shchecks slug existence and back-references, never whether a stated contribution is true.forge's retrofit asserted one that never happened and the script exited 0.installation.mdomitsapm runtime statusandapm runtime remove, which exist in apm 0.28.0. The skill is right and the doc is stale; flagged so nobody "corrects" the skill to match it.Not done, deliberately
No plugin version bump.
configure.md's repo policy says a substantive.apm/change bumps the package version, but no wave on this branch has bumped one across 41 commits. Bumpingkyberforgealone would be inconsistent; a single release bump covering all six plugins belongs at wave-5 close-out. Flagging rather than deciding unilaterally.Remaining — wave 5, orchestrator only
caveman: setdisable-model-invocation: true(zero inbound routes; still 263 chars, no boundary clause).Wave 5 — caveman flagged, and a correction to the wave-4 figure
Correction
The wave-4 comment reported the preload tax as 10,201 chars. That measurement summed all 39 skills, but
zoom-outalready carriesdisable-model-invocation: trueand is therefore withheld from the model-visible listing — it was never being preloaded. The correct post-wave-4 figure is 10,002 chars across 38 model-visible skills. The conclusion is unchanged (well under the 12,000 criterion), but the number was wrong.caveman
bdff6fdsetsdisable-model-invocation: trueoncaveman.The plan chose it on the criterion "zero inbound skill routes", which is necessary — the flag hard-blocks the Skill tool, so any
Call \caveman`step elsewhere would break the moment it lands. Re-verified across all 39 descriptions and bodies after four waves of rewrites: every mention is documentation, research notes orapm.lock.yaml`. Nothing routes to it.But that criterion is not the interesting one.
caveman's description was almost entirely a trigger list —"caveman mode","talk like caveman","use caveman","less tokens","be brief"— and"be brief"is a false-positive magnet. A user asking for brevity wants short answers, not dropped articles andRespond terse like smart caveman. Withholding the skill from the router deletes that trigger. The 287 characters saved are a side effect, not the reason. Decision taken explicitly by the repo owner: caveman is to be user-invoked only.The description is rewritten as human-facing text — it now states how to turn the mode on and off, rather than listing phrases for a reader that can no longer see it. That is what a hand-invoked skill's description is for once the router cannot read it.
Two SUGGESTIONs remain on it and are not to be fixed: the 280-char length and "description has no boundary clause". Both are #108 — the boundary check fires on hand-invoked skills, which ADR-0020 contractually exempts. Neither the length target nor the boundary clause means anything for a skill that is not in the routing surface.
Final state
Kyberforge.CompositionNote54% cut.
tests/run-tests.sh --strict24/0;pre-commit run --all-filesno failures;pre-commit run --hook-stage pre-push --all-files16/16.Still open on this issue
configure.md's repo policy says a substantive.apm/change should. Recommendation stands: one bump covering all six plugins, notkyberforgealone. Needs a decision.Gate defects found along the way and filed separately: #107, #108, #110, #111, #117, #118, #119. Content debt: #112, #113, #114, #115, #116, #119. None blocks closing this issue; all were held out of the waves deliberately rather than improvised mid-flight.
The hand-invocation decision, for all four candidates
The issue body asks for a per-skill decision on
disable-model-invocation: trueacrossgrill-me,grill-with-docs,cavemanandprototype. Onlycavemanwas flagged. Here is the reasoning for the other three, re-verified against the corpus after all five waves of description rewrites rather than against the pre-retrofit state.grill-meprototype's boundary clause-> \grill-me``grill-with-docsforge/SKILL.md:25Call \bin:grill-with-docs``prototypecavemanbdff6fdWhy the two with inbound routes are excluded
The flag hard-blocks the Skill tool (
CONTEXT.md:36-40). Note the failure mode is behavioural, not gated: flagging a skill does not delete its file, soskill-size-check's boundary-target resolver still resolves a clause pointing at it andtests/test-adr0020-targets.shstays green.prototype's boundary clause would keep passing every check while routing to something unreachable. Nothing would catch it.forge's case is blunter — Step 1 mandates the call, so flagginggrill-with-docsbreaks the entry-point router outright.Why
prototypeis excluded despite qualifyingprototypeis the only candidate besidescavemanwith zero inbound routes, so flagging it would break nothing. It should still not be flagged, and the distinction is worth writing down:Zero inbound routes makes flagging safe. It does not make it right.
prototype's description triggers on ordinary work requests — "Use when the user wants a throwaway prototype to answer a design question about a data model, state machine or business logic, or to mock up a UI in several variations, as in 'try a few designs'". That is exactly what a router should catch. Hand-invoking it hides a task skill behind a slash command the user has to already know exists, trading discoverability for 286 characters.cavemanis the opposite shape. It is a mode, requested by name, and its trigger list contained"be brief"and"less tokens"— phrasings that mean "give me short answers", not "drop articles and write in fragments". Auto-invocation there was actively wrong, and removing the skill from the router deletes the misfire. The preload saving was the side effect.So the criterion is not "does anything route to it" — that is only the safety precondition. The criterion is whether model invocation is desirable at all: modes and meta-commands, yes; task skills, no.
Remaining scope check
apm-orchestrate386,lint-runner343,gitea-orchestrate284,git-orchestrate254 — none over 400). Their bodies are 1,085 / 333 / 1,199 / 933 words, and three exceed 900 — but ADR-0020:181 exempts agents from the body gate deliberately, because an agent body runs in its own context rather than the caller's. The ADR records those same figures and explicitly warns against widening the file pattern to cover them. No agent work is outstanding.pre-commit run --all-filesclean onskill-size-checkandvale-audit-prefilter-skill.Kyberforge.CompositionNoteat 0;gitea-workflow's cross-plugin route resolved as agreed in wave 1; batched by plugin.This issue is functionally complete
Two things stand between here and closing it, both decisions rather than work:
.apm/change should. Recommendation: one bump across all six plugins.An independent post-closure audit (agent-audit + skill-audit run against every skill/agent changed on refactor/adr0020-skill-retrofit, cross-checked against this issue's scope and against the commit history of each flagged file) found 3 in-scope description-quality misses this branch's own retrofit commits introduced but never caught:
git-submodules(261e5b5),git-worktrees(3dd5387), andgitea-files(d5954d3) each collapsed length correctly but left a verb-enumerated capability clause instead of ADR-0020's required single clause. It also found one regression:00c1e6b(diagnose retrofit) moved a script-path bullet intoreferences/feedback-loops.mdand flipped a correct skill-root-relative path into an incorrect parent-relative one, contradicting that commit's own claim to have fixed a non-resolving path.All four are now fixed and validated clean via skill-audit and skill-size-check (commits on refactor/adr0020-skill-retrofit).
The same pass also re-confirmed two pre-existing, out-of-scope items already covered elsewhere:
prototype's LOGIC.md/UI.md placement is correctly deferred to #114, andapm-orchestrate.agent.md's description/dispatch issues (including a realadd-packagemapping bug vs. apm-workflow's marketplace.md) all predate this branch (authored in5e23250, before #99 started) and are outside this issue's skill-scoped gate — worth a follow-up issue in the #107-119 style, not folded in here.Second independent audit — 29 agents, all prior fixes confirmed, 3 new defects found and fixed
Ran a fresh, independent audit (skill-audit/agent-audit, one fresh agent per skill + one for
apm-orchestrate.agent.md— 29 total) against every skill and agent changed on this branch, cross-checked against this issue's full comment history rather than trusting the previous post-closure audit's claims at face value.Every previously-documented fix holds up on independent re-verification — not just re-asserted, checked against source: the
git-worktreesdestructive dispatch gap, thegit-commitssecret-scan reachability,pc-run's false-green, thegitea-issues/gitea-labels-milestonesclosed loop and org-parameter derivation,gitea-prs' three stale claims (live-verified against gitea-mcp v1.7.0),agentsmd-author's provider-file prohibition,vale-config/vale-run's false statement and stranded content,apm-install/apm-workflow/forge's wave-4 invented-content defects,research's provenance-chain break, and bothapm-orchestratecitation updates — all confirmed correct at their cited locations.3 new, previously-untracked defects were found and are now fixed (commits
59aaec4,0c0df46,6cb47f8, pushed):gitea-branches—references/commits.mdcited a nonexistentoverview.mdas authority for a scope-gating claim (the only file by that name is an external research doc, never shipped with the skill). Repointed tobranches.md's own Token scope section, which actually states the principle.gitea-releases— the retrofit (dfacf05) collapsed two Gotchas into a bidirectional claim ("deleting a tag never deletes the release wrapping it") thatreferences/call-signatures.mdnever confirms — that file explicitly marks the reverse direction unconfirmed and "the more dangerous direction to get wrong." Now states only the confirmed direction and hedges the reverse with a verification step, since this is a destructive, irreversible operation.apm-workflow— the registries precondition was only documented inreferences/configure.md, despite SKILL.md claiming it applies "anywhere — configure, install or publish."references/compile.md(Publish) andreferences/install.md(dependency resolution) — the two files that actually hit a named registry — were silent on it. Both now cross-reference the precondition.All three re-validated clean via skill-audit;
pre-commit run --all-filesandbash tests/run-tests.sh --strict(24/0) both green after the fixes; pushed.1 pre-existing bug independently confirmed rather than just alleged:
apm-orchestrate.agent.md'sadd-packagemapping really does contradictapm-workflow/references/marketplace.md's remote-only constraint, plus 2 newagent-auditFAILs (description quality, an uncited delegation restatement ofconfigure.md's Gotcha). Filed as #120, in the #107-#119 style, per this issue's own precedent for pre-existing agent-file defects being out of ADR-0020's skill-scoped gate.Nothing else changed — this issue's own "functionally complete" status and both success criteria from the previous comment stand.
Third independent review — 8 review agents, 6 fix agents, 10 commits pushed
Fresh read-only review of all 47 commits, dispatched across 8 agents (one per plugin family, plus tests/gate-scripts, commit-claim accuracy, collateral integrity, and repo-doc accuracy), then 6 fix agents. Every finding below was re-verified independently before acting on it. Pushed as
6cb47f8..0959291.The enforcement machinery was not weakened. Verified by mutation, not assertion: the removed test probes were genuine deletions with honest totals (39→37, 43→41, no vacuous placeholders), the rebuilt
.claude/skills/A/B fails under two separate mutations, and all five copies of the shared resolver hash identically. The prior audits' claims hold up on re-verification.Six content defects, four safety-relevant
git-worktrees— the "Create tracking a remote branch" row prescribedgit worktree add <path> <remote>/<branch>, which produces a detached HEAD, not a tracking branch. The DWIM fires only on a bare branch name not found locally. Commits made there are unreachable once the worktree goes. Branch-introduced — the word "tracking" was new.git worktree repair's semantics were also inverted, with an invented sentence the manual does not support.pc-run— thepre-commit cleanconfirmation gate moved intoclean.md, whilefailure-patterns.md(the diagnosis route) still prescribed the command ungated, under a body that says read one file "and no other". "Why is this hook failing" could wipe the machine-wide cache without asking. The fixer-hook rule was stranded behind "if the cause is not obvious", which is false exactly when pre-commit prints- files were modified by this hook.gitea-issues— stated in bold thatlist_issueshas notypeand nomilestonesparameter. Both exist on the deployed v1.7.0 and work.gitea-workflow's no-args check-in therefore reported PRs under "Open Issues" while the skill forbade the one-parameter fix.gitea-labels-milestones— calledexclusive"an org-labels-only flag" and mandated replace-don't-stack. Livelist_repo_labelsreturnsexclusiveon every repo label: all sevenKind/*arefalse,Priority/*/Reviewed/*/Status/*aretrue. The rule would strip a validKind/*label. The org-label fallback also read arequired=[read:organization]scope error as proof of absence.gitea-releases— theis_pre_releaseimperative was stranded behind a trigger that does not fire for "cut a v2.0.0-beta.1". Severe because there is no update/edit-release tool — the only repair is delete-and-recreate, and nothing said so.apm-workflow—type:described as constraining.apm/contents. Read against apm-cli 0.28.0: it is a processing selector, no mismatch check exists anywhere, so the real hazard is silent omission — install exits 0 having shipped none of the package's primitives.vale-config: a fix this issue recorded as landed, which never didWave 3 lists "
vale-configshipped a false statement. Gotcha 1's kernel lost the qualifier that only package styles need fetching" among "three findings worse than quality defects". The file was byte-identical to45cd260, the commit that introduced it. No repair ever landed, and the two later audits both missed it.The claim is also empirically false: a missing style is a hard
E100, exit 2 — not "finds nothing … a clean run". Worse, the same commit deleted thePackages-vs-BasedOnStylesGotcha that was the actual diagnostic, so the surviving rule sends you tovale sync, which reportsSynced 0 package(s)against aBasedOnStyles-only name. The remediation loop does not terminate. Reproduced end to end.A third gate blind spot, unfiled
validate-provenance.shmatched Contributing files only in the inline form. Seven skills —git-branches,git-remotes,git-submodules,git-workflow,git-worktrees,gitea-files,gitea-releases— use the bullet form, so checks 4 and 5 silently verified nothing on them. Those are among the skills this branch changed most, and the retrofit's mandatorysources.mdcollateral went in unchecked.Demonstrated rather than argued: planting a nonexistent contributing path in
git-remotesyields 0 findings under the old parser and 1 FAIL under the new one. Fixing the parser immediately surfaced one real defect (git-branches/references/merging.mdmissing a slugsources.mdcredits it with), now fixed. This sits alongside #117 and #118 and was not filed anywhere.docs/spec/gates.mdwas stale and contradictedAGENTS.mdIt still read "Two independent hot gates are currently red" with the full pre-retrofit table — 26/39, 9/39, 2 dangling, 58 SUGGESTIONs, 10 CompositionNote errors. Measured: 0, 0, 0, 33, 0. The branch correctly left ADR-0020 untouched (self-pinned to
f9b919d), butgates.mdcarries no such pin andAGENTS.mdnames it three times as the authoritative reasoning layer — so the stale doc told readers a one-line fix togitea-prswas blocked pending a retrofit already complete.Commit-message accuracy
Headers are 100% Conventional Commits compliant. Two numeric claims are not, and both were written into always-loaded
AGENTS.md:ee6b040claims research → 231 chars / 519 words. Actual 247 / 593. (This issue's own wave-3 table has the correct figures — the commit was measured on a pre-repair draft.)b8c36c3claims 19 descriptions over 400 and thatCompositionNote"now fires nowhere". Actual 20, andgitea-workflowstill sat at 1013 chars carrying four CompositionNote tokens; both became true one commit later atbedbd1d.Also: 24 commits typed
refactor:document behaviour fixes (restoring a force-pushconfirm: truegate, adding the dispatch row whose absence discarded commits) — those arefix:. Trailers appear on only 7 of 47. All immutable now, recorded here rather than rewritten.The release bump — done
No wave bumped a version across 56 commits. All six local packages had substantive
.apm/edits, so all six are bumped minor (the retrofit rewrote every routing surface): kyberforge 1.7.0, git 1.4.0, gitea 1.4.0, bin/core/lint 1.2.0. Perconfigure.md, versions are per package, so this is six independent bumps landing together, not one release number — the earlier "one bump covering all six" recommendation reaches the right outcome by the wrong reasoning.executables.allowis version-pinned tokyberforge#<version>by apm's design, and the first bump orphaned it — theSessionStarthook would have silently stopped deploying (ADR-0019). The comment above that block predicted exactly this, andtest-check-executables-allow-sync.shcaught it.Overlap check against #107–#120
No duplicates filed. Mapping: the conjoined-arrow recall hole is #107; the dotted-filename hole is #110, but worse than filed — for a bare target the name is not extracted at all, so a dangling route ships green and silent rather than being misreported as missing (worth adding to #110).
provider-adapter-authorbuilding a firmer rule on the dead--no-import-syntaxflag is #115's flag.git-submodules' reference files dropping thertk gitprefix is #113.git-branchesrouting cherry-pick to the weaker handler is #112. The unlintedreferences/files are #117. The false provenance assertion class is #118.apm-orchestrate'sadd-packagemapping is #120 and was left untouched. The new provenance-parser defect is the only genuinely new gate finding and is fixed here rather than filed.State
pre-commit run --all-filesclean (33 SUGGESTIONs, all advisory);tests/run-tests.sh --strict24/0;pre-commit run --hook-stage pre-push --all-filesall 14 real hooks pass. Pushed.Residual, not fixed:
gitea-prs' update-sidelabelsset semantics are unverified, so nothing was asserted about them; and two new SUGGESTIONs were accepted deliberately (vale-configGotchas at 37%,git-worktreesbody at 664) rather than deleting the safety content that earns the space.