fix(kyberforge): description-quality.md lists user-invocable: false as a hand-invocation marker, which is backwards #125

Closed
opened 2026-08-31 08:07:29 +00:00 by Claude · 1 comment
Collaborator

Found during the #99 ADR-0020 retrofit and the follow-up audit pass on refactor/adr0020-skill-retrofit. Left unfixed there because the rubric and the gate now disagree, and picking which one moves is a decision.

The line

plugins/kyberforge/.apm/skills/agent-audit/references/description-quality.md:30 — verified at that line in the working tree — opens the "Step 0 — establish which contract applies" dispatch:

  • disable-model-invocation: true or user-invocable: false — the agent is hand-invoked. Its description is never matched against user intent, so it is not a routing string. It carries one plain human-facing sentence stating what the agent does. Audit it for that and nothing else.

The two fields are not synonyms and do not point the same way:

  • disable-model-invocation: true — the model cannot route to it. Only a human can invoke it. That is hand-invoked, and the paragraph's reasoning ("its description is never matched against user intent") follows.
  • user-invocable: false — the user cannot invoke it. The model still can, which means the description is matched against intent and is a routing string.

So the second field is listed as evidence for exactly the conclusion it contradicts. An agent carrying user-invocable: false is the most routing-dependent case there is, and this rubric tells an auditor to skip every routing check on it — no trigger clause, no boundary clause, no indirect triggers, and to report any such finding as "a wrong finding, not a strict one".

Why this is now also a rubric-vs-gate divergence

#108's carve-out is implemented on this branch, and hand_invoked() in the gate keys on disable-model-invocation only — matching that issue's stated scope, which named just that field. That was the right call for #108.

The effect is that description-quality.md:30 now documents a rule the gate does not implement, in the direction of being wrong on its own terms. A human auditor following the rubric would exempt an agent the script correctly checks, and would then read the script's finding as a false positive.

Scope note

Both fields are Copilot-only, and the rubric says so two sentences later — neither is on the vendor-neutral APM agent allowlist, so this arises in a project- or user-scope Copilot .agent.md and nowhere else. That bounds the blast radius; it does not make the sentence correct, and the rubric is the thing an auditor reads when they hit an unfamiliar field.

Suggested fix

Drop user-invocable: false from that bullet. If it deserves a contract of its own — plausibly "route-critical, audit the description harder than usual" — give it a separate bullet rather than folding it into the hand-invoked case. The two paragraphs below (the Copilot-only note, the Claude Code counterpart note) need re-reading against whichever shape is chosen.

Files

  • plugins/kyberforge/.apm/skills/agent-audit/references/description-quality.md:30 — and the plugins/kyberforge/skills/… mirror, which is generated
  • scripts/skill-size-check.sh — hand_invoked(), for the gate side
  • #108 — the carve-out whose scope this now diverges from
  • #99 — the retrofit during which this surfaced
Found during the #99 ADR-0020 retrofit and the follow-up audit pass on `refactor/adr0020-skill-retrofit`. Left unfixed there because the rubric and the gate now disagree, and picking which one moves is a decision. ## The line `plugins/kyberforge/.apm/skills/agent-audit/references/description-quality.md:30` — verified at that line in the working tree — opens the "Step 0 — establish which contract applies" dispatch: > - **`disable-model-invocation: true` or `user-invocable: false`** — the agent is hand-invoked. Its description is never matched against user intent, so it is not a routing string. It carries **one plain human-facing sentence** stating what the agent does. Audit it for that and nothing else. The two fields are not synonyms and do not point the same way: - `disable-model-invocation: true` — the **model** cannot route to it. Only a human can invoke it. That is hand-invoked, and the paragraph's reasoning ("its description is never matched against user intent") follows. - `user-invocable: false` — the **user** cannot invoke it. The model still can, which means the description *is* matched against intent and *is* a routing string. So the second field is listed as evidence for exactly the conclusion it contradicts. An agent carrying `user-invocable: false` is the most routing-dependent case there is, and this rubric tells an auditor to skip every routing check on it — no trigger clause, no boundary clause, no indirect triggers, and to report any such finding as "a wrong finding, not a strict one". ## Why this is now also a rubric-vs-gate divergence #108's carve-out is implemented on this branch, and `hand_invoked()` in the gate keys on `disable-model-invocation` **only** — matching that issue's stated scope, which named just that field. That was the right call for #108. The effect is that `description-quality.md:30` now documents a rule the gate does not implement, in the direction of being wrong on its own terms. A human auditor following the rubric would exempt an agent the script correctly checks, and would then read the script's finding as a false positive. ## Scope note Both fields are Copilot-only, and the rubric says so two sentences later — neither is on the vendor-neutral APM agent allowlist, so this arises in a project- or user-scope Copilot `.agent.md` and nowhere else. That bounds the blast radius; it does not make the sentence correct, and the rubric is the thing an auditor reads when they hit an unfamiliar field. ## Suggested fix Drop `user-invocable: false` from that bullet. If it deserves a contract of its own — plausibly "route-critical, audit the description harder than usual" — give it a separate bullet rather than folding it into the hand-invoked case. The two paragraphs below (the Copilot-only note, the Claude Code counterpart note) need re-reading against whichever shape is chosen. ## Files - `plugins/kyberforge/.apm/skills/agent-audit/references/description-quality.md:30` — and the `plugins/kyberforge/skills/…` mirror, which is generated - `scripts/skill-size-check.sh` — `hand_invoked()`, for the gate side ## Related - #108 — the carve-out whose scope this now diverges from - #99 — the retrofit during which this surfaced
Claude added this to the Skills & Agents milestone 2026-08-31 08:07:29 +00:00
Claude added the Kind/Bug
Reviewed
Confirmed
1
Priority
Low
4
labels 2026-08-31 08:07:29 +00:00
Author
Collaborator

Implementation note

Confirmed via agent-definition.md's field table that disable-model-invocation and user-invocable are independent: the first controls whether the runtime auto-selects the agent (i.e. whether its description is a routing string), the second only controls manual invocation. An agent can be disable-model-invocation: false + user-invocable: false — model-routed, three-part description shape still applies, just blocked from manual invocation. So user-invocable: false never changes whether the description is a routing string; it doesn't belong in this bullet, or in this file's description-quality contract at all.

Fix: removed user-invocable: false from the hand-invoked-marker bullet, leaving disable-model-invocation: true as its sole trigger. Added a short explanatory line noting user-invocable: false's independence and that it carries no description-quality contract of its own, rather than giving it a separate bullet — it isn't a description-quality concern to audit for either way.

## Implementation note Confirmed via `agent-definition.md`'s field table that `disable-model-invocation` and `user-invocable` are independent: the first controls whether the runtime auto-selects the agent (i.e. whether its description is a routing string), the second only controls manual invocation. An agent can be `disable-model-invocation: false` + `user-invocable: false` — model-routed, three-part description shape still applies, just blocked from manual invocation. So `user-invocable: false` never changes whether the description is a routing string; it doesn't belong in this bullet, or in this file's description-quality contract at all. Fix: removed `user-invocable: false` from the hand-invoked-marker bullet, leaving `disable-model-invocation: true` as its sole trigger. Added a short explanatory line noting `user-invocable: false`'s independence and that it carries no description-quality contract of its own, rather than giving it a separate bullet — it isn't a description-quality concern to audit for either way.
Sign in to join this conversation.