fix(spec): systemFields.owner guidance no longer claims ownership: 'org' picks a principal (#6365) - #6423
Merged
os-project-manager merged 1 commit intoAug 7, 2026
Conversation
… 'org'` picks a principal (#6365) The `guidance.owner` prescription on the `systemFields` strictObject said `'user'`/`'org'` "choose the principal". That is false: `'org'` injects no `owner_id` at all. The authority `applySystemFields` consumes — `resolveInjectedSystemColumns` — admits exactly `undefined` and `'user'`: const owner = ownershipEligible && (ownership === undefined || ownership === 'user'); and the `ownership` JSDoc ~90 lines above already said so correctly, so the guidance was the wrong side of a contradiction inside one file. It matters because this is the text an author (or an AI writing metadata) is handed at the moment they are already confused about where owner injection is configured. It sent them to `ownership: 'org'` expecting an org-keyed owner column; nothing rejects that value, so the mistake ships and every owner-keyed feature quietly does nothing. The sibling `guidance.ownership` message is widened in the same pass — not wrong, only out of date: since #5677 / ADR-0117 D1 the property governs both ownership anchors, so it now names `owning_business_unit_id` alongside `owner_id`. Text only — the acceptance surface does not move. `check:generated` is green with no artifact rewritten (these are error-message templates, not `.describe()` inputs, so `content/docs/references/**` does not regenerate, and `authorable-surface.base.json` is byte-identical). Tests: the batch-20 pin gains two cases that assert the message's SUBSTANCE against `resolveInjectedSystemColumns` rather than echoing the sentence. Reverse-verified in the predicted direction: restoring the old string turns exactly those two red and leaves the other 43 green. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018ffcE95NaMJcL9XJ9VDYgk
|
The latest updates on your projects. Learn more about Vercel for GitHub. 1 Skipped Deployment
|
Contributor
📓 Docs Drift CheckThis PR changes 1 package(s): 112 hand-written doc(s) reference the affected code and may need an implementation-accuracy re-verification:
|
os-project-manager
marked this pull request as ready for review
August 7, 2026 19:12
This was referenced Aug 7, 2026
os-project-manager
deleted the
claude/issue-6365-systemfields-owner-guidance
branch
August 7, 2026 19:25
This was referenced Aug 7, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #6365
The defect
packages/spec/src/data/object.zod.ts, in thesystemFieldsstrictObject'sguidanceblock — the message an author gets when they write the non-existentsystemFields: { owner: ... }key — said:The second half is false.
ownership: 'org'does not choose a different principal; it injects noowner_idat all. The authorityapplySystemFieldsconsumes —resolveInjectedSystemColumnsinpackages/spec/src/data/injected-system-columns.ts— admits exactly two spellings:The
ownershipproperty's own JSDoc, ~90 lines above the guidance, already stated it correctly (org/none— no per-record owner;owner_idis NOT injected). So the guidance contradicted a sibling stanza in the same file, and the guidance was the wrong side.Why it was worth fixing
This is not dormant prose. It is the error text an author — or an AI writing metadata — is handed at the exact moment they are already confused about where owner injection is configured, and it sent them to
ownership: 'org'expecting an owner column keyed to the organization. Nothing rejectsownership: 'org', so the mistake ships silently and every owner-keyed feature quietly does nothing: owner-scoped RLS, "My" views, owner reports, the first-admin bootstrap handoff. That is the failure mode the surroundingguidancemachinery exists to prevent, inverted — a wrong-key rescue handing out a second wrong answer.What changed
Two strings, nothing else.
guidance.ownernow states the rule as the authority implements it:'user'(or omitted) injectsowner_id, while'org'and'none'both skip it and noowner_idis injected at all. The two skipping values stay visibly distinct in intent ('org'for an org-wide catalog, Dataverse-style;'none'for a junction/link table), since that distinction is the reason the enum carries both.guidance.ownershipis widened in the same pass. It was not wrong, only out of date: since #5677 / ADR-0117 D1 theownershipproperty governs both record-ownership anchors, so the message now says it decides whetherowner_idandowning_business_unit_idare injected, rather than naming only the first.Deliberately untouched per the issue's scope ruling: the
ownershipenum's ownerrortext anddescribe()(#5678's surface), and any mention of thebusiness_unittier.The acceptance surface does not move
Text only. Every value
ObjectSchemaaccepted before is accepted now, every value it rejected is still rejected, and the injection behaviour is untouched.git statusafter a full build carries only the three files in this diff — in particularauthorable-surface.base.jsonis byte-identical andcontent/docs/references/**did not regenerate, which confirms the expectation that aguidancestring is an error-message template rather than a.describe()input.Tests
The batch-20 pin gains two cases. They assert the message's substance against
resolveInjectedSystemColumnsrather than echoing the sentence, so the prescription can only stay green while it still describes what the injection pass really does:'org'and'none'both resolveowner: false,'user'and omitted resolveowner: true— and the message names the two skipping values together, states the absence as an absence, keeps them distinct in intent, and no longer contains the wordprincipal.plan.owningBusinessUnit === plan.owner) — and the message names both.Reverse verification, direction predicted before running: red. Restoring the old string turns exactly those two new cases red and leaves the other 43 in the file green, so the pins are load-bearing and additive rather than a re-spelling of an existing assertion.
With the fix in place:
Gates enumerated from
.github/workflows/lint.ymland run one by one: all 30 ESLint-jobcheck:*steps pass, plus the TypeScript-job set (type-check-coverage,driver-conformance,stall-guard,skill-frame-sync,skill-compatibility,exported-any,dual-source-exports,skill-examples,doc-formula-expressions,type-check-debt,i18n,i18n-coverage) andpr-automation.yml'scheck-adr-0087-registration(0 declared-breaking changesets — this is a patch).One note on build state:
check:i18nandcheck:i18n-coveragefirst reported red in the fresh worktree because they run the built CLI, which did not exist yet. Both are green afterturbo run build— the AGENTS.md §9 stale/absent-artefact trap, not a finding.Changeset
.changeset/systemfields-owner-guidance-org-skips-owner-id.md—@objectstack/spec: patch. An author-visible error-message change earns a real changeset; no ADR-0087 marker is required because nothing is declared breaking.Generated by Claude Code