Skip to content

docs(validating-metadata): the four-door table records the object runtime door after #4716 - #9847

Merged
os-elon merged 1 commit into
mainfrom
claude/issue-9836-object-door-doc-table
Aug 19, 2026
Merged

docs(validating-metadata): the four-door table records the object runtime door after #4716#9847
os-elon merged 1 commit into
mainfrom
claude/issue-9836-object-door-doc-table

Conversation

@os-elon

Copy link
Copy Markdown
Collaborator

Fixes#9836

Docs-only. content/docs/deployment/validating-metadata.mdx describes "the one gate, four doors"; #4716 (PR #9825, squash 1408ae337) crossed five gating object rules onto the runtime publish door, and three rows of the table still read for rules that now run there.

The column was re-derived from the registry, not from the issue's table

Two independent readings, both on this branch's base 1408ae337, because the naive probe is known to be wrong here.

Reading 1 — the built registry.pnpm --filter '@objectstack/lint^...' build then pnpm --filter @objectstack/lint build, then AUTHORING_RULES imported from packages/lint/dist/index.js and filtered on runtimeTypes.includes('object'). 7 rules, all surfaces: cli,runtime-publish, all tier: gating:

ruleruntimeTypes
validateFunctionalCompleteness["object"]
validateManagedApiMethods["object"]
validatePresetComparands["dashboard","view","object","page","flow"]
lintAutonumberFormats["object"]
validateSecurityPosture["seed","permission","book","object"]
validateRuleCompilability["object"]
validateRuleSchemaFormats["object"]

