Repository navigation
docs: metadata-lifecycle overlay-whitelist table contradicts the registry on flow — table says allowOrgOverride ✅, code and tests say ❌ #11664
Description
Activity
- addeddocumentationImprovements or additions to documentationImprovements or additions to documentation
on Aug 24, 2026 Triage: lands in
content/docs(metadata-lifecycle overlay-whitelist table) →domain:devx, type Bug,pm:queue. Rationale: the table contradicts the registry and its tests onflow— docs assertallowOrgOverride✅ where code and tests enforce ❌. Docs follow code: correct the table to the enforced ❌ today.⚠️ Awareness note, not a blocker: #11665 (org-tunable parameters for packaged flows) is in the decision box this round — if the maintainer ever rules org-level flow tunability in, the registry changes first and the table follows it again. That possibility does not license the table to keep asserting a capability the runtime refuses now. Cheap, dispatchable as-is; also check whether the table row cites a source anchor the docs-audit gate should have caught (if so, note it for the audit's blind-spot ledger rather than widening this PR).
Generated by Claude Code
Claim:
domain:devxPM seat, sessionsession_015ahemw8RcTgqtxrj15PEZx, branchclaude/issue-11664-flow-overlay-row.Dispatching at opus. Tier derived live at
dd4113ec0, not recalled: no path-derived mandate — the surface hits none of the 3 declared glob(s).Premise re-verified on today's
main, with a positive control so the reading is a measurement:packages/spec/src/kernel/metadata-plugin.zod.ts:827 { type: 'flow', … supportsOverlay: false, allowOrgOverride: false, allowRuntimeCreate: true, … } content/docs/concepts/metadata-lifecycle.mdx:111 | `flow` | ✅ | Per-org overlays are allowed for automation definitions. | CTRL allowOrgOverride: true ×6 · false ×33 ← the probe is genuinely reading that fieldDirect contradiction confirmed. Opus rather than the floor because the ❌ has to preserve a two-tier fact — org overlay closed,
allowRuntimeCreatestilltrue(ADR-0070) — and a one-character flip is exactly the edit that flattens both tiers into one and makes the row wrong in the other direction.
Generated by Claude Code
{ "issue": 11664, "status": "done", "branch": "claude/issue-11664-flow-overlay-row", "pr": "https://github.com/objectstack-ai/objectstack/pull/11750", "premise_still_valid": true, "summary": "The premise held and understated the defect: the overlay-whitelist table in content/docs/concepts/metadata-lifecycle.mdx diverged from DEFAULT_METADATA_TYPE_REGISTRY on FOUR types, not one. `flow` ✅→❌ (rolled back #6283), `permission` ✅→❌ and `position` ✅→❌ (rolled back #6483, 2026-08-08 ruling), and `translation` was ABSENT from the table while the registry marks it allowOrgOverride:true — a false negative, so the table was wrong in both directions at once. Both corrected ❌ rows preserve the two-tier fact (allowRuntimeCreate stays true) using the Rationale column, which four existing rows (object/field/datasource/job) already use for exactly that — no new table convention was invented. Registry and conformance tests untouched: allowOrgOverride:false for `flow` is the correct outcome of the rollback. Docs-only, 1 file, +5/-3. The table is hand-maintained — verified before editing (no generator writes the page, no DO NOT EDIT marker, no gate compares table to registry), so the fix belongs in the page and not in a generator.", "tests": "All at final commit 180a416ac with a clean tree (git status --porcelain = 0 lines). NON-VACUITY BOTH DIRECTIONS via an AST-based comparator (typescript AST over DEFAULT_METADATA_TYPE_REGISTRY, not regex — several entries are multi-line; joined against the parsed mdx rows): BEFORE (git show HEAD:...mdx) exit 1 'VERDICT: 4 DIVERGENCE(S)' listing flow/permission/position mismatched + translation absent; AFTER exit 0 'VERDICT: CONSISTENT', 27 registry types vs 13 table types, zero registry-✅ types absent. The BEFORE run is the positive control, so the clean AFTER reading is a measurement, not a dead probe. Registry CTRL matched the dispatch premise exactly (allowOrgOverride true×6/false×33 by grep line count; AST resolves that to 5 true entries — the 6th grep line is a code comment at L789). SECOND SOURCE: content/docs/permissions/authorization.mdx:236 already stated `permission` declares allowOrgOverride:false, and content/docs/automation/jobs.mdx:42 already carried the correct `flow` two-tier fact — the table contradicted sibling docs pages too, and those pages needed no change. GATES: union derived by `node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack` (no hand-fed paths; script took its own change set — 17 families over 1 path), all re-run at 180a416ac, exit codes captured before any pipe (redirect-then-capture, never $? after a tail). All 17 + check:nul-bytes green. Quoted verdict lines the gates printed themselves: '✓ doc authoring guard: 389 files clean — no bare metadata literals.'; '✓ check-doc-frontmatter: 403 page(s) under content/docs parse with yaml@2.9.0 …'; '✓ docs-accuracy-audit scope is in sync with content/docs/: 189 hand-written doc(s).'; '✓ variant/doc gate: 18 discriminated union(s) — 8 governed, 10 exempt.'; '✓ check-nul-bytes --self-test: 75 assertions over a temp git repo'. doc-formula-expressions and doc-security-posture were RED on first run (ERR_MODULE_NOT_FOUND on packages/lint/dist) — the new-worktree missing-build trap, not this change; green after building the @objectstack/lint and @objectstack/spec dependency closures under the shared lock ('os-verify-lock: VERDICT command-exit 0 · held the lock 136s (2m16s) · waited 35s'). Every pnpm --filter run echoed its script name, so no zero-match silent-green. Control-byte self-scan on the edited file: no match, with a positive control proving the pattern is live. NOT MEASURED: CI gate job conclusions (report is delivered at draft-PR time per the dispatch contract; no CI wait).", "open_questions": [ { "question": "Should a gate assert the docs table against DEFAULT_METADATA_TYPE_REGISTRY? (Reporting only — not built, per dispatch instruction.)", "options": [ "A — Add a check that parses the mdx table and the registry and fails on any divergence, including a registry-✅ type absent from the table.", "B — Leave it to the docs-accuracy audit and human review." ], "recommendation": "A, and it is cheap. The comparison is fully mechanical — the throwaway comparator used for this PR's non-vacuity evidence already implements it end to end (AST-parse the registry, parse the table, join on type name) and correctly returned 4 divergences on the pre-fix file and 0 after. The drift went undetected long enough to reach a promo-video fact-check, and three of the four wrong rows came from rollbacks (#6283, #6483) whose authors correctly updated the registry and its code comments but had no mechanism telling them a docs table mirrored the column. Note the gate must check BOTH directions — a row that contradicts the registry AND a registry-✅ type missing from the table; `translation` was only findable by the second check. Not filed, per your instruction to file it yourself." }, { "question": "Your table-shape question: can the existing column semantics carry the two-tier distinction without inventing a convention?", "options": [ "A — Yes, the Rationale column already carries it; no shape change.", "B — No, it needs a footnote or a fourth column (allowRuntimeCreate)." ], "recommendation": "A — and this needed no judgment call, because the table already does it in four places: the `object`, `field`, `datasource` and `job` rows all state their allowRuntimeCreate value inside Rationale (e.g. datasource: '(`allowRuntimeCreate: true` — the datasource wizard persists origin: runtime rows.)'). I followed that existing precedent verbatim for `flow` and `permission`/`position`, so nothing new was smuggled into the table's shape. If you would still prefer an explicit fourth column, that is a clean follow-up — but it is a change to all 13 rows, not something this card should have decided." } ], "out_of_scope_findings": [ "NOT FILED, by instruction: the registry↔docs-table gate described in open_questions[0] — you said you would file it.", "No other out-of-scope defects found. The sibling docs pages that mention allowOrgOverride (permissions/authorization.mdx, permissions/capabilities.mdx, ai/agents.mdx, automation/jobs.mdx, api/declarative-endpoints.mdx, references/system/email-template.mdx) were swept and all already agree with the registry — metadata-lifecycle.mdx was the sole outlier." ] }Two procedural notes for the PM, not in the JSON:
-
skip-changesetwas applied via the read→union→whole-set fallback, declared as required. The additivePOST /issues/11750/labelswas unavailable from this seat (the rawcurlwas refused by the session's command classifier, andghis not installed in this container), so I read the label set first (size/xs), wrote the union["size/xs","skip-changeset"], then read back. Read-back:documentation,size/xs,skip-changeset— all three present, nothing clobbered (documentationwas added by the auto-labeler after my write, additively). The label's own description confirms the choice: "PR has no user-facing published change; bypasses the changeset gate." Justified against the actual rule rather than assumed:pr-automation.yml'schangeset-checkhas no path-based docs carve-out (itsifexempts only the label and thechangeset-release/mainbranch), and AGENTS.md:942 states "Pure bug fixes do not require a changeset." This PR touches one file undercontent/docs/and zero package source, so it publishes nothing. -
No
domain:*label was produced and the issue's assignee/claim were not touched, per dispatch.
Generated by Claude Code
Generated by Claude Code
-
os-dev-report
(Re-posting the machine-readable half: in the previous comment the
<!--marker-->was eaten by the GitHub body sanitizer after it landed, so that comment is invisible to a marker scan. Same JSON, byte-identical. The two procedural notes — theskip-changesetlabel fallback and its read-back — remain in that earlier comment.){ "issue": 11664, "status": "done", "branch": "claude/issue-11664-flow-overlay-row", "pr": "https://github.com/objectstack-ai/objectstack/pull/11750", "premise_still_valid": true, "summary": "The premise held and understated the defect: the overlay-whitelist table in content/docs/concepts/metadata-lifecycle.mdx diverged from DEFAULT_METADATA_TYPE_REGISTRY on FOUR types, not one. `flow` YES->NO (rolled back #6283), `permission` YES->NO and `position` YES->NO (rolled back #6483, 2026-08-08 ruling), and `translation` was ABSENT from the table while the registry marks it allowOrgOverride:true — a false negative, so the table was wrong in both directions at once. Both corrected NO rows preserve the two-tier fact (allowRuntimeCreate stays true) using the Rationale column, which four existing rows (object/field/datasource/job) already use for exactly that — no new table convention was invented. Registry and conformance tests untouched: allowOrgOverride:false for `flow` is the correct outcome of the rollback. Docs-only, 1 file, +5/-3. The table is hand-maintained — verified before editing (no generator writes the page, no DO NOT EDIT marker, no gate compares table to registry), so the fix belongs in the page and not in a generator.", "tests": "All at final commit 180a416ac with a clean tree (git status --porcelain = 0 lines). NON-VACUITY BOTH DIRECTIONS via an AST-based comparator (typescript AST over DEFAULT_METADATA_TYPE_REGISTRY, not regex — several entries are multi-line; joined against the parsed mdx rows): BEFORE (git show HEAD:...mdx) exit 1 'VERDICT: 4 DIVERGENCE(S)' listing flow/permission/position mismatched + translation absent; AFTER exit 0 'VERDICT: CONSISTENT', 27 registry types vs 13 table types, zero registry-YES types absent. The BEFORE run is the positive control, so the clean AFTER reading is a measurement, not a dead probe. Registry CTRL matched the dispatch premise exactly (allowOrgOverride true x6 / false x33 by grep line count; AST resolves that to 5 true entries — the 6th grep line is a code comment at L789). SECOND SOURCE: content/docs/permissions/authorization.mdx:236 already stated `permission` declares allowOrgOverride:false, and content/docs/automation/jobs.mdx:42 already carried the correct `flow` two-tier fact — the table contradicted sibling docs pages too, and those pages needed no change. GATES: union derived by `node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack` (no hand-fed paths; script took its own change set — 17 families over 1 path), all re-run at 180a416ac, exit codes captured before any pipe (redirect-then-capture, never $? after a tail). All 17 + check:nul-bytes green. Quoted verdict lines the gates printed themselves: 'doc authoring guard: 389 files clean — no bare metadata literals.'; 'check-doc-frontmatter: 403 page(s) under content/docs parse with yaml@2.9.0'; 'docs-accuracy-audit scope is in sync with content/docs/: 189 hand-written doc(s).'; 'variant/doc gate: 18 discriminated union(s) — 8 governed, 10 exempt.'; 'check-nul-bytes --self-test: 75 assertions over a temp git repo'. doc-formula-expressions and doc-security-posture were RED on first run (ERR_MODULE_NOT_FOUND on packages/lint/dist) — the new-worktree missing-build trap, not this change; green after building the @objectstack/lint and @objectstack/spec dependency closures under the shared lock ('os-verify-lock: VERDICT command-exit 0 · held the lock 136s (2m16s) · waited 35s'). Every pnpm --filter run echoed its script name, so no zero-match silent-green. Control-byte self-scan on the edited file: no match, with a positive control proving the pattern is live. NOT MEASURED: CI gate job conclusions (report delivered at draft-PR time per the dispatch contract; no CI wait).", "open_questions": [ { "question": "Should a gate assert the docs table against DEFAULT_METADATA_TYPE_REGISTRY? (Reporting only — not built, per dispatch instruction.)", "options": [ "A — Add a check that parses the mdx table and the registry and fails on any divergence, including a registry-YES type absent from the table.", "B — Leave it to the docs-accuracy audit and human review." ], "recommendation": "A, and it is cheap. The comparison is fully mechanical — the throwaway comparator used for this PR's non-vacuity evidence already implements it end to end (AST-parse the registry, parse the table, join on type name) and correctly returned 4 divergences on the pre-fix file and 0 after. The drift went undetected long enough to reach a promo-video fact-check, and three of the four wrong rows came from rollbacks (#6283, #6483) whose authors correctly updated the registry and its code comments but had no mechanism telling them a docs table mirrored the column. Note the gate must check BOTH directions — a row that contradicts the registry AND a registry-YES type missing from the table; `translation` was only findable by the second check. Not filed, per your instruction to file it yourself." }, { "question": "Your table-shape question: can the existing column semantics carry the two-tier distinction without inventing a convention?", "options": [ "A — Yes, the Rationale column already carries it; no shape change.", "B — No, it needs a footnote or a fourth column (allowRuntimeCreate)." ], "recommendation": "A — and this needed no judgment call, because the table already does it in four places: the `object`, `field`, `datasource` and `job` rows all state their allowRuntimeCreate value inside Rationale (e.g. datasource: '(allowRuntimeCreate: true — the datasource wizard persists origin: runtime rows.)'). I followed that existing precedent verbatim for `flow` and `permission`/`position`, so nothing new was smuggled into the table's shape. If you would still prefer an explicit fourth column, that is a clean follow-up — but it is a change to all 13 rows, not something this card should have decided." } ], "out_of_scope_findings": [ "NOT FILED, by instruction: the registry-vs-docs-table gate described in open_questions[0] — you said you would file it.", "No other out-of-scope defects found. The sibling docs pages that mention allowOrgOverride (permissions/authorization.mdx, permissions/capabilities.mdx, ai/agents.mdx, automation/jobs.mdx, api/declarative-endpoints.mdx, references/system/email-template.mdx) were swept and all already agree with the registry — metadata-lifecycle.mdx was the sole outlier." ] }
Generated by Claude Code
Generated by Claude Code
What
The "Overlay whitelist (shared-DB tenancy invariant)" table in
content/docs/concepts/metadata-lifecycle.mdxlists:But the registry — which that very section declares to be the single machine-readable source ("The whitelist lives in one place:
MetadataTypeRegistryEntry.allowOrgOverride") — says the opposite (packages/spec/src/kernel/metadata-plugin.zod.ts,flowentry):The adjacent code comment documents the rationale (the #6190 phantom: an org-scoped flow overlay wrote successfully and lost its binding on the next cold start, because the loader cannot merge per-org flow overlays; rolling the flag back turned the silent phantom into a loud
403 not_overridable). Conformance tests pin the refusal:save-meta-response-conformance.test.ts("allowOrgOverride: falseforflow, so an org-scoped write is …") andpublish-meta-response-conformance.test.ts("org-scoped draft is refused").Why it matters
The table tells an integrator that an org-scoped flow overlay will persist; in reality the write 403s. Anyone building on the docs row plans a customization path that does not exist.
Suggested fix
flowrow to ❌ with the two-tier note (org overlay closed;allowRuntimeCreatestaystrue— brand-new runtime flows remain supported per ADR-0070 package-first authoring).permission,position, …) against the registry — the table appears to predate the org 作用域的 flow overlay 只在「本进程内发布后」绑定触发器,重启后静默失绑——冷启动两条读路径都把 organization_id 非空的行滤掉了 #6190 rollback.Found while fact-checking a promo video's on-screen claims against the runtime (video-studio #4). Filing as a finding — unassigned, not claimed.