Skip to content

Commit f289f2b

Browse files
fix(cli)!: retire os generate schema — it refuses, and points at os validate and the published per-type schemas (#19098) (#20266)
Fixes #19098 Fixes #20270 Clause-②: no (narrowing) Executes maintainer ruling `5856790152` on #19098 (batch #227 item 5, **letter C**, maintainer 「同意」, confirmed `5856865990`): `os generate schema` is retired, not repaired. No file is generated in its place, and nothing in `packages/spec` moves (ruling item 4). ## What changed **`packages/cli/src/commands/generate.ts`** - `RETIRED_GENERATORS` gains `schema` in the `os g agent` shape. The refusal reads "`os g schema` was retired" and cites the maintainer ruling. It names `os validate` (the real parse: `ObjectStackDefinitionSchema.safeParse`), and names the per-type schemas `@objectstack/spec` publishes at `node_modules/@objectstack/spec/json-schema/CATEGORY/TYPE.json`, which carry the published projection plus `x-dropped-refinements`. It exits 1. - The ruling's id lives in the ledger's code comment, not in the refusal. Text an author is shown carries no tracker number (AGENTS.md; `check:doc-authoring`'s cross-package prose-id leg ratchets `#NNN` tokens in `packages/**` strings, and `generate.ts` has no ledger row). - **Deleted, not left dead:** `runSchemaGeneration`, with its three `z.toJSONSchema(ObjectStackDefinitionSchema…)` rungs and both ladder notices; `KNOWN_UNSUPPORTED_JSON_SCHEMA_PATTERNS`; `isKnownUnsupportedJsonSchema`; and the `case 'schema'` route. - Nothing else was schema-only. `-o`, `--dry-run` and `--format` serve `types`, `client` and `migration`. `z` and `ObjectStackDefinitionSchema` were dynamic imports inside the deleted function. `zod` stays a dependency, used by `compile`, `validate` and `format`. The `type` argument's help text derives from `GENERATORS` and never listed `schema`. - **The ledger door moved (PM mechanism assumption 2, measured and falsified).** The lookup sat inside `runMetadataGeneration`, which runs only after two earlier steps: the sub-command `switch` (where `schema` returned first) and the NAME requirement (where a nameless call stopped). So a `schema` entry alone could never be reached from `os generate schema`. - BEFORE, at BASE `0d3ec4713` on the dev runner, `os g agent` with no name printed `Missing required argument` and exited 1. - The lookup now runs first in `Generate.run`, through `refuseRetiredGenerator`, as an own-key read (the package's `Object.prototype.hasOwnProperty.call` idiom). - Consequences, both pinned. `os g agent` with no name now prints the agent retirement. `os g constructor NAME` used to print "`os g constructor` was retired — undefined" and crash with `TypeError: retired.detail is not iterable`. It now falls through to the ordinary checks: exit 1, "No file naming convention is declared for type: constructor". **Tests** - `packages/cli/test/generate-schema-retired.e2e.test.ts` (new) is the retirement pin, in the `agent` precedent's shape: a real child process through `bin/run-dev.js` plus tsx, with assertions on stdout content and the exit status. It covers: - `os generate schema` (no name) and `os g schema -o custom.schema.json` both exit 1 and write nothing (directory listing `[]`); - "was retired", with neither "Unknown type:" nor "Missing required argument"; - "maintainer ruling", `os validate`, and `@objectstack/spec/json-schema/`; - the alias output is byte-equal to the documented spelling's; - `os g agent` (no name) answers the agent retirement; - `os g constructor thing` is not taken for a retired type; - control: `os g object customer --dry-run` still previews. - `packages/cli/test/generate-schema-writes-json-schema.e2e.test.ts` (the #17873 pin, groups (a) to (e)) is **deleted**. Every assertion in it was about the document the ruling withdrew: the file exists, parses, declares its draft, the four members' fragments, and the input direction. Nothing in it survives the retirement. - Other tests that exercised the command: none. `git grep` for `generate schema`, `runSchemaGeneration`, `objectstack.schema.json` and `isKnownUnsupportedJsonSchema` over `packages/cli/src` and `packages/cli/test` finds only the deleted file. The ladder PR (#17903) added no pin of its own beyond that file. - `packages/cli/test/generate-refuses-retired-generator.test.ts` (new, patch round 1) is the per-PR guard for the door. It spawns the CLI and is not named `.e2e`, so it runs in the queue's `integration` project. It pins: - `os generate schema` and `os g schema -o custom.schema.json`: exit 1, nothing written, the retirement answered (neither "Missing required argument" nor "Unknown type:"), naming the ruling, `os validate` and `@objectstack/spec/json-schema/`; - `os g constructor thing`: an own-key miss (exit 1, not "was retired", no `TypeError`, nothing written); - control: `os g object customer --dry-run` exits 0 and previews exactly what the exported object template emits. - `packages/cli/test/generate-agent-retired.e2e.test.ts` (#20270, its own commit `a0cec66e9`): the live-generator control asserted `import * as Data from '@objectstack/spec/data'`, which the object template stopped emitting in `0bd11261e`, so this nightly case was red on main. It now asserts the template's current shape, `ObjectSchema` among the named imports from `@objectstack/spec/data` plus the `ObjectSchema.create({` call, never one exact import line. The case is neither skipped nor quarantined. **Docs (ruling item 2).** The sweep is `git grep -n "generate schema" -- content/docs`, run at BASE: | hit | disposition | |:--|:--| | `content/docs/api/data-flow.mdx:200`, the JSON Schema row ("Autocomplete and validation for `objectstack.config.ts` (via `os generate schema`)") | **removed** | | `content/docs/references/api/plugin-rest-api.mdx:107` | kept: a substring of "Auto-generate schemas", the REST plugin's `generateSchemas` option, in an auto-generated reference; it is not the command | | `content/docs/references/api/plugin-rest-api.mdx:282` | kept: same | - One docs edit goes beyond the grep-found set, declared here. The Mermaid node `D[JSON Schema]` and its two edges, in the same section directly above the row, pictured the removed row's output ("Build Time: generate, then JSON Schema, then feed IDE Autocomplete"). They are removed, and `TypeScript Types` now feeds `IDE Autocomplete` alone. - The page's front matter is untouched. #20170 landed its title-rule hunk on it, and the merge was clean. - `content/docs/deployment/cli.mdx` (patch round 1) gains a warn-type Callout, "`os generate schema` is retired", in the `os generate` section beside the `os g agent` one, the same shape as the two prior CLI retirements. It points at `os validate` (linked to the page's own `os validate` heading) and at the per-type schemas `@objectstack/spec` publishes. The refusal's `Docs:` link lands on this page. The page never listed a `schema` sub-type, so nothing else on it changes. - Also swept, with nothing to change: - `packages/cli/README.md` carries only the generic `os generate` row; - `skills/` has 0 hits; - `content/docs/protocol/diagram.mdx:243-255` (see Acceptance notes). **Changeset** `.changeset/19098-retire-generate-schema.md`: - `@objectstack/cli: minor`, with a **BREAKING** banner and `Clause-②: no (narrowing)`; - the ADR-0087 marker in the `os g agent` wording (`not-required (no-migration-prescription)`); - reach outside the repository stated as NOT MEASURED (no telemetry); - the refusal's pointer given as the migration: delete the call and any editor mapping of `objectstack.schema.json`, run `os validate`, and point editors at the per-type schemas. ## Verification, patch round 1 (at HEAD `2cb9e9613` unless stated) - **Merge.** `origin/main` `17bd31877` merged by merge as `9f702ee94` (`scripts/pm/os-regen-merge.sh`). No incoming file is one of this PR's. - **Derived gates.** `node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack --commands` derived 93 commands: round 0's 92 plus `pnpm check:cli-examples-parity`, from the `cli.mdx` edit. All 93 exited 0 at `2cb9e9613`. `--ran` with the recorded exit codes reads "93 derived, 93 run, 0 NOT-MEASURED, 0 UNRUN" (a derived zero). - **Named gates:** - `pnpm lint`: exit 0 (118 s); - `node scripts/check-issue-citations.mjs --base origin/main` (`ae8e3ca01`): exit 0; - `pnpm check:docs-audit-scope` and `node scripts/docs-audit/check-affected-docs.mjs`: exit 0 each; - `pnpm check:docs-transcript-drift` and `pnpm check:cli-examples-parity`: exit 0 each; - `pnpm --filter @objectstack/cli typecheck`: `VERDICT command-exit 0`, with the new pin inside the `tsconfig.test.json` program (`--listFilesOnly`: 1 hit). - **Per-PR pin.** `pnpm --filter @objectstack/cli exec vitest run --project integration --maxWorkers=2 test/generate-refuses-retired-generator.test.ts`: 7/7. It sits in the queue's `integration` population (54 to 55 files) and is absent from the nightly population. The partition pin `test/vitest-tiers-partition.test.ts` passes 22/22. - **Nightly.** `OS_TEST_TIERS=nightly pnpm --filter @objectstack/cli exec vitest run --project integration --maxWorkers=2 test/generate-schema-retired.e2e.test.ts test/generate-agent-retired.e2e.test.ts`: 2 files, 19/19. That includes "the generators that were not retired still work", the #20270 case. - **Shape proof for #20270.** The two new assertions accept the current emission: through the exported template, the `ObjectSchema` import matches and the `create` call matches. They reject the pre-#20195 emission taken from `0bd11261e^`: neither matches. - **Ablations on the per-PR file.** These ran on committed state `a2dda36c2`. `generate.ts` and the per-PR file are byte-identical at `2cb9e9613`. | leg | mutation | on-disk proof | per-PR pin result | restore | |:--|:--|:--|:--|:--| | A | `schema` ledger key renamed to `schema_ablated` | anchor 1 to 0; blob `62cb7ec8b8ed` to `7a1f27dd8cfb`; grep `schema-entry=0 ablated-entry=1` | **2 failed**, 5 passed: "is the retirement, not a missing name and not an unknown type"; "names the ruling, `os validate` and the per-type schemas" | blob equals HEAD `62cb7ec8b8ed`, `git diff HEAD` empty | | B | own-key read reverted to `RETIRED_GENERATORS[args.type]` | anchor 1 to 0; blob to `26ec87322f84`; grep `own-key=0 bracket=1` | **1 failed**, 6 passed: the `os g constructor thing` case | same | After both legs, the restored per-PR run passed 7/7. - **History.** The #20270 fix is its own commit, `a0cec66e9`. A first push, `a2dda36c2`, had carried a first version of it together with the per-PR pin and the callout. Rewriting that pushed commit was refused by the session's permission classifier, so the split line was merged with the pushed head instead (`2cb9e9613`, whose tree is identical to `a0cec66e9`'s). Nothing pushed was discarded. ## Verification, round 0 (at HEAD `c1036ebb9` unless stated) - **Derived gates.** `node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack --commands` derived 92 commands, and all 92 exited 0 at `c1036ebb9`. `--ran` with the recorded exit codes reads "92 derived, 92 run, 0 NOT-MEASURED, 0 UNRUN" (a derived zero). - `check-adr-0087-registration --base origin/main` reads `[BREAKING+clause-②-narrowing] not-required (no-migration-prescription)`. - `check-changeset-no-major` reads "no `major` bump". Its level axis reads the PR body, so it is judged in CI. - **`pnpm lint`** (repo-wide, `eslint . --no-inline-config`): exit 0, 130 s. - **`pnpm --filter @objectstack/cli typecheck`**: `VERDICT command-exit 0`. That is `tsc --noEmit` plus `check:test-typecheck`, and the new pin is inside the `tsconfig.test.json` program (`--listFilesOnly`: 1 hit). - **Board check.** `node scripts/check-issue-citations.mjs --base origin/main` (`a9fb83ef0`) exits 0: 73 citations judged, 71 resolve, 2 cross-repo. - **`unit` layer**, run at merge commit `5c7c5bee3`: - `pnpm --filter @objectstack/cli exec vitest run --project unit --maxWorkers=2` ran 229 files: 227 passed and 2 failed as prerequisite refusals ("packages/cli is not built"). - After the CLI build, those 2 files passed (29 tests). - `c1036ebb9` differs from `5c7c5bee3` only in the new pin, which is not in the unit population. - **Integration tier.** The retirement pin is `*.e2e.test.ts` like the `agent` precedent, so it is **nightly-tier**: the per-PR queue run does not select it. - Run locally as `OS_TEST_TIERS=nightly pnpm --filter @objectstack/cli exec vitest run --project integration --maxWorkers=2` over the new pin plus `generate-agent-retired`, `generate-skill` and `generate-object-namespace-prefix`. - Result: 38 passed and 1 failed. The failure is the pre-existing stale control in `generate-agent-retired.e2e.test.ts` (see Acceptance notes); the new pin is 10/10. - **Reach checks** on the dev runner, BEFORE at BASE and AFTER at HEAD: - `os generate schema -o out.json` went from exit 0 and a 3,335,734-byte file written to exit 1, the refusal, and nothing written; - `os g agent` went from "Missing required argument" to the agent retirement; - `os g constructor foo` went from a `TypeError` crash to the ordinary refusal. **Ablation.** This is a one-shot proof, run against the committed state `c1036ebb9` through `scripts/ablation-replace.mjs` under a script-level `trap` restore. The subject resolves from `src/` via tsx, so no dist is involved. | leg | mutation | on-disk proof | pin result | restore | |:--|:--|:--|:--|:--| | A | `schema` ledger key renamed to `schema_ablated` (the lookup misses it) | anchor 1 to 0; blob `62cb7ec8b8ed` to `7a1f27dd8cfb`; grep `schema-entry=0 ablated-entry=1` | **4 failed**, 6 passed: "was RETIRED", "names the ruling", `os validate`, per-type schemas | blob equals HEAD `62cb7ec8b8ed`; `git diff HEAD` empty | | B | own-key read reverted to `RETIRED_GENERATORS[args.type]` | anchor 1 to 0; blob to `26ec87322f84`; grep `own-key=0 bracket=1` | **1 failed**, 9 passed: the `constructor` own-keys assertion | same | In leg A the exit-1 and writes-nothing assertions stayed green, because the missing-name path also exits 1 without writing. That is exactly why the pin asserts the refusal's content, as the `agent` precedent does. ## Acceptance notes 1. Now repaired here (#20270, commit `a0cec66e9`): the live-generator control in `packages/cli/test/generate-agent-retired.e2e.test.ts` asserted `import * as Data from '@objectstack/spec/data'`, which the object template stopped emitting in `0bd11261e`. - Triage's note-2 sweep covered all 73 nightly-tier files under `packages/cli` for pre-#20195 template text (`import * as Data from '@objectstack/spec/data'`, `Data.ServiceObject`). That line was the only nightly hit; the control, the same sweep at `9f702ee94`, finds it. - Two per-PR files carry the old shape on purpose and are not changed: `scaffold-emission-typechecks.test.ts` (a tsc canary fixture) and `scaffold-object-declaration-shape.test.ts` (it refuses the pre-ruling literal). 2. `.changeset/sour-moons-smile.md`, pending and unreleased, records the #17903 ladder repair this PR deletes. It is left unchanged. - Rewriting it here is the case `check-empty-changeset.mjs` classes as a DELIBERATE CORRECTION (#18160, ruling D on #17712). The gate goes red on any modification or deletion of a changeset from the merge base, and its remedy is "do NOT restore it -- say so on the PR and get it confirmed". - The seat answered the open question with A: leave the file. The new changeset tells the reader that this retirement supersedes that repair, and the release owner may drop the file at compilation. 3. The docblock at `packages/spec/scripts/lib/refinement-projection.ts` (the "Producers outside `packages/spec`'s own artifacts" bullet) still names the CLI's `os generate` as a direct `z.toJSONSchema` producer. It goes stale once this lands, but `packages/spec` is untouched by ruling item 4. 4. The `GENERATORS[type]` lookup in `runMetadataGeneration` is still an inherited-key read. So `os g constructor NAME` answers "No file naming convention is declared for type: constructor" rather than "Unknown type". The answer is misleading but does not crash, and it is not changed here. 5. Reach outside this repository is NOT MEASURED (no telemetry). Inside it there is no reader and no editor mapping of `objectstack.schema.json`. 6. `content/docs/protocol/diagram.mdx:243-255` carries a `D[JSON Schema]` node feeding IDE autocomplete, and a row "Generated JSON Schema files for IDE validation". It does not name the command, and the plural "files" reads as the per-type schemas `@objectstack/spec` still publishes, so it is left unchanged. --- _Generated by [Claude Code](https://claude.ai/code/session_01UYBdGBzWSrAMzpW8ah3GbP)_ --------- Co-authored-by: Claude <noreply@anthropic.com>
1 parent 733822c commit f289f2b

8 files changed

Lines changed: 534 additions & 404 deletions
Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
---
2+
'@objectstack/cli': minor
3+
---
4+
5+
fix(cli): **BREAKING** — `os generate schema` is retired, and it now says why and points at `os validate` and the per-type JSON Schemas `@objectstack/spec` publishes (#19098)
6+
7+
Clause-②: no (narrowing)
8+
9+
<!-- adr-0087: not-required (no-migration-prescription) A CLI COMMAND NAME is an invocation surface, not authorable metadata. There is no authorable key, no `sys_metadata` row and no schema to tombstone here, so there is nothing for `objectstack migrate meta` to rewrite, nothing for `spec-changes.json` to project and no FROM -> TO spelling for the upgrade guide to carry: the ruling generates no replacement file, so there is no call to rename, only one to delete. Nor is the ledger the notification channel: the command itself now refuses, exits 1 and names `os validate` and the published per-type schemas at the exact moment and place of use. Same reasoning shape as the `os g agent` retirement, one command over. -->
10+
11+
**⛔ If a script, a Makefile or a CI step in your project runs `os generate schema`
12+
(or `os g schema`), it will now exit 1 and write nothing.** That is the intended
13+
outcome: the command is gone by maintainer ruling, and the failure is how you find
14+
out. Nothing in this repository reads the file it wrote.
15+
16+
`minor`, not `major`: during the launch window this stack ships breaking changes as
17+
`minor` (pre-1.0 semantics under lockstep versioning — see
18+
`scripts/check-changeset-no-major.mjs`).
19+
20+
**What the command actually did.** `os generate schema` wrote
21+
`objectstack.schema.json`, a JSON Schema of the whole stack definition for an editor
22+
to check `objectstack.config.ts` against. It projected `ObjectStackDefinitionSchema`
23+
through a bare `z.toJSONSchema`, so the refinements the platform enforces beyond the
24+
shape — a non-blank string, a required one-of, a banned key — were missing from the
25+
file. Measured against the published projection on the tree the ruling was made on,
26+
the file lacked 874 keywords, every one of them an absence. An editor pointed at it
27+
reported a config as valid, and the platform then refused that config.
28+
29+
**Why it is retired rather than repaired.** A TypeScript configuration is typed by its
30+
own `define*` helper, and `objectstack.config.ts` is typed end to end by
31+
`defineStack`, so no config format this CLI loads is one an editor validates against
32+
a JSON Schema. JSON metadata already has the per-type schemas `@objectstack/spec`
33+
publishes, which carry the published projection. Repairing the command would have
34+
added a permanent public export to `@objectstack/spec` for a file with no reader. The
35+
ruling generates no replacement file. What you see now:
36+
37+
```
38+
✗ `os g schema` was retired — its JSON Schema passed configs the platform refuses (maintainer ruling).
39+
40+
The file it wrote described only the shape of a stack. Every rule the
41+
platform enforces beyond that shape — a non-blank string, a required
42+
one-of, a banned key — was missing from it, so an editor showed a config
43+
as valid and the platform then refused it. By maintainer ruling it is
44+
retired, not repaired, and no replacement file is generated.
45+
46+
Check a project against the rules that actually run:
47+
48+
os validate
49+
50+
For JSON metadata, point your editor at the per-type schemas that
51+
@objectstack/spec publishes. They state the rules a JSON Schema can
52+
express, and name the ones it cannot under `x-dropped-refinements`:
53+
54+
node_modules/@objectstack/spec/json-schema/<category>/<Type>.json
55+
56+
`objectstack.config.ts` needs neither: `defineStack` types it in your
57+
editor. Delete the `os generate schema` call, and any editor setting
58+
that maps `objectstack.schema.json` — nothing writes that file now.
59+
60+
Docs: https://objectstack.ai/docs/deployment/cli
61+
```
62+
63+
**What to do.** The refusal's pointer is the whole of it. Delete the
64+
`os generate schema` call, and any editor setting that maps `objectstack.schema.json`
65+
(a `json.schemas` or `yaml.schemas` entry, for example). Run `os validate` to check a
66+
project against the rules that actually run. For JSON metadata, point the editor at
67+
`node_modules/@objectstack/spec/json-schema/`, one file per metadata type. There is no
68+
call to rename: nothing replaces the command.
69+
70+
**Reach outside this repository is NOT MEASURED.** There is no telemetry, so whether
71+
any project runs the command or reads its file is unknown. Inside this repository
72+
nothing does: no reader and no editor mapping of `objectstack.schema.json` exists.
73+
If these release notes also record that `os generate schema` can now write its file,
74+
this retirement supersedes that repair.
75+
76+
**Two neighbouring answers change with it.** The retirement ledger is now read before
77+
the command routes its sub-commands and before it asks for a `<name>`, which is what
78+
lets `os generate schema` (no name) reach the refusal at all. So `os g agent` with no
79+
name now prints the agent retirement instead of `Missing required argument: <name>`.
80+
The ledger lookup also reads its own keys only: `os g constructor <name>` used to be
81+
taken for a retired type and crashed with a `TypeError`, and it now falls through to
82+
the ordinary type checks.
83+
84+
The docs row that advertised the command — "Autocomplete and validation for
85+
`objectstack.config.ts` (via `os generate schema`)" in
86+
`content/docs/api/data-flow.mdx` — is gone, with the diagram's JSON Schema node above
87+
it.

‎content/docs/api/data-flow.mdx‎

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -181,10 +181,8 @@ flowchart LR
181181
end
182182
183183
subgraph "Build Time"
184-
C -->|"generate"| D[JSON Schema]
185184
C -->|"generate"| E[TypeScript Types]
186-
D -->|"feed"| F[IDE Autocomplete]
187-
E -->|"feed"| F
185+
E -->|"feed"| F[IDE Autocomplete]
188186
end
189187
190188
subgraph "Runtime"
@@ -198,7 +196,6 @@ flowchart LR
198196

199197
| Output | Used By | Purpose |
200198
|:---|:---|:---|
201-
| JSON Schema | VS Code, IntelliJ | Autocomplete and validation for `objectstack.config.ts` (via `os generate schema`) |
202199
| TypeScript Types | Plugin developers | Type-safe access to object definitions |
203200
| Manifest | Kernel | Runtime metadata for query validation and execution |
204201
| Metadata API | Client SDK | Dynamic object/field discovery |

‎content/docs/deployment/cli.mdx‎

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1461,6 +1461,24 @@ third-party extension primitive, authored as `src/skills/<name>.skill.ts` with
14611461
`os g skill <name>`, which writes exactly that path.
14621462
</Callout>
14631463
1464+
<Callout type="warn" title="`os generate schema` is retired">
1465+
There is no `schema` type. Running `os generate schema` (or `os g schema`) fails
1466+
and writes nothing, not even `objectstack.schema.json`. The message says it was
1467+
retired by maintainer ruling and points at [`os validate`](#os-validate) and the
1468+
per-type schemas `@objectstack/spec` publishes, rather than the generic "unknown
1469+
type" listing. No replacement file is generated.
1470+
1471+
The file it wrote described only the shape of a stack. Every rule the platform
1472+
enforces beyond that shape (a non-blank string, a required one-of, a banned key)
1473+
was missing from it, so an editor showed a config as valid that the platform then
1474+
refused. `objectstack.config.ts` needs no JSON Schema: `defineStack` types it in
1475+
your editor. Check a project against the rules that actually run with
1476+
`os validate`. For JSON metadata, point your editor at
1477+
`node_modules/@objectstack/spec/json-schema/<category>/<Type>.json`, which states
1478+
the rules a JSON Schema can express and names the rest under
1479+
`x-dropped-refinements`.
1480+
</Callout>
1481+
14641482
**Options:**
14651483
- `-d, --dir <directory>` — Override target directory
14661484
- `--dry-run` — Preview without writing files

0 commit comments

Comments
 (0)