From ca4533a926dbe16e8f3c1e9caa22935fae4eb3d0 Mon Sep 17 00:00:00 2001 From: Jack Zhuang <277994282+os-zhuang@users.noreply.github.com> Date: Mon, 20 Jul 2026 00:56:40 +0800 Subject: [PATCH] fix(cli): correct `os explain object` ownership values (#3244) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `os explain object` documented the object `ownership` field as the package contribution kind (`"own" | "extend"`, ObjectOwnershipEnum set via registerObject). The real ObjectSchema.ownership field is the record-ownership model — `z.enum(['user', 'org', 'none'])` — a distinct concept the spec explicitly warns not to conflate. The explain catalog now prints the correct allowed values and description. Adds a regression test that pins the documented values to the record-ownership enum so the two concepts can't drift back together. Closes #3244 Co-Authored-By: Claude Opus 4.8 --- .changeset/fix-explain-ownership-values.md | 21 +++++++++++++++++++++ packages/cli/src/commands/explain.ts | 4 ++-- packages/cli/test/commands.test.ts | 21 ++++++++++++++++++++- 3 files changed, 43 insertions(+), 3 deletions(-) create mode 100644 .changeset/fix-explain-ownership-values.md diff --git a/.changeset/fix-explain-ownership-values.md b/.changeset/fix-explain-ownership-values.md new file mode 100644 index 0000000000..4d7d48f0d1 --- /dev/null +++ b/.changeset/fix-explain-ownership-values.md @@ -0,0 +1,21 @@ +--- +"@objectstack/cli": patch +--- + +fix(cli): `os explain object` documented `ownership` with the wrong allowed values (#3244) + +The schema catalog described the object `ownership` field as the package +*contribution* kind (`"own" | "extend"`, the `ObjectOwnershipEnum` set via +`registerObject`). But `ObjectSchema.ownership` is the **record-ownership +model** — `z.enum(['user', 'org', 'none'])` — a distinct concept the spec +explicitly warns not to conflate despite the shared word. + +`os explain object` now prints: + + ownership 'user' | 'org' | 'none' Record-ownership model: user (default, + injects a reassignable owner_id) | org | none (no per-record owner). + Distinct from the package own/extend contribution kind. + +A regression test (`packages/cli/test/commands.test.ts`) pins the documented +values to the record-ownership enum so the two concepts can't drift back +together. Found during the #1880 docs implementation-accuracy audit. diff --git a/packages/cli/src/commands/explain.ts b/packages/cli/src/commands/explain.ts index 59d3d2bf12..2834d39923 100644 --- a/packages/cli/src/commands/explain.ts +++ b/packages/cli/src/commands/explain.ts @@ -22,7 +22,7 @@ interface SchemaInfo { docsPath: string; } -const SCHEMAS: Record = { +export const SCHEMAS: Record = { object: { name: 'Object', description: 'Defines a data entity in the ObjectStack data model. Objects contain fields, enable capabilities, and form the foundation of the metadata-driven platform.', @@ -34,7 +34,7 @@ const SCHEMAS: Record = { { name: 'label', type: 'string', description: 'Human-readable display name' }, { name: 'pluralLabel', type: 'string', description: 'Plural display name' }, { name: 'description', type: 'string', description: 'Documentation for the object' }, - { name: 'ownership', type: '"own" | "extend"', description: 'Whether this object is owned or extended' }, + { name: 'ownership', type: "'user' | 'org' | 'none'", description: 'Record-ownership model: user (default, injects a reassignable owner_id) | org | none (no per-record owner). Distinct from the package own/extend contribution kind.' }, { name: 'enable', type: 'ObjectCapabilities', description: 'Feature flags (trackHistory, apiEnabled, etc.)' }, { name: 'icon', type: 'string', description: 'Icon identifier for UI display' }, ], diff --git a/packages/cli/test/commands.test.ts b/packages/cli/test/commands.test.ts index c80429bfee..c694c159a1 100644 --- a/packages/cli/test/commands.test.ts +++ b/packages/cli/test/commands.test.ts @@ -11,7 +11,7 @@ import Info from '../src/commands/info'; import Generate from '../src/commands/generate'; import Lint from '../src/commands/lint'; import Diff from '../src/commands/diff'; -import Explain from '../src/commands/explain'; +import Explain, { SCHEMAS } from '../src/commands/explain'; describe('CLI Commands (oclif)', () => { it('should have compile command', () => { @@ -67,3 +67,22 @@ describe('CLI Commands (oclif)', () => { expect(Explain.description).toContain('explanation'); }); }); + +describe('os explain — schema catalog accuracy', () => { + // Regression guard for #3244: `os explain object` used to document the + // `ownership` field as the package-contribution kind (`"own" | "extend"`), + // which is a DISTINCT concept (`ObjectOwnershipEnum`, set via registerObject). + // The real `ObjectSchema.ownership` field is the record-ownership model — + // `z.enum(['user','org','none'])` — see packages/spec/src/data/object.zod.ts. + it('documents object.ownership as the record-ownership model, not the own/extend contribution kind (#3244)', () => { + const ownership = SCHEMAS.object.optional.find((f) => f.name === 'ownership'); + expect(ownership, 'object schema should document an `ownership` field').toBeDefined(); + + // The type string must enumerate exactly the record-ownership enum values. + const tokens = (ownership!.type.match(/'[^']+'|"[^"]+"/g) ?? []).map((t) => t.slice(1, -1)); + expect(new Set(tokens)).toEqual(new Set(['user', 'org', 'none'])); + + // …and must never regress back to the contribution-kind values. + expect(ownership!.type).not.toBe('"own" | "extend"'); + }); +});