Skip to content

Retire the nine dead members of the plugin-manifest contributes block (events/menus/themes/translations/actions/drivers/fieldTypes/functions/commands) — tombstone route, kinds survives #10724

Description

@os-elon

Follow-on from #10627, which measured reader liveness for the whole plugin-manifest contributes block. Filed unassigned. That card carries the full verdict table; this one is the removal unit it cut.

The verdict being acted on

Measured on origin/main (299b85e9d), monorepo-wide, non-test: the entire monorepo contains exactly one read of manifest.contributes, and it reads one member.

packages/objectql/src/engine.ts:4499: if (manifest.contributes?.kinds) {
packages/objectql/src/engine.ts:4500: this.logger.debug('Registering kinds from manifest', { id, kindCount: manifest.contributes.kinds.length });
packages/objectql/src/engine.ts:4501: for (const kind of manifest.contributes.kinds) {

grep: \.contributes\b|contributes\?\.|contributes\[ over *.ts,*.tsx,*.mts,*.mjs,*.js, excluding node_modules/dist, test files and comment lines. So kinds is LIVE and every other member of the block has zero readers.

Controls — an uncontrolled zero is not a reading

  • A. The pattern finds a real read of this exact block. The identical regex returns the three kinds lines above. The probe is not blind to contributes.
  • B. Manifest reads are findable at all.manifest.(namespace|navigationContributions|plugins|id) → 133 non-test hits.
  • C. The member words exist in the corpus. Bare-word non-test hit counts: events 568, menus 3, themes 29, translations 497, actions 1175, drivers 395, fieldTypes 31, functions 338, commands 223. The zero is about the path, not the word.
  • D. objectui. 65 hits of the word contributes, 0 property reads (\.contributes\b|contributes\?\.) — the same asymmetry.

⚠️Measurement gap to close before executing this card. The cloud repo could not be attached to the measuring session (add_repo returned "you don't have access to objectstack-ai/cloud"). The #4914 precedent measured all three repos; this census covers objectstack + objectui only. Re-run the cloud leg first — with its own control probe — and only then remove.

Scope — nine members, one carrier

MemberlineWhat the author should use instead
events337Imperative subscription in the plugin's start(). Its only in-repo author — packages/plugins/plugin-hono-server/objectstack.config.ts:233, declaring kernel:ready / kernel:listening — already subscribes in code; the declaration drives nothing.
menus342App navigation / manifest.navigationContributions (read at packages/objectql/src/engine.ts:4472-4474registerAppNavContribution). Two alias maps already redirect this spelling: packages/spec/src/ui/app.zod.ts:1242 (menus: 'navigation') and packages/spec/src/system/translation.zod.ts:428.
themes351None — see #10485 (stack themes carrier). Note the two surfaces are unrelated shapes: this one is {id,label,path}, the stack carrier is ThemeSchema, and the stack-level hits reach the registry through top-level manifest.themes (METADATA_ARRAY_KEYS, packages/objectql/src/engine.ts:1884), never through contributes.themes.
translations361The translation metadata type / stack translations collection (governed, packages/spec/liveness/translation.json).
actions370Stack actions collection (METADATA_ARRAY_KEYS, packages/objectql/src/engine.ts:1884) + engine.registerAction.
drivers382The driver.* kernel service. Its only in-repo author — packages/drivers/driver-memory/objectstack.config.ts:250 — is registered by packages/objectql/src/plugin.ts:592 (this.ql.registerDriver(service) for services named driver.*), not by its declaration.
fieldTypes392Nothing declared. There is no registerFieldType seam anywhere — zero hits monorepo-wide.
functions402defineStack({ functions })packages/objectql/src/hook-binder.ts:128-143 (engine.registerFunction(...)).
commands448oclif auto-discovery. Already stated in-tree at packages/spec/src/kernel/cli-extension.zod.ts:58-63: "The previous plugin model required contributes.commands in the manifest … The objectstack.config.ts plugins array no longer determines CLI commands."

Deliberately not in this card:contributes.routes (line 419) is an enforce-or-remove fork, not a mechanical removal — filed separately. contributes.kinds (line 327) survives; it has a reader.

Route: tombstone, not deletion

Neither ManifestSchema nor the contributes object is .strict()packages/spec/src/kernel/manifest.zod.ts:321 is a plain z.object, and the only .strict() in the file (line 44) belongs to PluginPermissionsSchema. That is exactly the condition recorded for the sibling retirement at lines 510-514: a plain deletion silently strips the key, replacing an inert declaration with an invisible one. So the route is retiredKey() per member, mirroring loading at line 518.

Record shape to mirror (#4914 precedent, ADR-0087)

  1. retiredKey() tombstone per member in manifest.zod.ts (see loading, line 518) carrying the migrate sentence.
  2. Retired-key entries under packages/spec/src/migrations/entries/retired-keys/ — cf. 17.kernel__Manifest__loading.ts (export const entry = 'kernel/Manifest:loading').
  3. One ADR-0087 D3 SemanticMigration for the block — cf. packages/spec/src/migrations/entries/semantic/17.plugin-manifest-loading-retired.ts, registered in packages/spec/src/migrations/registry.ts. D3 and not a D2 conversion, for the reason recorded verbatim in the retired-key entry: the conversion chain walks a normalized STACK and PLURAL_TO_SINGULAR has no packages / plugins entry, so a manifest is not a stack collection member and a conversion would be a transform with no seam that ever runs.
  4. Generated upgrade-guide row (docs/protocol-upgrade-guide.md) + changeset.

Author-facing corrections this removal owes

These are the reason the block is not merely tidying — published material actively teaches these keys:

  • content/docs/protocol/kernel/i18n-standard.mdx:599-612 — an os:check-marked example stating "Translation files are registered under contributes.translations". It parses and configures nothing.
  • content/docs/plugins/development.mdx:334 and :388 — manifest examples with contributes blocks.
  • docs/adr/0088-metadata-kind-admission-and-retirement.md:41 credits "plugin contributes.functions (QL query functions)" as a delivered form of the function kind. That row is wrong and should be corrected with this removal.
  • packages/spec/src/kernel/manifest.zod.ts:429-447 documents Commander.js runtime resolution for commands as current behaviour, contradicting cli-extension.zod.ts:58-63.

Acceptance

  • The cloud leg is measured (with control) and still zero, or this card is re-scoped.
  • Nine retiredKey() tombstones; kinds and routes untouched.
  • ADR-0087 D3 + retired-key entries + upgrade-guide row + changeset.
  • Runtime behaviour unchanged and verified as such — nothing read the members, so removing them removes no behaviour.
  • The four author-facing corrections above land with it, so no published page keeps teaching a no-op key.

Generated by Claude Code

Metadata

Metadata

Assignees

No one assigned

    Type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions