Skip to content

docs(metadata-core): correct createFieldPresenceProbe's stated reason — organization_id is provisioned unconditionally - #13810

Merged
zhuangjianguo merged 3 commits into
mainfrom
claude/issue-13416-field-presence-probe-docstring
Aug 31, 2026
Merged

docs(metadata-core): correct createFieldPresenceProbe's stated reason — organization_id is provisioned unconditionally#13810
zhuangjianguo merged 3 commits into
mainfrom
claude/issue-13416-field-presence-probe-docstring

Conversation

@claude

@claude claude Bot commented Aug 31, 2026

Copy link
Copy Markdown
Contributor

Fixes #13416

createFieldPresenceProbe's docstring recorded a falsified fact as the probe's reason for existing:

Why the probe exists at all: the SchemaRegistry auto-injects organization_id only in multi-tenant mode (applySystemFields({ multiTenant })), so on single-tenant stacks the sys_audit_log / sys_activity tables have no such column.

The stated reason is corrected. The probe itself is untouched — its implementation is byte-identical. Deleting it as dead is the exact wrong move the stale sentence invited, and it still has real work: what it answers is provenance, not posture.

What is true now

The organization_id COLUMN is provisioned unconditionally, subject only to the explicit opt-outs (systemFields: false, systemFields.tenant: false, managedBy: 'better-auth', tenancy.enabled: false). The multi-tenant flag now governs only whether the column is INDEXED, never whether it EXISTS. The sentence is corrected and its history kept rather than dropped — the convention this repo already uses for a fact whose owner changed.

Four independent sources in the tree agree, three of them the card's and one it did not cite:

Source Line Says
packages/objectql/src/registry.ts 508-518 "provisioned unconditionally … its existence no longer depends on the global multi-tenant flag … now governs only whether the column is INDEXED"
packages/spec/src/data/injected-system-columns.ts 139 resolveInjectedSystemColumns(def: unknown) — one parameter, the object definition. No multiTenant to decide with.
packages/objectql/src/registry-tenancy-posture.test.ts 14-19 pins it executably, not in prose
this repo's census (below) zero declarations excluded by posture

Re-located needles (the card measured at 090f2302e; main has moved)