Reading 2 — anchored source grep, ^\s+runtimeTypes: \[ over packages/lint/src/authoring-rules.ts: 16 declarations, 7 containing object (:472 :500 :597 :1072 :1260 :1382 :1408). Agrees with reading 1.

The trap, reproduced.grep -c "runtimeTypes.*'object'" returns 8. The extra is :1276, a comment inside the validateSecurityRoleWord block that quotes a declaration it does not make. The anchored form excludes it because a comment line begins with //, not whitespace-then-runtimeTypes. A grep cannot tell a claim from a description of a claim; both readings above avoid the form that can't.

Non-vacuity control. The two rules the issue predicts are ABSENT from the object door read absent in the same query that finds the other seven: validateSharingRuleEnforceabilitysurfaces: cli only, no runtimeTypes; validateRlsPredicateEnforceability → same. So the cells are a positive claim about the door, not an artifact of a query that finds nothing. Cross-check on the other side: pre-merge (1408ae337^) the anchored grep finds only :567 and :1226 carrying object — the door was 2 rules and is now 7, which is exactly #4716's five.

The three rows

  • :392 Zod-valid but functionally inert declarations → ✓ᵒ (validateFunctionalCompleteness, validateManagedApiMethods)
  • :413 Autonumber {field} interpolation → ✓ᵒ (lintAutonumberFormats)
  • :411split, not a cell edit.

How the :411 split is drawn

The row bundled four rule ids now on opposite sides of the wall. ✓ᵒ on the merged row would claim the door judges an RLS predicate on an object write; keeps the current falsehood. Split by which collection carries the declaration each rule reads:

new rowrule idscell
declared on the object being written — a validation rule's regex / JSON Schema (#4762) and its format names (#5178)validateRuleCompilability, validateRuleSchemaFormats✓ᵒ
declared on another collection — sharing-rule conditions (#4698), row-level-security predicates (#4983)validateSharingRuleEnforceability, validateRlsPredicateEnforceability

The two that crossed read validationRules[] on the object being written, so an object write already carries everything they judge. The two that stayed read sharingRules[] and permissions[], and are held by different things — each stated in the registry's own surfaceReason, so the doc quotes the registry rather than modelling it: the door does not accept sharing_rule as a type at all, whereas permissions has been in the snapshot since #8309 and only the declaration is missing.

A new paragraph states the split rule in prose, following the precedent the page already sets for the security vocabulary freeze ("one rule id has to sit on ONE side of the wall"), so the next crossing does not have to re-derive it.

The -reasons paragraph — checked, and one reason class was missing

All four enumerated reasons are still true of at least one remaining cell, bucketed from the built registry over the 25 rules with no runtimeTypes: stack-wide collection 13 rules; typescript/sucrase source parse 2; snapshot-safe-not-rolled-out 2 (exactly the sharing-rule / RLS pair the paragraph names, which survives untouched); capability-reference graduation 1.

That accounts for 18 of 25. The remaining 6 sit in a class the paragraph did not have, and #4716 is what created it. Their old reason constant RUNTIME_OBJECT_WRITES_P2 is retired — 0 occurrences post-merge; they now carry RUNTIME_OBJECT_ADVISORY_VOLUME, whose text says crossing one is a UX decision with its own card and "never a bare runtimeTypes edit". Left uncorrected, the paragraph's third reason would have pointed a reader at a runtimeTypes flip for precisely the population where the registry says that is the wrong move. Added as a fifth reason, phrased from the registry constant.

Also

#4716 appended to the crossing-history clause in its siblings' shape (what crossed, onto which type).

Out of scope, untouched

content/docs/releases/ (release-owned); AUTHORING_RULES and every rule's runtimeTypes / surfaces / surfaceReason; the RUNTIME_NEEDS_FULL_SNAPSHOT population (17 occurrences, unchanged — #9612's territory).

Verification — 12/12 gates green at 3c693d15c, the final commit

Union re-derived at the final HEAD with node scripts/pm/dispatch-gates.mjs (no paths passed; it takes its own change set from the merge base), then run:

check:nul-bytes · check:doc-anchors · check:doc-authoring · check:docs-audit-scope · check:docs-redirects · check:role-word · check:published-readme-links · check:cross-package-test-inputs · @objectstack/speccheck:empty-state · check:liveness · check:strictness-ledger · check:variant-docs

No package tests are affected and none should be: the diff is one file under content/docs/, in no workspace package. The only code references to this page are two route entries in apps/docs/redirects.mjs, a comment in check-doc-anchors.mjs, and the generated apps/docs/.source/* — no test pins the table's content. docs-drift-check.yml does not trigger here; its paths: are packages/**, scripts/docs-audit/** and its own file, so a content/**-only diff is correctly outside it. The affected-docs mapper itself is exercised by check:docs-audit-scope, which is green.

One byte-discipline note against the record: a U+200B slipped into validateRuleCompilability while this prose was being written, and was caught by a scan before commit. It is gone; check:nul-bytes is green and the identifier greps as one token.

No changeset — skip-changeset

AGENTS.md §"Add a changeset for feature work" scopes changesets to features and functional improvements. This ships no package: content/ is in no workspace entry, and apps/docs is private. The ten most recent docs-only commits on main carry zero changesets. The skip-changeset label is applied instead.

Generated by Claude Code


Generated by Claude Code

…time door after #4716 (#9836)
Three rows of the "one gate, four doors" table read `—` in the `runtime
publish` column for rules that now run there. #4716 (PR #9825, squash
1408ae3) crossed five gating object rules onto that door, taking it from
2 rules to 7.
Corrected against the built registry, not the source text:
:392 functional completeness + managed API methods — -> ✓ᵒ
:413 autonumber `{field}` interpolation — -> ✓ᵒ
The "declared enforcement that cannot run" row is SPLIT rather than
edited. It bundled four rule ids that now sit on opposite sides of the
wall: `validateRuleCompilability` (#4762) and `validateRuleSchemaFormats`
(#5178) carry `runtimeTypes: ['object']`, while
`validateSharingRuleEnforceability` (#4698) and
`validateRlsPredicateEnforceability` (#4983) are `CLI_ONLY`. `✓ᵒ` on the
merged row would claim the door judges an RLS predicate on an object
write; `—` keeps the current falsehood. The split is drawn by which
collection carries the declaration each rule reads, and a new paragraph
states that rule so the next crossing does not re-derive it — following
the precedent the page already sets for the security vocabulary freeze.
Also: #4716 appended to the crossing-history clause, and the `—`-reasons
paragraph gains the reason class #4716 created. All four existing reasons
still hold, but the advisory-tier object rules moved: their old
`RUNTIME_OBJECT_WRITES_P2` reason is retired (0 occurrences), replaced by
`RUNTIME_OBJECT_ADVISORY_VOLUME` on six rules whose registry text says
crossing them is "explicitly NOT a bare `runtimeTypes` edit" — the
opposite of what the paragraph's third reason would have told a reader.
No change to `AUTHORING_RULES`, no rule's `runtimeTypes`, nothing under
`content/docs/releases/`.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019yDEhPBC3tcGkW9bkce1HM
@github-actionsgithub-actionsBot added size/s documentation Improvements or additions to documentation labels Aug 19, 2026
@os-elonos-elon added skip-changeset PR has no user-facing published change; bypasses the changeset gate and removed documentation Improvements or additions to documentation labels Aug 19, 2026 — with Claude
@os-elon
os-elon marked this pull request as ready for review August 19, 2026 06:25
@os-elon
os-elon added this pull request to the merge queueAug 19, 2026
@os-elonClaude

Copy link
Copy Markdown
CollaboratorAuthor

PM review — ACCEPT, ready flipped, enqueued (squash auto-merge)

Reviewed against GitHub, not against the report. Every load-bearing claim in the body was re-derived here independently rather than taken on trust.

Independent verification, on origin/main at review time

claim in the bodymy measurement
object door = 7 rules (anchored probe)^\s+runtimeTypes: \[[^]]*'object'7
naive probe returns 8 (the trap)runtimeTypes.*'object'8✓ reproduces
RUNTIME_OBJECT_WRITES_P2 retired0 occurrences
RUNTIME_OBJECT_ADVISORY_VOLUME is its replacement8 occurrences
validateSharingRuleEnforceability absent from the doorsurfaces: CLI_ONLY, no runtimeTypes
validateRlsPredicateEnforceability absent from the doorsurfaces: CLI_ONLY, no runtimeTypes

The two absence rows are what make the :411 split truthful rather than merely plausible — they are the reason the half of the split row is a correct statement about the door and not a leftover.

Divergence — and the check that actually mattered here

Merge base 1408ae337 (correctly the #4716 squash — the card required deriving on the merged ref, and the branch is based on it). origin/main has since advanced 17 commits:

proberesult
commits touching content/docs/deployment/validating-metadata.mdx0
⭐ commits touching packages/lint/src/authoring-rules.ts0
git merge-tree conflicts against current main0
files this branch changes1
positive control — same command on a file main did move1

That second row is the one I ran deliberately. This PR's entire content is a derivation from the registry, so a doc that is correct against authoring-rules.ts at the merge base would be silently wrong if that file had moved underneath it. It has not moved in 17 commits, so the derivation is still live. A per-file check on the changed file alone would not have caught that class of staleness — the input file matters as much as the output file when the change is a derivation.

What raises this above the card as written

The card asked the dev to "re-read the -reasons paragraph … check the others rather than assuming." They did, bucketed all 25 no-runtimeTypes rules from the built registry, accounted for 18 across the four existing reasons — and found the remaining 6 sit in a class the paragraph did not have, created by #4716 itself.

The consequence is the part worth keeping: those six now carry RUNTIME_OBJECT_ADVISORY_VOLUME, whose own text says crossing one is a UX decision with its own card and "never a bare runtimeTypes edit." Left uncorrected, the paragraph's third reason would have pointed the next reader at a runtimeTypes flip for exactly the population where the registry says that is the wrong move. That is a doc actively teaching the wrong repair, caught by doing the check rather than asserting it. Both readings agree and the trap was reproduced rather than merely avoided.

The :411 split is drawn on a principled basis — which collection carries the declaration each rule reads — and the doc quotes each rule's own surfaceReason rather than re-modelling the reasoning, so the two rules stay correct for the right reason (the door does not accept sharing_rule as a type at all; permissions has been in the snapshot since #8309 and only the declaration is missing). The prose paragraph stating the rule follows the page's own vocabulary-freeze precedent, so the next crossing inherits it.

The U+200B that slipped into validateRuleCompilability mid-prose and was caught by a scan before commit is recorded in the body rather than quietly fixed. That is the right instinct — a zero-width character inside an identifier is exactly the defect that survives review by being invisible.

Note on sequencing

Enqueued without waiting for the structured report. The PR body is a complete deliverable that declares 3c693d15c final and carries 12/12 named gates green; the artifact is on GitHub and I have verified it directly. Auto-merge re-validates through the queue regardless, so a late push cannot land unchecked.

Tracking to MERGED and verifying on the artifact.


Generated by Claude Code

Merged via the queue into main with commit cfe1c49Aug 19, 2026
28 checks passed
@os-elon
os-elon deleted the claude/issue-9836-object-door-doc-table branch August 19, 2026 06:38
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size/sskip-changesetPR has no user-facing published change; bypasses the changeset gate

Projects

None yet

Development

Successfully merging this pull request may close these issues.

docs(validating-metadata): the four-door table understates the object runtime door after #4716 — and one row bundles four rule ids across the wall

2 participants

@os-elon@claude