Skip to content

Commit 7a25e7d

Browse files
os-zhuangclaude
andauthored
feat(spec): retire the orphan CLICommandContributionSchema export after the contributes.commands tombstone (#12007, ADR-0049) (#12613)
* wip(spec): retire CLICommandContributionSchema/CLICommandContribution — source edits before regeneration (#12007) * wip(spec): regenerate artifacts for the CLICommandContribution retirement (#12007) * test(spec): pin count 838 -> 837 — Iso385 left with the retired schema (#12007) --------- Co-authored-by: Claude <noreply@anthropic.com>
1 parent 0e4e51b commit 7a25e7d

15 files changed

Lines changed: 351 additions & 134 deletions

File tree

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
---
2+
"@objectstack/spec": minor
3+
---
4+
5+
feat(spec): retire the orphan `CLICommandContributionSchema` export — the manifest surface it described is a tombstone (#12007, ADR-0049)
6+
7+
<!-- adr-0087: registered cli-command-contribution-retired -->
8+
9+
**BREAKING** export removal, landing after the v17.0.0 cut (the lockstep
10+
launch-window convention ships it as `minor`; the prescription is registered
11+
under protocol major 18 — `RETIRED_DEFS_BY_MAJOR[18]`
12+
(`kernel/CLICommandContribution`) + the D3 semantic entry
13+
`cli-command-contribution-retired` — where `os migrate meta` users will
14+
look).
15+
16+
`CLICommandContributionSchema` described a "CLI Command Contribution
17+
declaration in the manifest" and claimed to be "retained for backward
18+
compatibility and for describing command metadata in plugin manifests" — but
19+
after #10724 tombstoned `manifest.contributes.commands`, no manifest surface
20+
could legally carry these entries: the exported schema advertised a shape
21+
whose only declared carrier rejects it. The manifest never referenced this
22+
schema even before the tombstone (its inline `commands` item schema was an
23+
independent duplicate), and it had zero consumers outside spec's own test and
24+
generated artifacts, measured at the retirement's base commit with positive
25+
controls in objectstack, objectui (pinned sha) and cloud — the exported
26+
orphan-value-schema class (#3950: an exported schema with no consumer reads
27+
as a capability).
28+
29+
FROM → TO:
30+
31+
- `CLICommandContributionSchema` / `CLICommandContribution`*(removed — no
32+
declarative replacement, because no declarative surface ever carried it)*.
33+
CLI commands are registered through oclif's native plugin discovery: the
34+
plugin package declares an `oclif` section in its own `package.json`
35+
`OclifPluginConfigSchema` / `OclifPluginConfig` (same module) describe that
36+
live surface and survive unchanged.
37+
38+
One-line fix: delete the import (nothing ever read the declaration); if you
39+
describe a plugin's CLI commands, declare the `oclif` section in the plugin's
40+
`package.json``OclifPluginConfigSchema` validates it.
41+
42+
The retirement kit:
43+
44+
- whole-def deletion (route 3 — no carrier key, no authored document, so no
45+
tombstone and no D2 conversion; the #11825 / #8715 shape):
46+
`kernel/CLICommandContribution` in `RETIRED_DEFS_BY_MAJOR[18]`, plus the D3
47+
semantic entry `cli-command-contribution-retired`
48+
- pin test (`kernel/cli-command-contribution-retirement.test.ts`): zero
49+
holders for both retired names on every public entry, survivors pinned
50+
(`OclifPluginConfigSchema` / `OclifPluginConfig` — the live `package.json`
51+
`oclif` surface)
52+
- the module docblock's Commander.js migration prose is KEPT — it is cited by
53+
the `contributes.commands` tombstone (full-file deletion was explicitly not
54+
the shape)
55+
- zero authored occurrences in objectstack, objectui or cloud (measured at
56+
dispatch, re-verified at claim), so no in-repo source changes ride along

content/docs/references/index.mdx

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
---
22
title: Protocol Reference
3-
description: Every schema published by @objectstack/spec — 1585 schemas across 14 protocol modules
3+
description: Every schema published by @objectstack/spec — 1584 schemas across 14 protocol modules
44
---
55

66
{/* ⚠️ AUTO-GENERATED — DO NOT EDIT. Run build-docs.ts to regenerate. Hand-written docs live in the module folders under content/docs/. */}
@@ -26,14 +26,14 @@ counts are sums of the rows they head. Regenerate with
2626
| [Data Protocol](/docs/references/data) | 29 | 166 | Objects, fields, queries, filters, datasources and drivers — the ObjectQL layer. |
2727
| [Identity Protocol](/docs/references/identity) | 5 | 27 | Users and accounts, organizations, positions, SCIM provisioning. |
2828
| [Integration Protocol](/docs/references/integration) | 1 | 27 | The single connector protocol (ADR-0097) — catalog descriptors and provider-bound instances. |
29-
| [Kernel Protocol](/docs/references/kernel) | 31 | 172 | Plugin lifecycle and manifests, capabilities and security, metadata loading, service registry. |
29+
| [Kernel Protocol](/docs/references/kernel) | 31 | 171 | Plugin lifecycle and manifests, capabilities and security, metadata loading, service registry. |
3030
| [QA Protocol](/docs/references/qa) | 1 | 8 | Declarative test suites — scenarios, steps, actions and assertions. |
3131
| [Security Protocol](/docs/references/security) | 5 | 27 | Permission sets, row-level security, sharing rules, tenancy posture. |
3232
| [Shared Protocol](/docs/references/shared) | 8 | 32 | Primitives used across every protocol — identifiers, HTTP, expressions, error maps, enums. |
3333
| [Studio Protocol](/docs/references/studio) | 3 | 35 | Studio designer metadata — the authoring surfaces for the protocols above. |
3434
| [System Protocol](/docs/references/system) | 36 | 288 | The runtime environment — logging, jobs, cache, metrics, notifications, i18n and compliance. |
3535
| [UI Protocol](/docs/references/ui) | 16 | 152 | Apps, pages, views, dashboards, reports, actions and themes — the ObjectUI layer. |
36-
| **Total** | **199** | **1585** | 14 protocol modules |
36+
| **Total** | **199** | **1584** | 14 protocol modules |
3737

3838
---
3939

@@ -215,13 +215,13 @@ The single connector protocol (ADR-0097) — catalog descriptors and provider-bo
215215

216216
## Kernel Protocol
217217

218-
**Source:** `packages/spec/src/kernel/` · **Import:** `@objectstack/spec/kernel` · **31 pages, 172 schemas**
218+
**Source:** `packages/spec/src/kernel/` · **Import:** `@objectstack/spec/kernel` · **31 pages, 171 schemas**
219219

220220
Plugin lifecycle and manifests, capabilities and security, metadata loading, service registry.
221221

222222
| File | Schemas |
223223
| :--- | :--- |
224-
| [`cli-extension.zod.ts`](/docs/references/kernel/cli-extension) | `CLICommandContribution`, `OclifPluginConfig` |
224+
| [`cli-extension.zod.ts`](/docs/references/kernel/cli-extension) | `OclifPluginConfig` |
225225
| [`cluster.zod.ts`](/docs/references/kernel/cluster) | `ClusterCapabilityConfig`, `ClusterDriver`, `ClusterTenantIsolation`, `EventClusterOptions`, `EventDeliverySemantics`, `EventScope`, `MetadataChangeOperation`, `ServiceClusterAnnotations`, `ServiceClusterScope`, `ServiceLeaderStrategy` |
226226
| [`context.zod.ts`](/docs/references/kernel/context) | `KernelContext`, `PreviewModeConfig`, `RuntimeMode`, `TenantRuntimeContext` |
227227
| [`dependency-resolution.zod.ts`](/docs/references/kernel/dependency-resolution) | `DependencyResolutionResult`, `DependencyStatusEnum`, `RequiredAction`, `ResolvedDependency` |

content/docs/references/kernel/cli-extension.mdx

Lines changed: 3 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -71,26 +71,13 @@ The `objectstack.config.ts` plugins array no longer determines CLI commands.
7171
## TypeScript Usage
7272

7373
```typescript
74-
import { CLICommandContributionSchema, OclifPluginConfigSchema } from '@objectstack/spec/kernel';
75-
import type { CLICommandContribution, OclifPluginConfig } from '@objectstack/spec/kernel';
74+
import { OclifPluginConfigSchema } from '@objectstack/spec/kernel';
75+
import type { OclifPluginConfig } from '@objectstack/spec/kernel';
7676

7777
// Validate data
78-
const result = CLICommandContributionSchema.parse(data);
78+
const result = OclifPluginConfigSchema.parse(data);
7979
```
8080

81-
---
82-
83-
## CLICommandContribution
84-
85-
### Properties
86-
87-
| Property | Type | Required | Description |
88-
| :--- | :--- | :--- | :--- |
89-
| **name** | `string` || CLI command name |
90-
| **description** | `string` | optional | Command description for help text |
91-
| **module** | `string` | optional | Module path exporting oclif Command classes |
92-
93-
9481
---
9582

9683
## OclifPluginConfig

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/` | 273 |
264+
| `kernel/` | 272 |
265265
| `qa/` | 6 |
266266
| `shared/` | 20 |
267267
| `system/` | 361 |

packages/spec/api-surface/kernel.json

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,6 @@
1313
"BreakingChange (type)",
1414
"BreakingChangeParsed (type)",
1515
"BreakingChangeSchema (const)",
16-
"CLICommandContribution (type)",
17-
"CLICommandContributionSchema (const)",
1816
"CONSUMER_INSTALLABLE_TYPES (const)",
1917
"CORE_PLUGIN_TYPES (const)",
2018
"CapabilityClassification (interface)",

packages/spec/authorable-surface/kernel.json

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,9 +20,6 @@
2020
"kernel/BreakingChange:removedIn",
2121
"kernel/BreakingChange:severity",
2222
"kernel/BreakingChange:type",
23-
"kernel/CLICommandContribution:description",
24-
"kernel/CLICommandContribution:module",
25-
"kernel/CLICommandContribution:name",
2623
"kernel/ClusterCapabilityConfig:driver",
2724
"kernel/ClusterCapabilityConfig:driverOptions",
2825
"kernel/ClusterCapabilityConfig:heartbeatMs",

packages/spec/export-origins/kernel.json

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,6 @@
1313
"BreakingChange": "src/kernel/plugin-versioning.zod.ts#BreakingChange (type)",
1414
"BreakingChangeParsed": "src/kernel/plugin-versioning.zod.ts#BreakingChangeParsed (type)",
1515
"BreakingChangeSchema": "src/kernel/plugin-versioning.zod.ts#BreakingChangeSchema (const)",
16-
"CLICommandContribution": "src/kernel/cli-extension.zod.ts#CLICommandContribution (type)",
17-
"CLICommandContributionSchema": "src/kernel/cli-extension.zod.ts#CLICommandContributionSchema (const)",
1816
"CONSUMER_INSTALLABLE_TYPES": "src/kernel/plugin.zod.ts#CONSUMER_INSTALLABLE_TYPES (const)",
1917
"CORE_PLUGIN_TYPES": "src/kernel/plugin.zod.ts#CORE_PLUGIN_TYPES (const)",
2018
"CapabilityClassification": "src/kernel/platform-capabilities.ts#CapabilityClassification (interface)",

packages/spec/json-schema.manifest/kernel.json

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@
66
"kernel/ArtifactFileEntry",
77
"kernel/ArtifactSignature",
88
"kernel/BreakingChange",
9-
"kernel/CLICommandContribution",
109
"kernel/CapabilityConformanceLevel",
1110
"kernel/ClusterCapabilityConfig",
1211
"kernel/ClusterDriver",
Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
// Copyright (c) 2026 ObjectStack. Licensed under the Apache-2.0 license.
2+
3+
import { describe, it, expect } from 'vitest';
4+
5+
import {
6+
EXPORT_ENTRY_POINTS,
7+
exportNamesOf,
8+
holdersOf,
9+
} from '../../scripts/lib/export-origins-testkit';
10+
11+
// ─── [#12007] kernel/ CLICommandContribution is RETIRED ─────────────────────
12+
//
13+
// ADR-0049 enforce-or-remove. `kernel/cli-extension.zod.ts` no longer declares
14+
// `CLICommandContributionSchema` / `CLICommandContribution` — 1 emitted def,
15+
// 2 exported names, 3 authorable-surface keys (`name`, `description`,
16+
// `module`), the reference page's section with them.
17+
//
18+
// The measurement that decided it (re-verified at this retirement's base
19+
// commit, 146f448a5, with positive controls — the full record lives in the
20+
// retirement block inside the zod module):
21+
//
22+
// 1. CARRIER — after #10724, `manifest.contributes.commands` is a
23+
// `retiredKey()` tombstone: no manifest surface could legally carry a
24+
// command-contribution entry, so the exported schema advertised a shape
25+
// whose only declared carrier rejects it. The manifest never referenced
26+
// this schema even before the tombstone — its inline `commands` item
27+
// schema was an independent duplicate.
28+
// 2. STATIC — zero readers outside `packages/spec`'s own test and generated
29+
// artifacts, in objectstack, objectui (at the pinned sha) and cloud
30+
// (controls: `OclifPluginConfigSchema` and `@objectstack/spec` both
31+
// resolve hits — the scans see real readers).
32+
// 3. DOORS — no metadata-type binding, no stack collection, no manifest
33+
// embed: no authored document could ever carry it.
34+
//
35+
// Route 3: with no carrier key there is nothing to tombstone and no seam for
36+
// a D2 conversion — `RETIRED_DEFS_BY_MAJOR[18]` plus the D3 semantic entry
37+
// `cli-command-contribution-retired` ARE the declaration.
38+
//
39+
// Form follows #11825 / #8715 / #4988: resolved symbol identity over every
40+
// public entry via the build-time `export-origins/` artifact.
41+
describe('[#12007] kernel/ CLICommandContribution retirement', () => {
42+
/** The 2 names the retired def exported (1 schema const + 1 type). */
43+
const RETIRED_NAMES = [
44+
'CLICommandContributionSchema',
45+
'CLICommandContribution',
46+
] as const;
47+
48+
/**
49+
* Names that must SURVIVE on `./kernel`: the LIVE half of the same module —
50+
* `OclifPluginConfigSchema` describes the `oclif` section of a plugin's own
51+
* `package.json`, the mechanism that actually registers CLI commands.
52+
* Exactly what a too-wide "tidy the cli-extension module" sweep would take
53+
* (full-file deletion was explicitly NOT the shape: the module docblock's
54+
* Commander.js migration prose is cited by the `contributes.commands`
55+
* tombstone).
56+
*/
57+
const MUST_SURVIVE_KERNEL = [
58+
'OclifPluginConfigSchema',
59+
'OclifPluginConfig',
60+
] as const;
61+
62+
it('every retired name has ZERO holders on any public entry; the survivors still stand', () => {
63+
// Anti-vacuity: the baseline must cover the real surface.
64+
for (const needed of ['.', './kernel']) {
65+
expect(EXPORT_ENTRY_POINTS, `exports map must include ${needed}`).toContain(needed);
66+
}
67+
expect(
68+
exportNamesOf('./kernel').length,
69+
'./kernel must export a non-trivial surface'
70+
).toBeGreaterThan(50);
71+
72+
// ── ABSENCE (every entry, not just ./kernel) ──────────────────────────
73+
for (const name of RETIRED_NAMES) {
74+
expect(holdersOf(name), `${name} must have zero holders after #12007`).toEqual([]);
75+
}
76+
77+
// ── SURVIVAL ──────────────────────────────────────────────────────────
78+
const kernelNames = exportNamesOf('./kernel');
79+
for (const name of MUST_SURVIVE_KERNEL) {
80+
expect(kernelNames, `${name} must SURVIVE this retirement`).toContain(name);
81+
}
82+
});
83+
84+
it('the runtime barrel resolves without the retired schema and keeps the survivor', async () => {
85+
const kernel = await import('./index');
86+
expect(kernel, 'CLICommandContributionSchema must not be exported after #12007')
87+
.not.toHaveProperty('CLICommandContributionSchema');
88+
// Anti-vacuity: the barrel really resolved and still exports the live
89+
// oclif surface plus an unrelated kernel anchor.
90+
expect(kernel).toHaveProperty('OclifPluginConfigSchema');
91+
expect(kernel).toHaveProperty('ManifestSchema');
92+
});
93+
});

