Commit aaacf1d
## ⛔ This PR does NOT close #17147, and must not be given a closing
keyword
#17147 asks for the opposite of what this lands: it asks that the
registered grant start **refusing**. This PR states, in every shipped
carrier, that it **does not**. The card stays open and keeps its
`needs-user-decision` grade — the maintainer's A/B/C question is
answered **B** (say it truthfully now), ⛔ not withdrawn.
`Blocked-by: #17147` on **#13458** (Phase 2, retire the legacy
`string[]` arm) is unchanged and still load-bearing: retiring the legacy
arm while the structured replacement refuses nothing is exactly the
shape this correction exists to keep visible.
## The measurement, on `9bd4344e4`
| probe | result |
|---|---|
| `new SecurePluginContext(` outside tests | **0** |
| callers of `enforceFileRead` / `enforceFileWrite` /
`enforceNetworkRequest` | **0** — `SecurePluginContext` included |
| `os plugin install` / `.osplugin` loader / `sys_plugin_installation` |
**none of the three exist** |
| artifact `packages[]` payload | `sys_package_version.manifest_json` —
metadata; the blob never reaches a runtime |
| `contains_code` readers in this repo | **0** |
⇒ #13457 / #17137 gave `registerGrantedPermissions` its first production
caller. Nothing queries the registry it fills, and two of the four grant
classes have **no enforcement surface even in principle** — so the
consented set records what is allowed and denies no operation.
## The ruling this executes
Maintainer, 2026-09-12, **option B** — the same option ruled for the
sibling half of this very sentence in **#11330** (2026-08-30,
verbatim「同意」): *say it truthfully now*; building the per-plugin context
stays a separate, separately designed direction, fenced to the ADR-0025
install-flow design effort by ruling `5486840233`.
## What changed
Four shipped sentences claimed enforcement. Each is corrected to one
truthful split — **registered at load · queried by nothing · refuses no
operation**:
- `PluginPermissionEnforcer.registerGrantedPermissions` docblock (the
retracted sentence was *"so `SecurePluginContext` enforces exactly the
consented surface"*)
- `PluginPermissionsSchema` docblock — `spec/kernel/manifest.zod.ts`
- the `manifest.loading` tombstone prescription, which ships into
**six** generated reference pages
- the ADR-0087 D3 entry `plugin-manifest-loading-retired`, which ships
the same prescription into `docs/protocol-upgrade-guide.md`
plus a `Callout` beside the permission table in the hand-written
`content/docs/plugins/development.mdx`.
Generated artifacts regenerated from those sources:
`content/docs/references/**` (6 pages),
`docs/protocol-upgrade-guide.md`, `packages/spec/spec-changes.json`,
`packages/spec/src/migrations/registry.ts`. `check:docs` /
`check:migration-registry` / `check:upgrade-guide` /
`check:spec-changes` all report in-sync.
## The mechanical half — why this cannot rot in either direction
`plugin-runtime-tier-truthful-text.test.ts`'s coordination pin held the
permissions half **verbatim** so it would go red the day that half was
corrected (*"#11333 owns it"*). It did its job. It is discharged and
replaced by pins on the truthful text in **both** carriers, each keeping
the negative assertion that stops the retracted sentence returning
*beside* the new one.
`packages/core/src/security/granted-permissions-not-enforced.pin.test.ts`
(new) pins the **measurement** as well as the words:
- zero production `SecurePluginContext` construction sites;
- zero callers of the three fs/network gates;
- both halves present on the docblock, retracted sentence absent;
- a behavioural case: a registered grant answers `getPluginPermissions`
and gates nothing;
- a positive control, so a broken scan can never read as "no offenders".
⇒ **It fails the day the ADR-0025 materialize seam lands**, and its
failure message names every text that becomes false at that moment —
including objectui's `PluginDisclosure.tsx`, corrected in
objectstack-ai/objectui#TBD.
**Ablated three ways**, each turning it red: a fake production
construction site; the retracted sentence restored; the tombstone
reverted to `which are enforced`.
Clause-②: no — no new key lands on any published payload. The diff is
docblocks, two tombstone/prescription strings, one hand-written docs
page, their generated projections, and one new test file;
`PluginPermissionsSchema`, `EnvironmentArtifactSchema` and every other
schema keep their exact key sets (`packages/spec`'s own suite, 13434
tests, is green on that).
> ⚠️ **`Part-of PR must not also close its card` is red and stays red.**
Its RULE 2 forbids a card-relation trailer in any *commit* message, and
this branch's commit ends `Refs #17147 · …`. The gate's own log states
the position: the check is advisory (absent from the required-context
registry, and its workflow subscribes to no `merge_group` event), `Refs`
lands as a reference and moves **no** card, and ⛔ the repair it refuses
to ask for is a history rewrite — forbidden in this repository. Recorded
here so the red is read rather than acted on.
## Not in this PR
- **`docs/adr/0025-plugin-package-distribution.md` §3.7**
(*"service/hook/file/network already enforced"*) carries the same claim.
`docs/adr/**` is a governed surface (Prime Directive #14), so it is
split into its own PR that stays draft for maintainer merge.
- **No behaviour change.** No accept/reject, no registration, no gate
added or removed.
Refs #17147 · #11333 · #13457 · #13458 · #11330 · `5486840233`
🤖 Generated with [Claude Code](https://claude.com/claude-code)
---------
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
1 parent cb0c616 commit aaacf1d
16 files changed
Lines changed: 327 additions & 34 deletions
File tree
- .changeset
- content/docs
- plugins
- references
- api
- kernel
- docs
- packages
- core
- src/security
- spec
- src
- kernel
- migrations
- entries/semantic
Lines changed: 16 additions & 0 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
264 | 264 | | |
265 | 265 | | |
266 | 266 | | |
| 267 | + | |
| 268 | + | |
| 269 | + | |
| 270 | + | |
| 271 | + | |
| 272 | + | |
| 273 | + | |
| 274 | + | |
267 | 275 | | |
268 | 276 | | |
269 | 277 | | |
| |||
Large diffs are not rendered by default.
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
1901 | 1901 | | |
1902 | 1902 | | |
1903 | 1903 | | |
1904 | | - | |
| 1904 | + | |
1905 | 1905 | | |
1906 | 1906 | | |
1907 | 1907 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
45 | 45 | | |
46 | 46 | | |
47 | 47 | | |
48 | | - | |
| 48 | + | |
49 | 49 | | |
50 | 50 | | |
51 | 51 | | |
| |||
0 commit comments