Skip to content

fix(spec): align config-schema.json object ownership with the record-ownership vocabulary - #7436

Merged
os-help merged 1 commit into
mainfrom
claude/issue-7286-config-schema-ownership
Aug 10, 2026
Merged

fix(spec): align config-schema.json object ownership with the record-ownership vocabulary#7436
os-help merged 1 commit into
mainfrom
claude/issue-7286-config-schema-ownership

Conversation

@os-help

@os-help os-help commented Aug 10, 2026

Copy link
Copy Markdown
Collaborator

Fixes #7286

The defect

packages/spec/config-schema.json:67-69 declared the object-level ownership property as enum ["own", "shared"]. That matched neither vocabulary in the package:

Surface Vocabulary Where
Record-ownership model (the object-schema key) user / business_unit / org / none packages/spec/src/data/object.zod.ts:1402 (post-#7260)
Package contribution kind own / extend / overlay registry contributor record, set via registerObject
The artifact (before this PR) own / shared matches neither — a stale third spelling

shared appears in no acceptance face anywhere in the package. Since this artifact is hand-written authoring guidance served to config authors as IDE autocomplete/validation, the entry actively steered an author toward ownership: "shared" — a value no schema accepts. This is the #3244 confusion class exactly: same key name, different vocabularies, adjacent surfaces.

The fix

Aligns the enum to the acceptance face and adopts that face's own .describe() text, whose closing sentence carries the own/extend disambiguation that caused the drift in the first place:

"ownership": {
  "type": "string",
  "enum": ["user", "business_unit", "org", "none"],
  "description": "Record-ownership model: user (default — injects reassignable owner_id plus owning_business_unit_id) | business_unit (unit-owned: owning_business_unit_id only, no owner_id) | org | none (no per-record owner, neither anchor). Distinct from the package own/extend contribution kind."
}

The acceptance face is untouched. packages/spec/src/data/object.zod.ts is byte-identical to origin/main (git diff --quiet origin/main -- packages/spec/src/data/object.zod.ts passes). The whole diff is 2 lines in one hand-written JSON artifact.

Measurements behind the route

The dispatch carried a two-branch decision rule; branch A was selected by measurement — the zod acceptance face does declare an object-level ownership, so the artifact entry is aligned rather than removed:

packages/spec/src/data/object.zod.ts:1402
  ownership: z.enum(['user', 'business_unit', 'org', 'none'], {
    error:
      "`ownership` is the record-ownership model — one of 'user' (default) | 'business_unit' | 'org' | 'none'. " +
      "The package-contribution kind 'own'/'extend' is set via registerObject, not on the object schema.",
  }).optional().describe(...)

Mechanical set-difference before and after (scan script over the artifact's parsed JSON vs. the zod enum literal):

### objects[].ownership   BEFORE
  artifact (2): ["own","shared"]
  accepted (4): ["user","business_unit","org","none"]
  PHANTOM (artifact offers, schema rejects) [2]: ["own","shared"]
  MISSING (schema accepts, artifact hides)  [4]: ["user","business_unit","org","none"]

### objects[].ownership   AFTER
  PHANTOM [0]: []
  MISSING [0]: []

Three mechanism hypotheses were checked before editing; all three hold:

  • Hand-written, not generated — no script writes this path. The repo's only mentions of config-schema.json are prose in a code comment, CHANGELOG.md and a changeset. Note the CLI does have a correct generated route (objectstack generate schema builds the config JSON Schema live from ObjectStackDefinitionSchema via z.toJSONSchema), but it writes objectstack.schema.json in the user's cwd, not this file.
  • Vocabulary is exactly user/business_unit/org/none — verified at origin/main.
  • No pin/sync test guards this artifact — nothing in the repo reads it; check:generated does not cover it. Filed as an out-of-scope finding rather than built here (S-card scope).

Verification

  • node -e "JSON.parse(...)" — parses as valid JSON.
  • pnpm --filter @objectstack/spec typecheck — green (tsc, scripts-typecheck, test-typecheck all pass).
  • pnpm --filter @objectstack/spec test366 files / 9557 tests passed.
  • node scripts/check-nul-bytes.mjs — OK, 6803 files scanned; targeted control-byte self-scan of the edited file clean.

No changeset — measured, not assumed

config-schema.json is not published. npm pack --dry-run --ignore-scripts on @objectstack/spec packs 269 entries and zero match config-schema; the only root-level JSON entries shipped are package.json and spec-changes.json. It is absent from the package's files list. Nothing user-visible ships, so this carries the skip-changeset label instead of a changeset.

Out-of-scope findings (reported, deliberately NOT fixed here)

A sweep of every enum in the artifact found two siblings of the same class. Neither is the identical defect with an identical mechanical fix, so both are left for separate triage rather than widened into this diff:

  1. objects[].fields.*.type offers 6 phantom field typesinteger, slug, uuid, ip_address, geo_point, encrypted are in the artifact but not in FieldType (packages/spec/src/data/field.zod.ts:17); and 21 real types are hidden, including secret, toggle, radio, checkboxes, tree, user, composite, repeater, record, location, address, code, tags. Aligning it is a 34-value list becoming a 49-value list, and whether the artifact deliberately offers a curated subset is a judgement call — not mechanical.
  2. data[].mode hides update — artifact has ["upsert","insert","ignore","replace"], SeedMode (packages/spec/src/data/seed.zod.ts:12) has 5 including update. Different shape from this card's defect: it hides a valid value rather than offering an invalid one, so no author is steered wrong.

Also noted: the artifact's own top-level description claims it is "Generated from ObjectStackDefinitionSchema", which is false — that false provenance claim is plausibly why nobody re-derived it as the vocabularies moved.


Generated by Claude Code

…d-ownership vocabulary

`packages/spec/config-schema.json` declared the object-level `ownership`
property as `enum ["own", "shared"]`. That matched neither vocabulary in the
package: the record-ownership model is `user | business_unit | org | none`
(`packages/spec/src/data/object.zod.ts`, post-#7260), and the package
*contribution* kind is own/extend/overlay and lives on the registry's
contributor record, not on the object schema. `["own","shared"]` was a stale
third spelling with no acceptance face behind it.

This artifact is hand-written authoring guidance served to config authors as
IDE autocomplete/validation, so the entry actively steered an author toward
`ownership: "shared"` — a value no schema in the package accepts (#3244
confusion class: same key name, different vocabularies, adjacent surfaces).

Aligns the enum to the acceptance face and adopts its `.describe()` text,
whose closing sentence carries the own/extend disambiguation that caused the
drift. The acceptance face itself is untouched — `object.zod.ts` is
byte-identical to `origin/main`.

Fixes #7286
@vercel

vercel Bot commented Aug 10, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

1 Skipped Deployment
Project Deployment Actions Updated (UTC)
objectstack Ignored Ignored Aug 10, 2026 11:06am

Request Review

@github-actions

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

This PR changes 1 package(s): @objectstack/spec.

106 hand-written doc(s) reference the affected code and may need an implementation-accuracy re-verification:

  • content/docs/ai/agents.mdx (via @objectstack/spec)
  • content/docs/ai/skills-reference.mdx (via @objectstack/spec)
  • content/docs/ai/skills.mdx (via @objectstack/spec)
  • content/docs/api/client-sdk.mdx (via @objectstack/spec)
  • content/docs/api/environment-routing.mdx (via @objectstack/spec)
  • content/docs/api/error-catalog.mdx (via @objectstack/spec)
  • content/docs/api/error-handling-client.mdx (via @objectstack/spec)
  • content/docs/api/error-handling-server.mdx (via @objectstack/spec)
  • content/docs/api/index.mdx (via @objectstack/spec)
  • content/docs/automation/approvals.mdx (via @objectstack/spec)
  • content/docs/automation/connectors.mdx (via @objectstack/spec)
  • content/docs/automation/flows.mdx (via @objectstack/spec)
  • content/docs/automation/hook-bodies.mdx (via packages/spec)
  • content/docs/automation/hooks.mdx (via @objectstack/spec)
  • content/docs/automation/index.mdx (via @objectstack/spec)
  • content/docs/automation/webhooks.mdx (via @objectstack/spec)
  • content/docs/automation/workflows.mdx (via @objectstack/spec)
  • content/docs/concepts/architecture.mdx (via @objectstack/spec)
  • content/docs/concepts/design-principles.mdx (via packages/spec)
  • content/docs/concepts/index.mdx (via @objectstack/spec)
  • content/docs/concepts/metadata-driven.mdx (via @objectstack/spec)
  • content/docs/concepts/metadata-lifecycle.mdx (via packages/spec)
  • content/docs/concepts/north-star.mdx (via @objectstack/spec)
  • content/docs/data-modeling/analytics.mdx (via @objectstack/spec)
  • content/docs/data-modeling/drivers.mdx (via @objectstack/spec)
  • content/docs/data-modeling/external-datasources.mdx (via @objectstack/spec)
  • content/docs/data-modeling/field-types.mdx (via @objectstack/spec)
  • content/docs/data-modeling/fields.mdx (via @objectstack/spec)
  • content/docs/data-modeling/formulas.mdx (via @objectstack/spec)
  • content/docs/data-modeling/index.mdx (via @objectstack/spec)
  • content/docs/data-modeling/objects.mdx (via @objectstack/spec)
  • content/docs/data-modeling/queries.mdx (via @objectstack/spec)
  • content/docs/data-modeling/schema-design.mdx (via @objectstack/spec)
  • content/docs/data-modeling/seed-data.mdx (via @objectstack/spec)
  • content/docs/data-modeling/validation-rules.mdx (via @objectstack/spec)
  • content/docs/data-modeling/validation.mdx (via @objectstack/spec)
  • content/docs/deployment/cli.mdx (via @objectstack/spec)
  • content/docs/deployment/tenancy-modes.mdx (via @objectstack/spec)
  • content/docs/deployment/troubleshooting.mdx (via @objectstack/spec)
  • content/docs/deployment/validating-metadata.mdx (via @objectstack/spec)
  • content/docs/getting-started/build-with-claude-code.mdx (via @objectstack/spec)
  • content/docs/getting-started/common-patterns.mdx (via @objectstack/spec)
  • content/docs/getting-started/examples.mdx (via @objectstack/spec)
  • content/docs/getting-started/quick-reference.mdx (via @objectstack/spec)
  • content/docs/getting-started/quick-start.mdx (via @objectstack/spec)
  • content/docs/getting-started/your-first-project.mdx (via @objectstack/spec)
  • content/docs/kernel/cluster.mdx (via @objectstack/spec)
  • content/docs/kernel/contracts/auth-service.mdx (via packages/spec)
  • content/docs/kernel/contracts/cache-service.mdx (via packages/spec)
  • content/docs/kernel/contracts/data-engine.mdx (via @objectstack/spec)
  • content/docs/kernel/contracts/index.mdx (via @objectstack/spec)
  • content/docs/kernel/contracts/metadata-service.mdx (via packages/spec)
  • content/docs/kernel/contracts/storage-service.mdx (via @objectstack/spec)
  • content/docs/kernel/index.mdx (via packages/spec)
  • content/docs/kernel/runtime-services/data-service.mdx (via @objectstack/spec)
  • content/docs/kernel/runtime-services/email-service.mdx (via packages/spec)
  • content/docs/kernel/runtime-services/examples.mdx (via @objectstack/spec)
  • content/docs/kernel/runtime-services/index.mdx (via packages/spec)
  • content/docs/kernel/runtime-services/queue-service.mdx (via packages/spec)
  • content/docs/kernel/runtime-services/sharing-service.mdx (via @objectstack/spec)
  • content/docs/kernel/runtime-services/sms-service.mdx (via packages/spec)
  • content/docs/kernel/runtime-services/storage-service.mdx (via @objectstack/spec)
  • content/docs/kernel/services-checklist.mdx (via @objectstack/spec)
  • content/docs/kernel/services.mdx (via @objectstack/spec)
  • content/docs/permissions/authorization.mdx (via @objectstack/spec)
  • content/docs/permissions/permission-sets.mdx (via @objectstack/spec)
  • content/docs/permissions/permissions-matrix.mdx (via @objectstack/spec)
  • content/docs/permissions/positions.mdx (via @objectstack/spec)
  • content/docs/permissions/rls.mdx (via @objectstack/spec)
  • content/docs/permissions/sharing-rules.mdx (via @objectstack/spec)
  • content/docs/permissions/system-context.mdx (via packages/spec)
  • content/docs/plugins/adding-a-metadata-type.mdx (via @objectstack/spec)
  • content/docs/plugins/development.mdx (via @objectstack/spec)
  • content/docs/plugins/index.mdx (via @objectstack/spec)
  • content/docs/plugins/packages.mdx (via @objectstack/spec)
  • content/docs/protocol/backward-compatibility.mdx (via @objectstack/spec)
  • content/docs/protocol/diagram.mdx (via packages/spec)
  • content/docs/protocol/kernel/config-resolution.mdx (via @objectstack/spec)
  • content/docs/protocol/kernel/http-protocol.mdx (via @objectstack/spec)
  • content/docs/protocol/kernel/i18n-standard.mdx (via @objectstack/spec)
  • content/docs/protocol/kernel/index.mdx (via @objectstack/spec)
  • content/docs/protocol/kernel/lifecycle.mdx (via @objectstack/spec)
  • content/docs/protocol/kernel/plugin-spec.mdx (via @objectstack/spec)
  • content/docs/protocol/knowledge.mdx (via @objectstack/spec)
  • content/docs/protocol/objectql/index.mdx (via @objectstack/spec)
  • content/docs/protocol/objectql/query-syntax.mdx (via @objectstack/spec)
  • content/docs/protocol/objectql/schema.mdx (via @objectstack/spec)
  • content/docs/protocol/objectql/security.mdx (via packages/spec)
  • content/docs/protocol/objectql/state-machine.mdx (via @objectstack/spec)
  • content/docs/protocol/objectui/actions.mdx (via @objectstack/spec)
  • content/docs/protocol/objectui/concept.mdx (via @objectstack/spec)
  • content/docs/protocol/objectui/index.mdx (via @objectstack/spec)
  • content/docs/protocol/objectui/layout-dsl.mdx (via @objectstack/spec)
  • content/docs/protocol/objectui/record-alert.mdx (via @objectstack/spec)
  • content/docs/protocol/objectui/widget-contract.mdx (via @objectstack/spec)
  • content/docs/ui/actions.mdx (via @objectstack/spec)
  • content/docs/ui/apps.mdx (via @objectstack/spec)
  • content/docs/ui/create-vs-edit-form.mdx (via @objectstack/spec)
  • content/docs/ui/dashboards.mdx (via @objectstack/spec)
  • content/docs/ui/field-grouping-and-order.mdx (via @objectstack/spec)
  • content/docs/ui/forms.mdx (via @objectstack/spec)
  • content/docs/ui/index.mdx (via @objectstack/spec)
  • content/docs/ui/public-data-collection.mdx (via @objectstack/spec)
  • content/docs/ui/setup-app.mdx (via @objectstack/spec)
  • content/docs/ui/translations.mdx (via @objectstack/spec)
  • content/docs/ui/views.mdx (via @objectstack/spec)

7 release-owned page(s) also reference the affected code. These are read-only:

  • content/docs/releases/implementation-status.mdx (via @objectstack/spec)
  • content/docs/releases/index.mdx (via @objectstack/spec)
  • content/docs/releases/v12.mdx (via @objectstack/spec)
  • content/docs/releases/v13.mdx (via @objectstack/spec)
  • content/docs/releases/v16.mdx (via @objectstack/spec)
  • content/docs/releases/v17.mdx (via @objectstack/spec)
  • content/docs/releases/v9.mdx (via @objectstack/spec)

content/docs/releases/ is RELEASE-OWNED (AGENTS.md "Documentation Guardrails"): release
notes are written centrally at release time, and a code PR that edits them is the exact PR
that guardrail exists to stop. They are still audited — read-only. If one of them is actually
wrong, file an issue or open a dedicated docs-only PR; do not edit it here.

Advisory only. To re-verify, run the docs-accuracy-audit workflow scoped to these files:
node scripts/docs-audit/affected-docs.mjs origin/main → pass the list as args.docs.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size/xs skip-changeset PR has no user-facing published change; bypasses the changeset gate

Projects

None yet

1 participant