Re-found by quoted prose, never by line number.

  • "Why the probe exists at all: the SchemaRegistry auto-injects" — card said record-organization.ts:56-58, still at 56-58. No drift.
  • "The \organization_id` COLUMN is provisioned unconditionally"— card saidregistry.ts:509-517`; the block now sits at 508-518 (start drifted by -1; the card cited a sub-range of an 11-line block).
  • "which is exactly how a docstring becomes the last place a wrong fact survives"packages/drivers/driver-sql/src/sql-driver.ts:11888. Echoed in the replacement prose.

The card's multiTenant zero — re-run with a positive control

The card's third re-check command was "prints nothing, which is the point." It now prints one line, and the file it names is not where the function lives. The substantive claim survives both corrections:

  • git grep -n "multiTenant" -- packages/spec/src/data/injected-system-column-provenance.ts1 hit, :102, a historical prose mention: "(meta: applySystemFields stamps indexed on organization_id — a key FieldSchema rejects by name, so every registry-backed object read answers _diagnostics: { valid: false } #6810 removed the indexed: opts.multiTenant that used to be; the tenant index is declared in the object's indexes[] instead.)". Not an input.
  • Positive control, same filter, a token that IS in that file: resolveInjectedSystemColumns6 hits. The filter was live, so the reading above is a reading rather than a dead grep.
  • resolveInjectedSystemColumns is defined in the sibling packages/spec/src/data/injected-system-columns.ts:139; the provenance file imports it at :42. The quoted docstring "the flag cannot change what EXISTS, only what is indexed" is registry.ts:517-518, not the provenance file's own.
  • Verdict: the derivation still takes no multiTenant input. Signature is (def: unknown); tenant is computed at :163 as sf?.tenant !== false && !isTenancyDisabled(obj).

Census re-measured (the card's 117 / 92 / 25 predate #13491)

packages/objectql/src/tenancy/platform-object-tenancy.ts is present in this tree, so the reclassification has landed. Re-measured by running the real resolveInjectedSystemColumns over every declaration, at this PR's head:

  • population: 112 tracked *.object.ts files, 116 object declarations read
  • 88 resolve a tenant field, 28 do not
  • all 28 excluded by explicit opt-out: 26 by managedBy: 'better-auth', 2 by managedBy: 'better-auth' + tenancy.enabled: false
  • excluded by posture: 0

One further declaration (blank_note, the create-objectstack blank template) could not be imported — it resolves @objectstack/spec outside the workspace graph — so 117 candidate declarations, 1 unread. The qualitative claim is what carries the fix, and it holds: nothing is excluded by posture.

Decisive for the two tables the old sentence named: sys_audit_log and sys_activity both carry managedBy: 'append-only', neither declares organization_id itself, and both resolve tenant: true with organization_id in the injected plan — on every posture.

Why a changeset, and why patch

Not assumed from "prose only" — measured. createFieldPresenceProbe is a published export of @objectstack/metadata-core, and the falsified sentence was emitted verbatim into the built declarations, so it is what a consumer's editor showed on hover:

  • packages/metadata-core/dist/index.d.ts:20159 and dist/index.d.cts:20612 carried the stale sentence before this change; after the rebuild both carry the corrected text.
  • @objectstack/plugin-audit re-exports the symbol but its declarations do not inline the JSDoc — dist/index.d.ts:3 is a bare export { createFieldPresenceProbe } from '@objectstack/metadata-core';, and a grep for the new text there returns 0. So the changeset names @objectstack/metadata-core only.

Verification — all at final commit d28e026558

  • pnpm --filter @objectstack/metadata-core test15 files, 261 tests, all pass
  • pnpm --filter @objectstack/metadata-core typecheck — green (tsc --noEmit and tsc --noEmit -p tsconfig.test.json, so the test program is covered too)
  • eslint . --no-inline-config over the repo — 5574 files, 0 errors, 0 warnings. Run whole, not narrowed.
  • gate family derived mechanically with node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack --commands (27 families, re-derived after the merge with main and identical to the pre-merge derivation): 25 green, 2 NOT MEASURED, neither a red:
    • check-test-completeness exit 3 — "PREREQUISITE NOT MET … the local reading for this gate is NOT MEASURED"; it grades a saved turbo run test log CI tees and this invocation has none.
    • check:dual-build-cjs-loads exit 3 — "PREREQUISITE NOT MET — this gate reads built output, and some package has no dist/"; it needs a whole-workspace pnpm build, which is CI's run.
  • pnpm check:nul-bytes — green (7586 files, self-test 75 assertions); plus a direct control-byte scan of both changed files, no hits.

Builds were serialised through scripts/pm/os-verify-lock.sh; every result above is read from the gate's own verdict line, with the exit code captured before any pipe.

Scope

Prose in one docstring, plus its changeset. No code, no test, no index, no behaviour. Deliberately not claimed: which commit moved the provisioning off the flag — the card declines to assert it and it is not needed to correct the sentence. #13433 and #13434 are separate cards and are not addressed here.


Generated by Claude Code

claude added 3 commits August 31, 2026 13:02
… — the organization_id column is provisioned unconditionally

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01F3jdziLbAPGeceVNmSox5L
@github-actions

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

⚠️ 1 changed file(s) yielded no anchor (packages/metadata-core/src/record-organization.ts), so the pages documenting them are NOT COVERED by this run — this is not a clean bill of health for those files. Nothing else in this diff resolved to a documentable surface (no symbol, route or SDK anchor derived from 1 changed package(s)).

What this run could not see
  • 1 changed file(s) yielded no anchor (packages/metadata-core/src/record-organization.ts) — pages documenting those are invisible to this run
  • a page that states a rule by its inputs shares no identifier with the emitter that implements the rule, so an emitter-only diff cannot list it — not on this run and not on any run. Measured on fix(driver-sql): emit varchar(maxLength) for a text field a declared index keys on #11430: content/docs/protocol/objectql/types.mdx documents the text-family column mapping by the ObjectQL type names it maps FROM (text / textarea / html) while the diff changed createColumn; it went unlisted, and it was the page that diff falsified, in four places. No shared token exists to detect this on, so a rule your change carries has to be re-read by hand in the pages that restate it.

Coarse fallback — 3 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): node scripts/docs-audit/affected-docs.mjs --json 6b285eca42a780636ea416a916e0de228f797fc1packageMentionDocs.

@github-actions github-actions Bot added documentation Improvements or additions to documentation tooling labels Aug 31, 2026

Copy link
Copy Markdown
Collaborator

PM review: ACCEPT, pending green

Reviewed against the dispatch order (#13416 comment 5478571701). CI is still running; this review is independent of it. Release follows on full green — ⛔ enqueue bar is EVERY check, not the required subset.

⭐⭐ The headline: the seat falsified the card's own decisive evidence, and the fix survives anyway

The card's third re-check command was offered as its sharpest proof — "The third command prints nothing, which is the point." Running it with a control found two errors in the card:

  1. It no longer prints nothing. git grep -n "multiTenant" -- packages/spec/src/data/injected-system-column-provenance.ts returns 1 hit at :102 — a historical prose mention ("meta: applySystemFields stamps indexed on organization_id — a key FieldSchema rejects by name, so every registry-backed object read answers _diagnostics: { valid: false } #6810 removed the indexed: opts.multiTenant that used to be…"), not an input.
  2. ⭐ The card named the wrong file. resolveInjectedSystemColumns is defined in the sibling packages/spec/src/data/injected-system-columns.ts:139; the provenance file merely imports it at :42. I verified independently that these are two distinct files in that directory — the correction is structurally sound, not a naming quibble.

A zero that had drifted into a one, in a file that was never the right file. Had the seat re-run the card's command and stopped at "hmm, 1 hit, premise broken", it would have reported a false STOP. Instead it traced to the real definition and confirmed the substantive claim holds: signature (def: unknown), tenant computed at :163 from sf?.tenant !== false && !isTenancyDisabled(obj). No multiTenant input.

The positive control fired — resolveInjectedSystemColumns → 6 hits under the same filter — so the reading is a reading and not a dead grep. That is exactly the discipline the order asked for, and it paid twice.

⭐ The sharpest evidence in the PR is one the card never had

sys_audit_log and sys_activity are the two tables the stale sentence names by name, and both resolve tenant: true with organization_id in the injected plan on every posture. The falsified sentence is not merely outdated in general — it is wrong about its own two examples. That is the most economical possible refutation and it belongs in the record.

Four independent in-tree sources agree, including one the card did not cite: registry-tenancy-posture.test.ts:14-19 pins it executably rather than in prose.

Census re-measured, with the unread declaration declared

The card's 117 / 92 / 25 predate #13491. Re-measured by running the real resolveInjectedSystemColumns over every declaration: 116 read, 88 tenant, 28 not — and 0 excluded by posture, all 28 by explicit managedBy / tenancy.enabled opt-out. ⭐ One further declaration (blank_note) could not be imported and is stated as unread rather than folded into the total. Reporting 116-of-117 with the gap named is the honest shape.

Changeset measured, not assumed — including the re-export

The order warned not to take skip-changeset on "prose only" (the lesson from #13434). Measured: the falsified sentence was emitted verbatim into metadata-core/dist/index.d.ts:20159 and dist/index.d.cts:20612 — it is what a consumer's editor showed on hover. ⭐ And it checked one step further: @objectstack/plugin-audit re-exports the symbol but its declarations do not inline the JSDoc (bare export { … } from, grep returns 0), so the changeset names @objectstack/metadata-core only. Correctly scoped rather than reflexively widened.

⚠️ My own prediction, corrected

Zone 2 A2.1 said the card's line numbers were "almost certainly false" and warned of heavy drift. Measured: near zero. record-organization.ts:56-58 — no drift at all; registry.ts:509-517 → 508-518, a one-line start shift because the card cited a sub-range of an 11-line block. I over-generalised from #13434, where the same class of coordinate had drifted +150 lines.

⭐ Re-locating by needle was still the right procedure — it is what turned up the wrong-file error above, which a line-number edit would have sailed past. The method was right; my confidence about the magnitude was not.

Scope held

Probe implementation byte-identical — the card's central instruction. ⛔ Which commit moved the provisioning off the flag is deliberately not claimed, per Zone 1.3. #13433 and #13434 untouched.

Verification: 15 files / 261 tests green; typecheck covering the test program too; repo-wide eslint --no-inline-config over 5574 files with 0 errors; 25 of 27 gate families green with the 2 exit-3 gates reported as NOT MEASURED rather than as passes.


Generated by Claude Code

Copy link
Copy Markdown
Collaborator

Green — releasing

All 32 checks success or skipped, zero failures: all six Test Core shards + the rollup, Lint & Repo Gates, all four Type Check legs + TypeScript Type Check, Build Core, Temporal Conformance (live PG + MySQL), Dogfood ×4, Check Changeset, Governed Surface Queue Guard. ACCEPT stands (comment 5479131062).

One thing not yet on this PR: the docs-drift blind spot, checked

The drift bot reported a blind spot rather than a findingrecord-organization.ts yielded no anchor, so "the pages documenting them are NOT COVERED by this run — this is not a clean bill of health."

⭐ That is worth closing rather than waving through, because this card's own thesis is "how a docstring becomes the last place a wrong fact survives" — correcting the JSDoc while an identical stale claim sat in a docs page would merely relocate the defect.

Searched content/docs for organization_id + multi-tenant: 3 hits, and the outcome is better than clean.

content/docs/references/data/object.mdx is ALREADY CORRECT. It documents tenant as:

Inject the organization_id column. Default true (the column is always provisioned; the multi-tenant flag governs only its index).

⇒ That is precisely what this PR corrects the docstring to say. The docs site and the JSDoc will agree once this lands — and it is a fifth independent corroborating source, on top of the four the PR already cites (registry.ts, injected-system-columns.ts, registry-tenancy-posture.test.ts, the re-run census).

The other two hits are release-owned (releases/v15.mdx, v17.mdx) — ⛔ read-only under the Documentation Guardrails, untouched, and neither states the falsified rule.

⚠️ Stated limit: that establishes those pages, not that no page anywhere restates the rule in different wording. Combined with the bot's own caveat that an inputs-stated rule shares no identifier with its emitter, that is as far as an advisory row is worth taking.

Releasing: draft cleared, auto-merge enabled. ⛔ Not merged by this seat, and no approving review from it.


Generated by Claude Code

@zhuangjianguo
zhuangjianguo marked this pull request as ready for review August 31, 2026 13:54
@zhuangjianguo
zhuangjianguo added this pull request to the merge queue Aug 31, 2026
Merged via the queue into main with commit 00d8f65 Aug 31, 2026
34 checks passed
@zhuangjianguo
zhuangjianguo deleted the claude/issue-13416-field-presence-probe-docstring branch August 31, 2026 14:16
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation size/s tooling

Projects

None yet

2 participants