Skip to content

Commit be21955

Browse files
claude[bot]claude
andauthored
feat(spec): retire the nine dead contributes members and kinds[].globs (family: #10724 + #11169) (#12001)
* feat(spec): retire the nine dead members of the plugin-manifest contributes block (#10724) ADR-0049 enforce-or-remove, triage graded 2026-08-21, cloud census leg discharged clean 2026-08-24. #10627 measured exactly ONE non-test read of manifest.contributes monorepo-wide (engine.ts, member kinds), controlled and re-verified on current main at claim time (objectstack main@ac59744: engine.ts:4604-4606 only; objectui origin/main@65d3e76: 0 property reads, control 63 files carry the bare word; cloud: delivered clean reading 2026-08-24 @5b5925a). - events / menus / themes / translations / actions / drivers / fieldTypes / functions / commands are retiredKey() tombstones mirroring loading (the block is not .strict() — a plain deletion would silently strip) - kinds (live reader) and routes (open fork #10726) untouched - nine retired-key entries kernel/Manifest:contributes.<member> at major 18 + ONE D3 SemanticMigration plugin-manifest-contributes-dead-members-retired (no D2 conversion: a manifest is not a stack collection member — no seam would ever run it) - liveness ledger rows updated (tombstone rows STAY, the loading/orphans asymmetry) + README row; cloud-census provenance refreshed - in-repo authors corrected: plugin-hono-server (events — subscribes imperatively already), driver-memory (drivers — wired via driver.* service) - author-facing corrections: i18n-standard.mdx Plugin Integration rewritten to defineTranslationBundle + stack translations; the Commander.js JSDoc replaced by the commands tombstone; metadata-plugin.zod.ts ADR-0088 comment stops crediting contributes.functions - rejection pins assert the specific zod issue (path + prescription) per member; survivors pinned parsing - minor changeset with FROM → TO per member + adr-0087 registered marker Upgrade-guide/spec-changes projections cover majors <=17 today; the major-18 row materializes at the protocol bump, as with every shipped major-18 retirement (entries are registered now). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01K93P8PbH7kVjsAXkqzH1zY * feat(spec): retire contributes.kinds[].globs — declared file-type watch patterns nothing ever read (#11169) ADR-0049 enforce-or-remove; maintainer ruling 2026-08-24 (verbatim provenance on the card: 「接受你的建议。」). The schema promised that declaring globs enables file-type discovery; real discovery globs filePatterns off the metadata type registry — which contributes.kinds does not extend, as metadata-plugin.zod.ts records outright — so an authored globs was accepted, stored, served back through GET /metadata/kind, and never consulted. Liveness dead confirmation re-run at claim with the card's positive control: filtered criterion empty; unfiltered returns the schema declaration (manifest.zod.ts:329) and the two type positions (registry.ts:3748/:3752 — anchors moved from the card's engine.ts citations). - globs is a retiredKey() tombstone inside the kinds item (non-strict shape); the kind bucket and id are NOT touched; kinds JSDoc stops promising discovery - registerKind/getAllKinds drop globs from their declared shapes (type-only; the parameter widens; nothing read the value) - retired-key entry kernel/Manifest:contributes.kinds.globs at major 18 + D3 SemanticMigration plugin-manifest-kind-globs-retired (no D2 conversion — no seam would ever run it) - rejection pin asserts the specific zod issue (path contributes.kinds[0].globs + prescription); positive pin: { id, description } still parses and keeps registering - breaking changeset naming the removed key + adr-0087 registered marker; ledger kinds row updated Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01K93P8PbH7kVjsAXkqzH1zY * chore(spec): regenerate unknown-key strictness-ledger counts for the contributes retirements (#10724, #11169) gen:strictness-ledger — kernel/ non-strict accepted-key count 296 -> 288: the nine tombstoned contributes members and kinds[].globs leave the accept set. Caught by scripts/strictness-ledger-doc.test.ts on the full spec suite run. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01K93P8PbH7kVjsAXkqzH1zY * merge origin/main (os-regen artifacts taken from main; regeneration follows) * chore(spec): regenerate artifacts on the merged tree (post-merge regeneration commit) os-regen-merge step 4: spec rebuilt on the merge of origin/main@497ded78, whole gen chain re-run; the two artifacts the driver took from main's side (references/kernel/manifest.mdx, strictness-ledger counts) now describe the merged source. Battery green: migration-registry / liveness / empty-state / authorable-surface / docs / api-surface / spec-changes / upgrade-guide / skill-refs / skill-docs / strictness-ledger / variant-docs all PASS. Family entries and tombstones verified present post-merge. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01K93P8PbH7kVjsAXkqzH1zY --------- Co-authored-by: Claude <noreply@anthropic.com>
1 parent 4f24e9d commit be21955

28 files changed

Lines changed: 942 additions & 243 deletions
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
---
2+
"@objectstack/spec": minor
3+
---
4+
5+
feat(spec): retire the nine dead members of the plugin-manifest `contributes` block — `events` / `menus` / `themes` / `translations` / `actions` / `drivers` / `fieldTypes` / `functions` / `commands` (#10724, ADR-0049 enforce-or-remove)
6+
7+
<!-- adr-0087: registered plugin-manifest-contributes-dead-members-retired -->
8+
9+
**BREAKING** accept-set narrowing, landing after the v17.0.0 cut (the lockstep
10+
launch-window convention ships it as `minor`; the migration prescription is
11+
registered under protocol major 18, where `os migrate meta` users will look).
12+
13+
The census behind it (#10627, controlled and re-verified at claim time across
14+
objectstack, objectui and cloud) measured that the ENTIRE monorepo contains
15+
exactly one non-test read of `manifest.contributes`, and it reads `kinds`
16+
(`packages/objectql/src/engine.ts``registry.registerKind`). The other nine
17+
members parsed, entered the manifest, and changed nothing — while published
18+
material kept teaching them: `commands` documented Commander.js runtime
19+
resolution the CLI dropped for oclif auto-discovery, `fieldTypes` advertised a
20+
registration seam that has never existed, and `events` was decorative even for
21+
its only in-repo author, which already subscribes imperatively.
22+
23+
**What is refused:** authoring any of the nine keys. Each is a `retiredKey()`
24+
tombstone (the `manifest.loading` precedent — neither `ManifestSchema` nor the
25+
`contributes` object is `.strict()`, so a plain deletion would have silently
26+
stripped the keys), so authoring one is a `tsc` error and a parse error
27+
carrying the per-key prescription.
28+
29+
**FROM → TO, per member** (each tombstone carries its own one-line fix):
30+
31+
- `contributes.events` → subscribe in plugin code (`ctx.hook('kernel:ready', …)`
32+
from `init`/`start`); delete the key.
33+
- `contributes.menus` → app `navigation` / `manifest.navigationContributions`
34+
(ADR-0029 D7); delete the key.
35+
- `contributes.themes` → the stack-level `themes` metadata collection (an
36+
unrelated `ThemeSchema` surface); delete the key.
37+
- `contributes.translations` → the `translation` metadata type:
38+
`defineTranslationBundle` in `defineStack({ translations })`; delete the key.
39+
- `contributes.actions` → the stack `actions` collection or
40+
`engine.registerAction`; delete the key.
41+
- `contributes.drivers` → register a kernel service named `driver.*`; delete
42+
the key.
43+
- `contributes.fieldTypes` → nothing (no registration seam exists; the
44+
vocabulary is the spec `FieldType` enum); delete the key.
45+
- `contributes.functions``defineStack({ functions })`; delete the key.
46+
- `contributes.commands` → oclif native plugin auto-discovery (an `oclif`
47+
section in the plugin's own `package.json`; see `cli-extension.zod.ts`);
48+
delete the key.
49+
50+
**What stays:** `contributes.kinds` (the block's one live member) and
51+
`contributes.routes` (an open enforce-or-remove fork, #10726 — deliberately
52+
untouched here). Runtime behaviour is unchanged: nothing ever read the nine
53+
members, so removing them removes no behaviour; a stored manifest still
54+
carrying one degrades to a single `[metadata_spec_invalid]` log line at
55+
registration rather than a boot failure.
56+
57+
D3 semantic entry `plugin-manifest-contributes-dead-members-retired`; no D2
58+
conversion, because a package manifest is not a stack collection member
59+
(`PLURAL_TO_SINGULAR` has no `packages`/`plugins` entry) and a conversion
60+
would be a transform with no seam that ever runs.
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
---
2+
"@objectstack/spec": minor
3+
---
4+
5+
feat(spec): retire `contributes.kinds[].globs` — the declared file-type watch patterns nothing ever read (#11169, ADR-0049 enforce-or-remove; maintainer-ruled 2026-08-24)
6+
7+
<!-- adr-0087: registered plugin-manifest-kind-globs-retired -->
8+
9+
**BREAKING** accept-set narrowing, landing after the v17.0.0 cut (the lockstep
10+
launch-window convention ships it as `minor`; the migration prescription is
11+
registered under protocol major 18, where `os migrate meta` users will look).
12+
13+
**Removed key:** `manifest.contributes.kinds[].globs`.
14+
15+
The schema promised that declaring `globs` "enables the system to parse and
16+
validate new file types" (its own example: a BI plugin handling
17+
`*.report.ts`). The promise was never kept: real glob-driven artifact
18+
discovery reads `filePatterns` off the metadata type registry — which
19+
`contributes.kinds` does not extend, as `metadata-plugin.zod.ts` records
20+
outright — so an authored `globs` was accepted, stored, served back through
21+
`GET /metadata/kind`, and never consulted. Measured (PR #11168, re-verified
22+
with positive control at claim): zero value reads anywhere; the only non-test
23+
occurrences of the path were the schema declaration and two type positions.
24+
25+
**FROM → TO:** `kinds: [{ id, globs: […], description? }]`
26+
`kinds: [{ id, description? }]` — delete the `globs` key; the kind's `id` and
27+
`description` are unchanged and still register. The key is a `retiredKey()`
28+
tombstone, so authoring it is a `tsc` error and a parse error carrying this
29+
prescription.
30+
31+
**What stays:** the `contributes.kinds` bucket itself and its `id` field
32+
(live: engine → `registry.registerKind`, served via `GET /metadata/kind`).
33+
File-type discovery remains single-channel on the metadata type registry's
34+
`filePatterns`; if plugin-extensible discovery is ever wanted, it gets
35+
designed against that registry, not revived here. The `registerKind` /
36+
`getAllKinds` type positions drop `globs` (type-only; the parameter widens).
37+
38+
D3 semantic entry `plugin-manifest-kind-globs-retired`; no D2 conversion (a
39+
manifest is not a stack collection member — no seam would ever run it).

content/docs/protocol/kernel/i18n-standard.mdx

Lines changed: 48 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -590,44 +590,63 @@ context.i18n.formatNumber(smallNumber, {
590590

591591
## Plugin Integration
592592

593-
Plugins register translation bundles in their manifest:
593+
Translations are **metadata**, declared on the stack — not manifest entries.
594+
(The former `contributes.translations` manifest key was removed in v17 (#10724,
595+
ADR-0049): no loader ever read its `{ locale, path }` entries, so a manifest
596+
that still carries it now fails the parse with the upgrade prescription.)
597+
598+
A package declares a translation bundle and registers it in its stack's
599+
`translations` collection:
594600

595601
{/* os:check */}
596602
```typescript
597-
// plugin.manifest.ts — a plugin manifest validated by `ManifestSchema`
598-
// from `@objectstack/spec/kernel` (there is no `definePlugin()` helper).
599-
// Translation files are registered under `contributes.translations` as
600-
// { locale, path } entries (see packages/spec/src/kernel/manifest.zod.ts).
601-
const manifest = {
602-
id: 'com.mycompany.crm',
603-
name: '@mycompany/crm',
604-
version: '1.0.0',
605-
606-
contributes: {
607-
translations: [
608-
{ locale: 'en', path: 'i18n/en/account.json' },
609-
{ locale: 'de', path: 'i18n/de/account.json' },
610-
],
603+
// src/translations/crm.translation.ts
604+
import { defineTranslationBundle } from '@objectstack/spec/system';
605+
606+
export const CrmTranslationBundle = defineTranslationBundle({
607+
en: {
608+
objects: {
609+
crm_account: {
610+
label: 'Account',
611+
pluralLabel: 'Accounts',
612+
fields: {
613+
name: { label: 'Account Name' },
614+
},
615+
},
616+
},
611617
},
612-
};
613-
614-
export default manifest;
618+
de: {
619+
objects: {
620+
crm_account: {
621+
label: 'Konto',
622+
pluralLabel: 'Konten',
623+
fields: {
624+
name: { label: 'Kontoname' },
625+
},
626+
},
627+
},
628+
},
629+
});
615630
```
616631

617-
### Translation File Registration
632+
```typescript
633+
// objectstack.config.ts
634+
import { defineStack } from '@objectstack/spec';
635+
import { CrmTranslationBundle } from './src/translations/crm.translation.js';
618636

619-
```
620-
@mycompany/crm/
621-
i18n/
622-
en/
623-
account.json → Namespace: crm.account
624-
contact.json → Namespace: crm.contact
625-
de/
626-
account.json
627-
contact.json
637+
export default defineStack({
638+
manifest: {
639+
id: 'com.mycompany.crm',
640+
version: '1.0.0',
641+
type: 'app',
642+
name: 'CRM',
643+
},
644+
translations: [CrmTranslationBundle],
645+
});
628646
```
629647

630-
**Namespace Convention:** `{pluginName}.{filename}`
648+
The engine registers the collection as `translation` metadata (the governed
649+
`translation` type), and the i18n pipeline serves it.
631650

632651
### Using Plugin Translations
633652

content/docs/references/kernel/manifest.mdx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ const result = ManifestSchema.parse(data);
4040
| **datasources** | `string[]` | optional | Glob patterns for Datasource definitions |
4141
| **dependencies** | `Record<string, string>` | optional | Package dependencies |
4242
| **configuration** | `{ title?: string; properties: Record<string, object> }` | optional | Plugin configuration settings |
43-
| **contributes** | `{ kinds?: object[]; events?: string[]; menus?: Record<string, object[]>; themes?: object[]; … }` | optional | Platform contributions |
43+
| **contributes** | `{ kinds?: object[]; routes?: object[] }` | optional | Platform contributions |
4444
| **data** | `{ object: string; externalId?: string \| string[]; mode?: Enum<'insert' \| 'update' \| 'upsert' \| 'replace' \| 'ignore'>; env?: Enum<'prod' \| 'dev' \| 'test'>[]; … }[]` | optional | Initial seed data (prefer top-level data field) |
4545
| **capabilities** | `{ implements?: object[]; provides?: object[]; requires?: object[]; extensionPoints?: object[]; … }` | optional | Plugin capability declarations for interoperability |
4646
| **extensions** | `Record<string, any>` | optional | Extension points and contributions |

docs/audits/2026-07-unknown-key-strictness-ledger.counts.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -261,7 +261,7 @@ directory rather than per file.
261261
| `cloud/` | 83 |
262262
| `identity/` | 32 |
263263
| `integration/` | 10 |
264-
| `kernel/` | 296 |
264+
| `kernel/` | 288 |
265265
| `qa/` | 6 |
266266
| `shared/` | 20 |
267267
| `system/` | 361 |

packages/drivers/driver-memory/objectstack.config.ts

Lines changed: 4 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -247,15 +247,10 @@ const MemoryDriverPlugin: ObjectStackManifest = {
247247
extensions: [],
248248
},
249249

250-
contributes: {
251-
drivers: [
252-
{
253-
id: 'memory',
254-
label: 'In-Memory Storage',
255-
description: 'Stores data in memory (volatile, for testing/development)',
256-
},
257-
],
258-
}
250+
// `contributes.drivers` was retired (#10724, ADR-0049): the declaration drove
251+
// nothing — this driver is wired by registering the `driver.memory` kernel
252+
// service (the objectql plugin calls `registerDriver` on `driver.*` services),
253+
// which is the enforced channel.
259254
};
260255

261256
export default MemoryDriverPlugin;

packages/objectql/src/engine-kind-registration-log.test.ts

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -52,9 +52,9 @@ describe('[#10729] contributes.kinds registration logging', () => {
5252
engine.registerApp({
5353
id: 'com.example.bi',
5454
contributes: {
55-
// Exactly the schema's shape — and exactly its own documented example
56-
// ("Registering a BI plugin to handle *.report.ts").
57-
kinds: [{ id: 'sys.bi.report', globs: ['**/*.report.ts'] }],
55+
// Exactly the schema's shape: `{ id, description? }` (`globs` was
56+
// retired unread, #11169).
57+
kinds: [{ id: 'sys.bi.report', description: 'BI reports' }],
5858
},
5959
});
6060

@@ -73,7 +73,7 @@ describe('[#10729] contributes.kinds registration logging', () => {
7373

7474
engine.registerApp({
7575
id: 'com.example.bi',
76-
contributes: { kinds: [{ id: 'sys.bi.report', globs: ['**/*.report.ts'] }] },
76+
contributes: { kinds: [{ id: 'sys.bi.report' }] },
7777
});
7878

7979
// `registerKind` → `registerItem('kind', kind, 'id')`. The value in the log
@@ -91,7 +91,6 @@ describe('[#10729] contributes.kinds registration logging', () => {
9191
contributes: {
9292
kinds: [{
9393
id: 'sys.bi.report',
94-
globs: ['**/*.report.ts'],
9594
// Neither key is declared by the schema. They are what the old line
9695
// reached for, so an author who copied an ancient example could put
9796
// them here — and the log must NOT start preferring them again.

packages/objectql/src/engine.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4606,7 +4606,8 @@ export class ObjectQL implements IObjectQLEngine {
46064606
for (const kind of manifest.contributes.kinds) {
46074607
this._registry.registerKind(kind);
46084608
// [#10729] Name the kind by its declared `id`. `contributes.kinds`
4609-
// items are `{ id, globs, description? }` (`manifest.zod.ts`) and
4609+
// items are `{ id, description? }` (`manifest.zod.ts`; `globs` was
4610+
// retired unread, #11169) and
46104611
// `registerKind` keys the item on `id` (`registerItem('kind', kind, 'id')`),
46114612
// so `id` is BOTH the only identifying field the schema declares and the
46124613
// exact key the item is stored under — a reader of this line can look the

packages/objectql/src/registry.ts

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3745,11 +3745,14 @@ export class SchemaRegistry {
37453745
// Kind Helpers
37463746
// ==========================================
37473747

3748-
registerKind(kind: { id: string, globs: string[] }) {
3748+
// #11169: `globs` left the kind shape — nothing ever read the value here or
3749+
// downstream (the two type positions below were its only non-schema
3750+
// occurrences); a kind entry is `{ id, description? }`, keyed on `id`.
3751+
registerKind(kind: { id: string; description?: string }) {
37493752
this.registerItem('kind', kind, 'id');
37503753
}
3751-
3752-
getAllKinds(): { id: string, globs: string[] }[] {
3754+
3755+
getAllKinds(): { id: string; description?: string }[] {
37533756
return this.listItems('kind');
37543757
}
37553758

packages/plugins/plugin-hono-server/objectstack.config.ts

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -230,13 +230,9 @@ const HonoServerPlugin: ObjectStackManifest = {
230230
extensions: [],
231231
},
232232

233-
contributes: {
234-
// System Events
235-
events: [
236-
'kernel:ready',
237-
'kernel:listening',
238-
],
239-
},
233+
// `contributes.events` was retired (#10724, ADR-0049): the declaration drove
234+
// nothing — this plugin already subscribes to `kernel:ready` / `kernel:listening`
235+
// imperatively in its own code, which is the enforced channel.
240236
};
241237

242238
export default HonoServerPlugin;

0 commit comments

Comments
 (0)