packages/spec/src/kernel/cli-extension.test.ts

Lines changed: 4 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -1,69 +1,9 @@
11
import { describe, it, expect } from 'vitest';
2-
import {
3-
CLICommandContributionSchema,
4-
OclifPluginConfigSchema,
5-
} from './cli-extension.zod';
6-
7-
describe('CLICommandContributionSchema', () => {
8-
it('should accept valid command contribution', () => {
9-
const result = CLICommandContributionSchema.parse({
10-
name: 'marketplace',
11-
description: 'Manage marketplace applications',
12-
});
13-
expect(result.name).toBe('marketplace');
14-
expect(result.description).toBe('Manage marketplace applications');
15-
});
16-
17-
it('should accept command with module path', () => {
18-
const result = CLICommandContributionSchema.parse({
19-
name: 'deploy',
20-
description: 'Deploy to cloud',
21-
module: './dist/commands/deploy.js',
22-
});
23-
expect(result.module).toBe('./dist/commands/deploy.js');
24-
});
25-
26-
it('should accept minimal command (name only)', () => {
27-
const result = CLICommandContributionSchema.parse({
28-
name: 'sync',
29-
});
30-
expect(result.name).toBe('sync');
31-
expect(result.description).toBeUndefined();
32-
expect(result.module).toBeUndefined();
33-
});
34-
35-
it('should accept hyphenated command names', () => {
36-
const result = CLICommandContributionSchema.parse({
37-
name: 'cloud-sync',
38-
});
39-
expect(result.name).toBe('cloud-sync');
40-
});
41-
42-
it('should reject invalid command names', () => {
43-
const invalidNames = [
44-
'Uppercase',
45-
'has spaces',
46-
'123start',
47-
'-leading-hyphen',
48-
'special_underscore',
49-
'dot.name',
50-
'',
51-
];
52-
53-
invalidNames.forEach(name => {
54-
expect(() => CLICommandContributionSchema.parse({ name })).toThrow();
55-
});
56-
});
57-
58-
it('should accept valid lowercase alphanumeric names', () => {
59-
const validNames = ['a', 'abc', 'a1', 'my-command', 'plugin2'];
60-
61-
validNames.forEach(name => {
62-
expect(() => CLICommandContributionSchema.parse({ name })).not.toThrow();
63-
});
64-
});
65-
});
2+
import { OclifPluginConfigSchema } from './cli-extension.zod';
663

4+
// [#12007] The `CLICommandContributionSchema` block that used to sit here left
5+
// with the retired export (ADR-0049 enforce-or-remove) — the rejection/holder
6+
// pins live in `cli-command-contribution-retirement.test.ts`.
677
describe('OclifPluginConfigSchema', () => {
688
it('should accept valid oclif plugin config', () => {
699
const result = OclifPluginConfigSchema.parse({

0 commit comments

Comments
 (0)