Symptom
The prose and the behaviour disagree, and the prose is the wrong one.
packages/spec/src/ai/skill.zod.ts carries a NOTE paragraph in its docblock:
NOTE — there is deliberately NO per-skill permissions field. Access to AI capability is gated at the AGENT level (agent.access / agent.permissions, both enforced at the chat route), and each tool enforces its own authz when invoked. A permissions key authored on a skill is unknown to this schema and silently stripped at parse time — it grants and restricts nothing (ADR-0049: no unenforced security-shaped fields). Do not author one.
But SkillSchema no longer strips it — it refuses it, with a located message keyed on permissions in the same file:
permissions is not a skill key — skill invocation was never permission-gated, so this was stripped in silence and the author believed they had a gate. Gate at the AGENT instead (access / permissions on the agent, enforced since #1884), or on the underlying tools' actions.
The behaviour is strictly safer than the docblock claims (an author who writes permissions is now told, instead of quietly believing they have a gate — the exact hazard the refusal message describes). Only the docblock needs to change: it should say the key is rejected, and point at the refusal message, so nobody reads "silently stripped" and concludes the authoring surface is still lax.
Note the refusal message's own "this was stripped in silence" is fine as written — it narrates the historical reason for the refusal, not current behaviour. The NOTE paragraph is the one that states the stale present tense.
Still present on origin/main as of 2026-08-11.
Root cause
Documentation drift: the permissions key was moved onto the strict unknown-key refusal list without the surrounding docblock being updated in the same pass. Not a behaviour defect — no code change is required, only the prose.
Reproduction
- Read the docblock paragraph beginning "NOTE — there is deliberately NO per-skill
permissions field" in packages/spec/src/ai/skill.zod.ts.
- Read the
permissions: entry in the unknown-key refusal messages further down the same file.
- Parse a skill authored with a
permissions key: it is rejected with that message, not accepted-and-stripped as the docblock states.
Source
Extracted from the QA run #7463 (framework a86db17).
Symptom
The prose and the behaviour disagree, and the prose is the wrong one.
packages/spec/src/ai/skill.zod.tscarries aNOTEparagraph in its docblock:But
SkillSchemano longer strips it — it refuses it, with a located message keyed onpermissionsin the same file:The behaviour is strictly safer than the docblock claims (an author who writes
permissionsis now told, instead of quietly believing they have a gate — the exact hazard the refusal message describes). Only the docblock needs to change: it should say the key is rejected, and point at the refusal message, so nobody reads "silently stripped" and concludes the authoring surface is still lax.Note the refusal message's own "this was stripped in silence" is fine as written — it narrates the historical reason for the refusal, not current behaviour. The
NOTEparagraph is the one that states the stale present tense.Still present on
origin/mainas of 2026-08-11.Root cause
Documentation drift: the
permissionskey was moved onto the strict unknown-key refusal list without the surrounding docblock being updated in the same pass. Not a behaviour defect — no code change is required, only the prose.Reproduction
permissionsfield" inpackages/spec/src/ai/skill.zod.ts.permissions:entry in the unknown-key refusal messages further down the same file.permissionskey: it is rejected with that message, not accepted-and-stripped as the docblock states.Source
Extracted from the QA run #7463 (framework a86db17).