You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
fix(spec): the manifest permissions block names its surface and offers the rename (#16846)
`PluginPermissionsSchema` was the one closed object among the three known
"strict object inside a union" doors that never adopted `strictObject`. Born
`.strict()` with the ADR-0025 plugin-distribution work, it never passed through
the unknown-key campaign, so its refusal was zod's own bare
`Unrecognized key: "hoooks"` — the key echoed, but no surface and no rename,
while every neighbouring block on the same manifest carried all three.
The cause reported on the card — `formatZodError` flattening the union's nested
refusal away — is false on this base. `formatZodIssue` descends `invalid_union`
and ranks the arms through `selectUnionBranches`
(`shared/union-branch-policy.ts`), dropping the `z.array(z.string())` arm as
kind-mismatch-only and rendering the object arm verbatim. That flattening was
lifted at #4971 and consolidated at #8318, and the strictness ledger's
`state-machine.zod.ts` row already records it as spent. So the union is
deliberately untouched: reshaping it costs either the accept set or the
published JSON Schema and buys a message the author already has.
The accept set does not move. `strictObject` is
`z.object(shape, { error }).strict()`: same shape, same strictness, plus an
error map consulted only once an issue is already being raised.
Claude-Session: https://claude.ai/code/session_016N6xmWt5hYm94ffVEwGH8x
Co-authored-by: Claude <noreply@anthropic.com>
The manifest `permissions` block's unknown-key refusal now names the surface and offers the rename, like every other block on the manifest.
6
+
7
+
`PluginPermissionsSchema` decides which services, hooks, network hosts and filesystem paths a plugin may touch. It has refused unknown keys since it was introduced, but through zod's own bare message: an author who transposed `hooks` as `hoooks` read `Unrecognized key: "hoooks"` — the key echoed back, with no surface name and no suggested spelling — while every neighbouring block on the same manifest (`contributes`, `contributes.kinds[]`, `engines`, the legacy `engine`, and the manifest root itself) named all three. Born closed at the ADR-0025 plugin-distribution work, it never passed through the unknown-key campaign that gave the others their error maps.
8
+
9
+
It now uses the same `strictObject` helper as its neighbours, so the refusal reads:
10
+
11
+
```
12
+
Unrecognized key(s) on the `permissions` block of this package manifest: `hoooks`.
13
+
Did you mean `hoooks` → `hooks`? …
14
+
```
15
+
16
+
Three spelled-out near-misses that edit distance cannot reach are curated as aliases: `filesystem` and `paths` point at `fs`, and `hosts` points at `network`.
17
+
18
+
**The accept set does not move.**`strictObject` is `z.object(shape, { error }).strict()` — the declared keys and the strictness are unchanged, and an error map is consulted only once an issue is already being raised. The `permissions` union keeps both arms (the legacy flat string list and the structured block), and the union itself is untouched. Only the text of a refusal that already happened is different.
0 commit comments