fix(cli)!: retire os generate schema — it refuses, and points at os validate and the published per-type schemas (#19098) - #20266
Conversation
…s validate` and the published per-type schemas Maintainer ruling on #19098 (comment 5856790152, letter C, confirmed 5856865990). `schema` joins RETIRED_GENERATORS in the `os g agent` shape. The retirement ledger is now read in Generate.run before sub-command routing and before the <name> requirement, as an own-key read; before this, neither `os generate schema` (routed, nameless) nor `os g agent` without a name could reach it. The three z.toJSONSchema rungs over ObjectStackDefinitionSchema, their ladder notices and the known-unsupported predicate are deleted with runSchemaGeneration. The e2e pin of the written file is replaced by the retirement pin. The data-flow docs row that advertised the command, and its diagram node, are removed. Claude-Session: https://claude.ai/code/session_01UYBdGBzWSrAMzpW8ah3GbP Co-authored-by: Claude <noreply@anthropic.com>
…tire-generate-schema
…he current object template The control copied the agent pin's `import * as Data` assertion, which the object template stopped emitting in 0bd1126. It now asserts the previewed file path and the `@objectstack/spec/data` import source. Claude-Session: https://claude.ai/code/session_01UYBdGBzWSrAMzpW8ah3GbP Co-authored-by: Claude <noreply@anthropic.com>
📓 Docs Drift CheckThis PR changes 1 package(s): 3 hand-written doc(s) NAME something this change touched and may need an implementation-accuracy re-verification:
⛔ 1 release-owned page(s) also name something this change touched. These are read-only:
What this run could not see
Coarse fallback — 25 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): Which tree this was computed onThis run read A worktree cut from an older # while this PR is open — GitHub drops the merge commit once it closes
git fetch origin 39a73531da06c15ef3a1ca8e11fd2aa1239f1435 && git checkout 39a73531da06c15ef3a1ca8e11fd2aa1239f1435
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin 3f86dc52f22668dd92de00f604148e04d3d048da 2cb9e9613fb244c880a81303a252176ce2e67e6b && git checkout -B drift-repro 3f86dc52f22668dd92de00f604148e04d3d048da && git merge --no-ff 2cb9e9613fb244c880a81303a252176ce2e67e6b
node scripts/docs-audit/affected-docs.mjs --json 3f86dc52f22668dd92de00f604148e04d3d048da
|
Contract reviewServed-tier: ① Derived judgments
② Semver level
It is the same triple and marker category as ③ Boundary flags
Implemented-by: Independence: INDEPENDENT AGENT (fed the card, the ruling and the PR only; not the dispatch order or the seat's conclusions) VERDICT: PASS |
…tire-generate-schema
…control reads the template; cli.mdx retirement callout The retirement pin was nightly-only (`.e2e`). generate-refuses-retired- generator.test.ts is the per-PR spawn guard: `os generate schema` and `os g schema -o FILE` exit 1, write nothing and answer the retirement (not a missing name, not an unknown type); `os g constructor NAME` is an own-key miss with no TypeError; `os g object --dry-run` is the control. The live-generator control in generate-agent-retired.e2e.test.ts asserted a copied `import * as Data` line the object template stopped emitting in 0bd1126. Both controls now assert the preview equals what the exported object template emits. content/docs/deployment/cli.mdx gains the `os generate schema` callout beside the `os g agent` one, pointing at `os validate` and the per-type schemas. Claude-Session: https://claude.ai/code/session_01UYBdGBzWSrAMzpW8ah3GbP Co-authored-by: Claude <noreply@anthropic.com>
…tirement callout The retirement pin was nightly-only (`.e2e`). generate-refuses-retired- generator.test.ts is the per-PR spawn guard: `os generate schema` and `os g schema -o FILE` exit 1, write nothing and answer the retirement (not a missing name, not an unknown type); `os g constructor NAME` is an own-key miss with no TypeError; `os g object --dry-run` is the control, asserting the preview equals what the exported object template emits. content/docs/deployment/cli.mdx gains the `os generate schema` callout beside the `os g agent` one, pointing at `os validate` and the per-type schemas. Claude-Session: https://claude.ai/code/session_01UYBdGBzWSrAMzpW8ah3GbP Co-authored-by: Claude <noreply@anthropic.com>
…the ObjectSchema.create shape (#20270) `generate-agent-retired.e2e.test.ts` asserted the pre-#20195 object template line `import * as Data from '@objectstack/spec/data'`, which the template stopped emitting in 0bd1126, so the nightly e2e tier was red on main. The control now asserts the template's current shape: `ObjectSchema` among the named imports from `@objectstack/spec/data`, and the `ObjectSchema.create({` call, not one exact import line. The case is neither skipped nor quarantined. Claude-Session: https://claude.ai/code/session_01UYBdGBzWSrAMzpW8ah3GbP Co-authored-by: Claude <noreply@anthropic.com>
The pushed head a2dda36 carried the per-PR pin, the cli.mdx callout and a first version of the stale-control fix in one commit. The line merged here carries them split, with the stale-control fix as its own commit a0cec66 (#20270). Conflict in generate-agent-retired.e2e.test.ts resolved to that commit's ObjectSchema.create shape assertion; the merged tree equals a0cec66's tree. Claude-Session: https://claude.ai/code/session_01UYBdGBzWSrAMzpW8ah3GbP Co-authored-by: Claude <noreply@anthropic.com>
Contract reviewServed-tier: Delta of: ① Derived judgments
② Semver level
The #20270 rider is test-only, in
Check Changeset is green on the head. Confirmed: the rider owes no changeset of its own. ③ Boundary flags
Implemented-by: Independence: INDEPENDENT AGENT (fed the cards, the prior review and the PR only; not the dispatch order or the seat's conclusions) VERDICT: PASS |
Fixes #19098
Fixes #20270
Clause-②: no (narrowing)
Executes maintainer ruling
5856790152on #19098 (batch #227 item 5, letter C, maintainer 「同意」, confirmed5856865990):os generate schemais retired, not repaired. No file is generated in its place, and nothing inpackages/specmoves (ruling item 4).What changed
packages/cli/src/commands/generate.tsRETIRED_GENERATORSgainsschemain theos g agentshape. The refusal reads "os g schemawas retired" and cites the maintainer ruling. It namesos validate(the real parse:ObjectStackDefinitionSchema.safeParse), and names the per-type schemas@objectstack/specpublishes atnode_modules/@objectstack/spec/json-schema/CATEGORY/TYPE.json, which carry the published projection plusx-dropped-refinements. It exits 1.check:doc-authoring's cross-package prose-id leg ratchets#NNNtokens inpackages/**strings, andgenerate.tshas no ledger row).runSchemaGeneration, with its threez.toJSONSchema(ObjectStackDefinitionSchema…)rungs and both ladder notices;KNOWN_UNSUPPORTED_JSON_SCHEMA_PATTERNS;isKnownUnsupportedJsonSchema; and thecase 'schema'route.-o,--dry-runand--formatservetypes,clientandmigration.zandObjectStackDefinitionSchemawere dynamic imports inside the deleted function.zodstays a dependency, used bycompile,validateandformat. Thetypeargument's help text derives fromGENERATORSand never listedschema.runMetadataGeneration, which runs only after two earlier steps: the sub-commandswitch(whereschemareturned first) and the NAME requirement (where a nameless call stopped). So aschemaentry alone could never be reached fromos generate schema.0d3ec4713on the dev runner,os g agentwith no name printedMissing required argumentand exited 1.Generate.run, throughrefuseRetiredGenerator, as an own-key read (the package'sObject.prototype.hasOwnProperty.callidiom).os g agentwith no name now prints the agent retirement.os g constructor NAMEused to print "os g constructorwas retired — undefined" and crash withTypeError: 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 theagentprecedent's shape: a real child process throughbin/run-dev.jsplus tsx, with assertions on stdout content and the exit status. It covers:os generate schema(no name) andos g schema -o custom.schema.jsonboth exit 1 and write nothing (directory listing[]);os validate, and@objectstack/spec/json-schema/;os g agent(no name) answers the agent retirement;os g constructor thingis not taken for a retired type;os g object customer --dry-runstill previews.packages/cli/test/generate-schema-writes-json-schema.e2e.test.ts(the cli:os generate schemacan never succeed —z.toJSONSchema(ObjectStackDefinitionSchema)throws in BOTH io directions, so the published IDE schema it exists to write is never written #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.git grepforgenerate schema,runSchemaGeneration,objectstack.schema.jsonandisKnownUnsupportedJsonSchemaoverpackages/cli/srcandpackages/cli/testfinds only the deleted file. The ladder PR (fix(cli): os generate schema falls back like every other toJSONSchema call site, so the IDE schema it exists to write is written #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'sintegrationproject. It pins:os generate schemaandos g schema -o custom.schema.json: exit 1, nothing written, the retirement answered (neither "Missing required argument" nor "Unknown type:"), naming the ruling,os validateand@objectstack/spec/json-schema/;os g constructor thing: an own-key miss (exit 1, not "was retired", noTypeError, nothing written);os g object customer --dry-runexits 0 and previews exactly what the exported object template emits.packages/cli/test/generate-agent-retired.e2e.test.ts(cli nightly e2e:generate-agent-retired.e2e.test.tsexpects the pre-#20195 object template import (import * as Data) and fails on main #20270, its own commita0cec66e9): the live-generator control assertedimport * as Data from '@objectstack/spec/data', which the object template stopped emitting in0bd11261e, so this nightly case was red on main. It now asserts the template's current shape,ObjectSchemaamong the named imports from@objectstack/spec/dataplus theObjectSchema.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:content/docs/api/data-flow.mdx:200, the JSON Schema row ("Autocomplete and validation forobjectstack.config.ts(viaos generate schema)")content/docs/references/api/plugin-rest-api.mdx:107generateSchemasoption, in an auto-generated reference; it is not the commandcontent/docs/references/api/plugin-rest-api.mdx:282D[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, andTypeScript Typesnow feedsIDE Autocompletealone.content/docs/deployment/cli.mdx(patch round 1) gains a warn-type Callout, "os generate schemais retired", in theos generatesection beside theos g agentone, the same shape as the two prior CLI retirements. It points atos validate(linked to the page's ownos validateheading) and at the per-type schemas@objectstack/specpublishes. The refusal'sDocs:link lands on this page. The page never listed aschemasub-type, so nothing else on it changes.packages/cli/README.mdcarries only the genericos generaterow;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 andClause-②: no (narrowing);os g agentwording (not-required (no-migration-prescription));objectstack.schema.json, runos validate, and point editors at the per-type schemas.Verification, patch round 1 (at HEAD
2cb9e9613unless stated)origin/main17bd31877merged by merge as9f702ee94(scripts/pm/os-regen-merge.sh). No incoming file is one of this PR's.node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack --commandsderived 93 commands: round 0's 92 pluspnpm check:cli-examples-parity, from thecli.mdxedit. All 93 exited 0 at2cb9e9613.--ranwith the recorded exit codes reads "93 derived, 93 run, 0 NOT-MEASURED, 0 UNRUN" (a derived zero).pnpm lint: exit 0 (118 s);node scripts/check-issue-citations.mjs --base origin/main(ae8e3ca01): exit 0;pnpm check:docs-audit-scopeandnode scripts/docs-audit/check-affected-docs.mjs: exit 0 each;pnpm check:docs-transcript-driftandpnpm check:cli-examples-parity: exit 0 each;pnpm --filter @objectstack/cli typecheck:VERDICT command-exit 0, with the new pin inside thetsconfig.test.jsonprogram (--listFilesOnly: 1 hit).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'sintegrationpopulation (54 to 55 files) and is absent from the nightly population. The partition pintest/vitest-tiers-partition.test.tspasses 22/22.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 cli nightly e2e:generate-agent-retired.e2e.test.tsexpects the pre-#20195 object template import (import * as Data) and fails on main #20270 case.generate-agent-retired.e2e.test.tsexpects the pre-#20195 object template import (import * as Data) and fails on main #20270. The two new assertions accept the current emission: through the exported template, theObjectSchemaimport matches and thecreatecall matches. They reject the pre-fix(cli): os init and os generate object declare the scaffolded object with ObjectSchema.create #20195 emission taken from0bd11261e^: neither matches.a2dda36c2.generate.tsand the per-PR file are byte-identical at2cb9e9613.schemaledger key renamed toschema_ablated62cb7ec8b8edto7a1f27dd8cfb; grepschema-entry=0 ablated-entry=1os validateand the per-type schemas"62cb7ec8b8ed,git diff HEADemptyRETIRED_GENERATORS[args.type]26ec87322f84; grepown-key=0 bracket=1os g constructor thingcaseAfter both legs, the restored per-PR run passed 7/7.
generate-agent-retired.e2e.test.tsexpects the pre-#20195 object template import (import * as Data) and fails on main #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 toa0cec66e9's). Nothing pushed was discarded.Verification, round 0 (at HEAD
c1036ebb9unless stated)node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack --commandsderived 92 commands, and all 92 exited 0 atc1036ebb9.--ranwith the recorded exit codes reads "92 derived, 92 run, 0 NOT-MEASURED, 0 UNRUN" (a derived zero).check-adr-0087-registration --base origin/mainreads[BREAKING+clause-②-narrowing] not-required (no-migration-prescription).check-changeset-no-majorreads "nomajorbump". 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 istsc --noEmitpluscheck:test-typecheck, and the new pin is inside thetsconfig.test.jsonprogram (--listFilesOnly: 1 hit).node scripts/check-issue-citations.mjs --base origin/main(a9fb83ef0) exits 0: 73 citations judged, 71 resolve, 2 cross-repo.unitlayer, run at merge commit5c7c5bee3:pnpm --filter @objectstack/cli exec vitest run --project unit --maxWorkers=2ran 229 files: 227 passed and 2 failed as prerequisite refusals ("packages/cli is not built").c1036ebb9differs from5c7c5bee3only in the new pin, which is not in the unit population.*.e2e.test.tslike theagentprecedent, so it is nightly-tier: the per-PR queue run does not select it.OS_TEST_TIERS=nightly pnpm --filter @objectstack/cli exec vitest run --project integration --maxWorkers=2over the new pin plusgenerate-agent-retired,generate-skillandgenerate-object-namespace-prefix.generate-agent-retired.e2e.test.ts(see Acceptance notes); the new pin is 10/10.os generate schema -o out.jsonwent from exit 0 and a 3,335,734-byte file written to exit 1, the refusal, and nothing written;os g agentwent from "Missing required argument" to the agent retirement;os g constructor foowent from aTypeErrorcrash to the ordinary refusal.Ablation. This is a one-shot proof, run against the committed state
c1036ebb9throughscripts/ablation-replace.mjsunder a script-leveltraprestore. The subject resolves fromsrc/via tsx, so no dist is involved.schemaledger key renamed toschema_ablated(the lookup misses it)62cb7ec8b8edto7a1f27dd8cfb; grepschema-entry=0 ablated-entry=1os validate, per-type schemas62cb7ec8b8ed;git diff HEADemptyRETIRED_GENERATORS[args.type]26ec87322f84; grepown-key=0 bracket=1constructorown-keys assertionIn 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
agentprecedent does.Acceptance notes
generate-agent-retired.e2e.test.tsexpects the pre-#20195 object template import (import * as Data) and fails on main #20270, commita0cec66e9): the live-generator control inpackages/cli/test/generate-agent-retired.e2e.test.tsassertedimport * as Data from '@objectstack/spec/data', which the object template stopped emitting in0bd11261e.packages/clifor pre-fix(cli): os init and os generate object declare the scaffolded object with ObjectSchema.create #20195 template text (import * as Data from '@objectstack/spec/data',Data.ServiceObject). That line was the only nightly hit; the control, the same sweep at9f702ee94, finds it.scaffold-emission-typechecks.test.ts(a tsc canary fixture) andscaffold-object-declaration-shape.test.ts(it refuses the pre-ruling literal)..changeset/sour-moons-smile.md, pending and unreleased, records the fix(cli): os generate schema falls back like every other toJSONSchema call site, so the IDE schema it exists to write is written #17903 ladder repair this PR deletes. It is left unchanged.check-empty-changeset.mjsclasses as a DELIBERATE CORRECTION (fix(scripts): the foreign-changeset refusal prescribes restoring a release note the same PR made false — name the second class (ruling D on #17712) #18160, ruling D on finding: random changeset filenames collide silently across parallel agents — a round overwrote a sibling PR'sminorchangeset and every gate stayed green #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".packages/spec/scripts/lib/refinement-projection.ts(the "Producers outsidepackages/spec's own artifacts" bullet) still names the CLI'sos generateas a directz.toJSONSchemaproducer. It goes stale once this lands, butpackages/specis untouched by ruling item 4.GENERATORS[type]lookup inrunMetadataGenerationis still an inherited-key read. Soos g constructor NAMEanswers "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.objectstack.schema.json.content/docs/protocol/diagram.mdx:243-255carries aD[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/specstill publishes, so it is left unchanged.Generated by Claude Code