Measured while seeding the plugin-manifest liveness ledger for #10728 (all eight verdicts recorded as dead in packages/spec/liveness/manifest.json). Filed, not fixed — this is the enforce-or-remove unit the ledger's dead rows produce.
Measured on origin/main (b9e9227e3). Sibling of #10724, which is the same shape for the contributes block; these three were outside that card's scope because nothing had measured the manifest's non-contributes surface until now.
What is declared, and what is measured
For each block the probe finds zero reads of the container, which settles every key beneath it at once — a key cannot be read if the object holding it never is.
| Block | Line | Keys beneath | Reads of the container |
|---|
capabilities (PluginCapabilityManifestSchema) | manifest.zod.ts:484 | implements, provides, requires, extensionPoints, extensions | 0 |
configuration | manifest.zod.ts:305 | title, properties | 0 |
extensions (z.record(z.string(), z.unknown())) | manifest.zod.ts:491 | — (untyped) | 0 |
Probe: (manifest|pkg|package|entry|artifact)?.<key> over *.ts,*.tsx,*.mts,*.mjs,*.js across packages, apps, examples, skills, excluding node_modules/dist, test files and comment lines. objectui likewise zero.
⚠️.capabilities is the one that looks non-zero and is not. A bare .capabilities probe returns hits, and every one belongs to a different surface: driver loader contracts (packages/metadata/src/metadata-manager.ts:798), the QuickJS sandbox argument set (packages/runtime/src/sandbox/quickjs-runner.ts:120), REST discovery (packages/rest/src/rest-server.ts:3333). None is reached from a manifest. Worth stating because the next person to re-measure this will hit the same false positive.
Also note the live analogue one level up: real dependency resolution runs off top-level manifest.dependencies (packages/metadata-protocol/src/protocol.ts:4290-4300), not off capabilities.requires.
Controls — an uncontrolled zero is not a reading
- The same probe finds real reads of sibling manifest keys:
.packaging (cli/src/commands/plugin/build.ts:126), .data (runtime/src/app-plugin.ts:946), .contributes (objectql/src/engine.ts:4504). objectui control: manifest.(id|name|namespace|version) → 46 hits, so manifest reads are findable in that repo.
The one key worth calling out separately
configuration.properties declares a per-setting shape with secret: z.boolean().optional().describe('If true, value is encrypted/masked (e.g. API Keys)').
Nothing encrypts, masks, resolves, or even parses it. A plugin author reading that describe() writes secret: true next to an API key and gets exactly the same handling as secret: false — which is a stronger claim than "unused", because the key's own text is an assurance about credential handling. capabilities' describe() has the milder version of the same problem (it sells "plugin interoperability and automatic discovery"; no discovery path consults it).
Note on extensions specifically
Its value type is z.unknown(), so this key is where anything the platform does not yet model would get parked. Its emptiness is therefore evidence of something slightly different from the other two: authors are not parking things here either. Worth separating when a removal is cut — an untyped escape hatch with no users is a cheaper removal than one with unknown users.
⚠️ Scope limit — measure cloud before removing
cloud is unmeasured, carried unchanged from #10627 (add_repo → "you don't have access to objectstack-ai/cloud"), and manifest.zod.ts:10-18 says the control plane mirrors these shapes when it validates a published .osplugin and persists declared metadata onto sys_package_version. capabilities is the most plausible of the three to have a cloud-side consumer (marketplace discovery). Run the cloud leg with its own control probe first — the same precondition #10724 carries.
Route, if removal is chosen
Tombstone, not deletion, on the #4914 / #10724 precedent: neither ManifestSchema nor these blocks are .strict() (manifest.zod.ts:132 is a plain z.object; the only .strict() in the file, at :44, belongs to PluginPermissionsSchema), so a plain deletion silently strips the key — replacing an inert declaration with an invisible one. retiredKey() per block, retired-key entries, one ADR-0087 D3 semantic entry, upgrade-guide row, changeset. Sequencing with #10724 (also a manifest tombstone card, currently pm:blocked on the same cloud census) is a triage call — the two touch the same file and the same D3 neighbourhood.
Filed unassigned for triage.
Blocked-by: #10812
Generated by Claude Code
Measured while seeding the plugin-manifest liveness ledger for #10728 (all eight verdicts recorded as
deadinpackages/spec/liveness/manifest.json). Filed, not fixed — this is the enforce-or-remove unit the ledger'sdeadrows produce.Measured on
origin/main(b9e9227e3). Sibling of #10724, which is the same shape for thecontributesblock; these three were outside that card's scope because nothing had measured the manifest's non-contributessurface until now.What is declared, and what is measured
For each block the probe finds zero reads of the container, which settles every key beneath it at once — a key cannot be read if the object holding it never is.
capabilities(PluginCapabilityManifestSchema)manifest.zod.ts:484implements,provides,requires,extensionPoints,extensionsconfigurationmanifest.zod.ts:305title,propertiesextensions(z.record(z.string(), z.unknown()))manifest.zod.ts:491Probe:
(manifest|pkg|package|entry|artifact)?.<key>over*.ts,*.tsx,*.mts,*.mjs,*.jsacrosspackages,apps,examples,skills, excludingnode_modules/dist, test files and comment lines.objectuilikewise zero..capabilitiesis the one that looks non-zero and is not. A bare.capabilitiesprobe returns hits, and every one belongs to a different surface: driver loader contracts (packages/metadata/src/metadata-manager.ts:798), the QuickJS sandbox argument set (packages/runtime/src/sandbox/quickjs-runner.ts:120), REST discovery (packages/rest/src/rest-server.ts:3333). None is reached from a manifest. Worth stating because the next person to re-measure this will hit the same false positive.Also note the live analogue one level up: real dependency resolution runs off top-level
manifest.dependencies(packages/metadata-protocol/src/protocol.ts:4290-4300), not offcapabilities.requires.Controls — an uncontrolled zero is not a reading
.packaging(cli/src/commands/plugin/build.ts:126),.data(runtime/src/app-plugin.ts:946),.contributes(objectql/src/engine.ts:4504).objectuicontrol:manifest.(id|name|namespace|version)→ 46 hits, so manifest reads are findable in that repo.The one key worth calling out separately
configuration.propertiesdeclares a per-setting shape withsecret: z.boolean().optional().describe('If true, value is encrypted/masked (e.g. API Keys)').Nothing encrypts, masks, resolves, or even parses it. A plugin author reading that describe() writes
secret: truenext to an API key and gets exactly the same handling assecret: false— which is a stronger claim than "unused", because the key's own text is an assurance about credential handling.capabilities' describe() has the milder version of the same problem (it sells "plugin interoperability and automatic discovery"; no discovery path consults it).Note on
extensionsspecificallyIts value type is
z.unknown(), so this key is where anything the platform does not yet model would get parked. Its emptiness is therefore evidence of something slightly different from the other two: authors are not parking things here either. Worth separating when a removal is cut — an untyped escape hatch with no users is a cheaper removal than one with unknown users.cloudbefore removingcloudis unmeasured, carried unchanged from #10627 (add_repo→ "you don't have access to objectstack-ai/cloud"), andmanifest.zod.ts:10-18says the control plane mirrors these shapes when it validates a published.ospluginand persists declared metadata ontosys_package_version.capabilitiesis the most plausible of the three to have a cloud-side consumer (marketplace discovery). Run the cloud leg with its own control probe first — the same precondition #10724 carries.Route, if removal is chosen
Tombstone, not deletion, on the #4914 / #10724 precedent: neither
ManifestSchemanor these blocks are.strict()(manifest.zod.ts:132is a plainz.object; the only.strict()in the file, at :44, belongs toPluginPermissionsSchema), so a plain deletion silently strips the key — replacing an inert declaration with an invisible one.retiredKey()per block, retired-key entries, one ADR-0087 D3 semantic entry, upgrade-guide row, changeset. Sequencing with #10724 (also a manifest tombstone card, currentlypm:blockedon the same cloud census) is a triage call — the two touch the same file and the same D3 neighbourhood.Filed unassigned for triage.
Blocked-by: #10812
Generated by Claude Code