Repository navigation
spec: rename allowOrgOverride to an environment-overlay key with an ADR-0087 load-time conversion (ADR-0131 C5's spec half, split from #15206 per #22007 ruling C) #22340
Description
Activity
- addedpriority:p1High: required for production / M2High: required for production / M2and removed
on Oct 8, 2026 objectstack-fleet commented
on Oct 8, 2026 ContributorAuthorMore actionsClaim: PM loop round 1 (stage 0 of this card: measure first, no code, no PR) · 2026-10-08T21:20Z
Session:session_01DhTqaEHqPVSVnAkjG3jywn
Account:os-sales(the seat's linked user asGET /useranswers it; the card's assignee from this act)
Branch:claude/issue-22340-stage-0(stage 0 writes no code; each later stage names its own branch on this card)
Worktree:objectstack-issue-22340
Domain:domain:spec
Seat:domain:spec#2(seat post #18549)
File surface (atorigin/main54c3ce10cor later): stage 0 writes no file and opens no PR. The report is the deliverable. It contains:- Census: every reader and writer of
allowOrgOverride(176 files onmainat this stamp, 60 hits inmetadata-protocol/src/protocol.tsalone), classified by what each one means:- (a) the per-organization overlay path that feat(metadata-core,metadata-protocol,objectql,plugin-security): the
sys_metadatafamily goes tenant-less; the per-organization overlay axis retires; managed content is sealed (ADR-0131 D6/D7/D13) #15206 stage (4) deletes (isOverlayAllowedand the organization-scoped write doors); - (b) the environment-overlay permission that survives the rename;
- (c) declaration, docs, CHANGELOG history and governed text (
docs/adr/0005,docs/adr/0010, anything underskills/**), each named for its carrier; - (d) tests, by which of (a) / (b) they pin.
- (a) the per-organization overlay path that feat(metadata-core,metadata-protocol,objectql,plugin-security): the
- The five types that enable the key today (view, dashboard, report, translation, email template): where each one's environment overlay is read and written, so that "no overlay is lost" can be pinned per type.
- The ADR-0087 D2 conversion: where a manifest that names the key enters (the type registry in
packages/spec/src/kernel/metadata-plugin.zod.tsabout:267,DEFAULT_METADATA_TYPE_REGISTRY, and plugin manifests). Whethermigrations/registry.ts/conversions/registry.tshas a precedent for a renamed registry key. Which generated baselines name the key. - Name candidates for the contract review to fix (the card says the name is fixed there, not here). Each candidate comes with the four-axis reading, and with every existing spec key it could collide with or be confused with.
- A stage plan: each stage's file surface, its serial constraints against feat(metadata-core,metadata-protocol,objectql,plugin-security): the
sys_metadatafamily goes tenant-less; the per-organization overlay axis retires; managed content is sealed (ADR-0131 D6/D7/D13) #15206's stages (engine seat, claim6067242116; stage 0 is reported in6067752061) and the open PRs, and the contract-tier review each stage owes.
Container & model:XLstaged,mode:subagentfor stage 0 (read-only),model: opus(dispatch-gates --tier: no path-derived mandate for a measurement). The code stages areClause-②: yes (widening), and each owes a contract review.
Clause-②: yes (widening: a new published key with an ADR-0087 load-time conversion fromallowOrgOverride; stage 0 itself changes nothing)
Responsibility: n/a — not a defect card (ADR-0131 C5's spec half, ruled under decision: ADR-0131 C5 —allowOrgOverridealso decides environment overlays of packaged items. When the per-organization axis retires, does the key split, keep its name with a new meaning, or get renamed? #22007 letter C, split from feat(metadata-core,metadata-protocol,objectql,plugin-security): thesys_metadatafamily goes tenant-less; the per-organization overlay axis retires; managed content is sealed (ADR-0131 D6/D7/D13) #15206 by triage6066851770)
Thread-read: none
Serial constraints cleared: stage 0 writes no file. feat(metadata-core,metadata-protocol,objectql,plugin-security): thesys_metadatafamily goes tenant-less; the per-organization overlay axis retires; managed content is sealed (ADR-0131 D6/D7/D13) #15206 (engine seat 1, staged,pm:dispatched) will editmetadata-protocolprotocol.tsand delete the per-organization path that reads this key. So the code stages' order against it is part of stage 0's plan, and is not assumed "in parallel".
Generated by Claude Code
- Census: every reader and writer of
objectstack-fleet commented
on Oct 8, 2026 ContributorAuthorMore actionsos-dev-report
{ "issue": 22340, "status": "done", "branch": "claude/issue-22340-stage-0", "pr": null, "session": "session_01DhTqaEHqPVSVnAkjG3jywn (subagent of the PM session; identity = the branch named in claim 6069288725)", "premise_still_valid": true, "summary": "Stage 0 measured on origin/main 54c3ce10c (worktree ../objectstack-issue-22340, read-only, no install, no build; main moved to b7e01fbbd during the run, one commit, 0 diff lines name the key). The direction (rename, ruling C) stands, but the ruled mechanism does not: no manifest, stack, plugin config or stored row can carry a type-registry entry (contributes.kinds is a closed shape, config.additionalTypes is a tombstone, DEFAULT_METADATA_TYPE_REGISTRY is a TypeScript literal that no production code parses), so an ADR-0087 D2 conversion would have no seam; the recorded precedent for renaming a non-stack key is a retiredKey() tombstone + a retired-keys entry + a D3 semantic entry, with no D2 entry (HotReloadConfig.debounceDelay). The rename also reaches a published wire field the card does not name, GET /meta/types entries[].allowOrgOverride (protocol.zod.ts:210), which objectui reads at the pin in 11 source files as Studio's write gate for every packaged item of the five types, through its own optional field type, so by reading neither the Console Pin Gate nor api-surface/authorable-surface would see a missed sibling. Census: 176 files / 765 hits. 46 source hits (14 files) and 120 test hits (32 files) sit in the per-organization path that #15206 S3-S5 delete, so the rename should land after #15206 S5; before S3/S4 a key with an environment name would still decide organization-scoped writes. The rename's declaration is Clause-② yes (narrowing), not yes (widening). Report and translation have no environment-overlay pin today.", "census": { "base": "origin/main 54c3ce10c (54c3ce10ce8cf89290b67813c34d1f10dbab6938). Re-read at b7e01fbbd (#22342, test titles only): git diff 54c3ce10c b7e01fbbd names the key on 0 lines.", "command_and_controls": "git grep -n allowOrgOverride 54c3ce10c = 765 lines in 176 files (the claim's 176 files and 60 hits in protocol.ts both reproduce). Pathspec proof: ':(glob)packages/**/src/**' key 113 files, controls DEFAULT_METADATA_TYPE_REGISTRY 95 and isOverlayAllowed 13; the plain pathspec 'packages/**/src' answers 0 for the key AND 0 for the control (vacuous, no glob magic). ':(glob)**/*.test.ts' key 80, control 'describe(' 4824. ':(glob)content/docs/**' key 14, control ADR-0005 12.", "method": "Every hit line was read and classed by what it governs or describes; comments are classed with the code they annotate. Three source files split by line (protocol.ts 19 a / 41 b, spec api/protocol.zod.ts 1 a / 3 b, runtime domains/meta.ts 3 a / 1 b). Counts are path:hits (path:class-hits/file-hits for a split file).", "class_totals": "(a) per-organization path: 46 hits, 14 files (11 wholly + 3 split). (b) environment-overlay permission that survives: 75 hits, 19 files (16 wholly + 3 split). (c) declaration 47/1, docs 64/23, generated 16/8, ADR-0087 ledger prose 4/4, tooling 28/3, history (CHANGELOG 147/10 + pending changesets 3/2) 150/12, governed 48/15. (d) tests: pinning (a) 120 hits / 32 files, pinning (b) 156 / 46, mixed 11 / 2. Sum 765.", "a_per_org_path": { "files": "packages/metadata-protocol/src/protocol.ts:19/60, packages/spec/src/api/protocol.zod.ts:1/4, packages/runtime/src/domains/meta.ts:3/4, packages/metadata-core/src/meta-write-capability.ts:1, packages/metadata-core/src/meta-write-org-scope.ts:6, packages/plugins/plugin-email/src/bootstrap-declared-email-templates.ts:1, packages/rest/src/meta-item-read-gate.ts:1, packages/rest/src/rest-route-ledger.ts:1, packages/rest/src/rest-server.ts:5, packages/runtime/src/domains/mcp.ts:1, packages/runtime/src/domains/packages.ts:3, packages/runtime/src/route-ledger.ts:1, packages/services/service-automation/src/sys-flow-credential.object.ts:1, packages/spec/src/security/capabilities.ts:2", "by_15206_stage": "S3 (doors): meta-write-org-scope.ts 6 (ORG_OVERRIDABLE_TYPES :96-100 reads the flag; declaresOrgOverride feeds organizationIdForMetaWrite :141 and organizationIdForMetaRead :189), meta-write-capability.ts 1, spec security/capabilities.ts 2 (manage_org_presentation :47/:62), rest meta-item-read-gate.ts 1, rest-route-ledger.ts 1, rest-server.ts 5, runtime domains/meta.ts 3, runtime route-ledger.ts 1, plugin-email bootstrap-declared-email-templates.ts 1 = 21. S4 (protocol writes): protocol.ts orgScopedWriteRefusal TSDoc and sentence :16136/:16140/:16168/:16215 (its exemption is :16210 'if (this.isOverlayAllowed(type)) return null'), resolveMetaItemOrgScope note :7144, applyRegistryWriteThrough note :19222, duplicatePackage note :24793; runtime domains/packages.ts 3, domains/mcp.ts 1, service-automation sys-flow-credential.object.ts 1, spec api/protocol.zod.ts:676 (SaveMetaItem organizationId describe) = 13. S5 (reads): protocol.ts :8927, :10064, :10145, :10719, :10778, :11537, :11544, :14732 (organization read-gate notes) and reportUnhydratableOrgScopedRows :27457/:27498/:27554 (reads the flag)/:27640 (sentence) = 12." }, "b_environment_overlay_permission": { "files": "packages/metadata-protocol/src/protocol.ts:41/60, packages/spec/src/api/protocol.zod.ts:3/4, packages/runtime/src/domains/meta.ts:1/4, examples/app-showcase/src/coverage.ts:1, packages/lint/src/validate-ai-agent-authoring.ts:1, packages/metadata-core/src/contract-suite.ts:1, packages/metadata-core/src/types.ts:1, packages/metadata-protocol/src/package-writability.ts:1, packages/metadata-protocol/src/sys-metadata-repository.ts:13, packages/objectql/src/engine.ts:1, packages/platform-objects/src/audit/sys-job.object.ts:1, packages/plugins/plugin-security/src/object-posture-gate.ts:1, packages/plugins/plugin-security/src/permission-set-overlay-discard.ts:1, packages/plugins/plugin-security/src/permission-set-projection.ts:2, packages/plugins/plugin-security/src/security-plugin.ts:2, packages/spec/src/kernel/index.ts:1, packages/spec/src/kernel/metadata-create-seeds.ts:1, packages/spec/src/kernel/metadata-type-schemas.ts:1, packages/spec/src/system/email-template.zod.ts:1", "code_reads": "protocol.ts:15845 (OVERLAY_ALLOWED_TYPES, read by isOverlayAllowed :15976, whose callers are refusePackagedBaseOverride :17171, refusePackagedBaseRemoval :17278, saveMetaItem :20172, migrateStoredMetadata :21644, historyMetaItem :21830, publish/promote :22266, rollbackMetaItem :26075, deleteMetaItem :26619/:26722, and orgScopedWriteRefusal :16210 which S4 deletes); sys-metadata-repository.ts:316 (its own OVERLAY_ALLOWED_TYPES, read by assertAllowed :1789/:1849); protocol.ts getMetaTypes :7883/:7884/:7953 (the wire field and its env-hatch elevation).", "author_facing_strings": "protocol.ts :16099 and :16118 (code-only refusals), :17247 (package-door sentence 'has not opted into per-org overlay writes (allowOrgOverride=false)'), :21650 (migrate-stored reason); sys-metadata-repository.ts :1854, :1855, :1975, :1983. All name the key and survive into the rename." }, "c_declaration_docs_history_governed": { "declaration": "packages/spec/src/kernel/metadata-plugin.zod.ts:47 (key :267, TSDoc :253-266, 28 registry rows, the customizationPolicies tombstone prescription :513-520 that ships on 17.x and names the key, and about 15 rationale comments).", "docs": "packages/spec/liveness/README.md:2, packages/spec/liveness/capability.json:1, packages/spec/liveness/job.json:1, docs/qa/platform-checklist/areas/studio-authoring.json:18, content/docs/concepts/metadata-lifecycle.mdx:8, docs/qa/platform-checklist/areas/platform-core.json:5, docs/qa/platform-checklist/areas/ai.json:3, content/docs/plugins/adding-a-metadata-type.mdx:3, docs/qa/platform-checklist/areas/records-forms.json:2, docs/qa/platform-checklist/areas/integration-system.json:2, docs/qa/platform-checklist/areas/automation.json:2, docs/qa/platform-checklist/FOLLOW-UPS.md:2, docs/audits/2026-06-ask-build-agent-development-assessment.md:2, content/docs/permissions/capabilities.mdx:2, content/docs/kernel/contracts/metadata-service.mdx:2, content/docs/ai/agents.mdx:2, docs/qa/platform-checklist/RUNNER.md:1, content/docs/protocol/objectui/concept.mdx:1, content/docs/permissions/authorization.mdx:1, content/docs/deployment/environment-variables.mdx:1, content/docs/automation/jobs.mdx:1, content/docs/api/metadata-api.mdx:1, content/docs/api/declarative-endpoints.mdx:1", "generated": "packages/spec/src/migrations/registry.ts:4, packages/spec/authorable-defaults/kernel.json:1, packages/spec/authorable-surface.base.json:1, packages/spec/authorable-surface/kernel.json:1, content/docs/references/api/protocol.mdx:3, content/docs/references/kernel/metadata-plugin.mdx:3, content/docs/references/system/email-template.mdx:1, docs/protocol-upgrade-guide.md:2", "adr_0087_ledger_prose": "packages/spec/src/migrations/entries/retired-keys/18.kernel__MetadataPluginConfig__customizationPolicies.ts:1, packages/spec/src/migrations/entries/semantic/17.api-runtime-create-withdrawn.ts:1, packages/spec/src/migrations/entries/semantic/17.field-runtime-create-withdrawn.ts:1, packages/spec/src/migrations/entries/semantic/18.metadata-customization-protocol-retired.ts:1. The two 17.* entries describe the shipped v17 refusal body and stay; the two 18.* entries are unreleased (spec latest 17.7.0, pre mode 'next') and present the key as the live mechanism.", "tooling": "scripts/check-overlay-whitelist-table.mjs:26, scripts/adr-anchors/packages__spec__src__kernel__metadata-plugin.zod.ts.json:1, scripts/adr-anchors/packages__plugins__plugin-security__src__permission-set-projection.ts.json:1", "history_never_edited": ".changeset/22203-position-package-door.md:1, .changeset/22220-package-door-before-gates.md:2, packages/metadata-protocol/CHANGELOG.md:58, packages/spec/CHANGELOG.md:33, packages/runtime/CHANGELOG.md:15, packages/rest/CHANGELOG.md:13, packages/objectql/CHANGELOG.md:10, packages/plugins/plugin-security/CHANGELOG.md:5, packages/lint/CHANGELOG.md:5, packages/metadata-core/CHANGELOG.md:4, packages/platform-objects/CHANGELOG.md:3, packages/metadata/CHANGELOG.md:1. The two .changeset files are other PRs' pending release inputs.", "governed": "docs/adr/0005-metadata-customization-overlay.md:12, docs/adr/0010-metadata-protection-model.md:9, docs/adr/0094-sys-permission-set-pure-projection.md:5, docs/adr/0070-package-first-authoring.md:5, docs/adr/0029-kernel-object-ownership-and-platform-objects-decomposition.md:4, docs/adr/0126-packaged-metadata-customization-model.md:2, docs/adr/0086-authz-metadata-config-boundary-and-cross-package-composition.md:2, docs/adr/0046-package-docs-as-metadata.md:2, skills/objectstack-ai/SKILL.md:1, docs/adr/0131-total-organization-ownership-no-null-organization-id.md:1, docs/adr/0109-ai-tool-authoring-model.md:1, docs/adr/0063-two-kernel-agents-skills-are-the-extension-primitive.md:1, docs/adr/0027-metadata-authoring-lifecycle.md:1, docs/adr/0015-external-datasource-federation.md:1, AGENTS.md:1", "governed_lines_a_carrier_must_change": "AGENTS.md:190-192 (Prime Directive #7: 'Org overlay opt-in lives only in allowOrgOverride on DEFAULT_METADATA_TYPE_REGISTRY', false twice once #15206 and this card land); skills/objectstack-ai/SKILL.md:347 (published skill, 'allowOrgOverride:false' on agent); docs/adr/0005 :47 and :68 (normative current-state sentences; the file already carries the v5.0 rename note at :3 as the shape for a top note); docs/adr/0010 :19, :94, :119-122 (the L1 type-level knob). The remaining ADR hits (0015, 0027, 0029, 0046, 0063, 0070, 0086, 0094, 0109, 0126, 0131) record decisions under the name of their time and need no rewrite; 0029:386 carries an ungated '#allowOrgOverride' symbol anchor. .claude/** has 0 hits." }, "d_tests": { "pins_a": "packages/metadata-core/src/meta-write-capability.test.ts:4, packages/metadata-core/src/meta-write-org-scope.test.ts:2, packages/metadata-protocol/src/get-meta-item-cached-etag-scope.test.ts:8, packages/metadata-protocol/src/get-meta-item-layered-org-read-gate.test.ts:4, packages/metadata-protocol/src/get-meta-item-org-read-gate.test.ts:4, packages/metadata-protocol/src/get-meta-items-org-read-gate.test.ts:3, packages/metadata-protocol/src/protocol-publish-drafts-advisories.test.ts:1, packages/metadata-protocol/src/protocol-publish-drafts-org-scope.test.ts:3, packages/metadata-protocol/src/protocol-publish-drafts-package-scope.test.ts:1, packages/metadata-protocol/src/protocol.lock-org-axis-agree.test.ts:1, packages/metadata-protocol/src/protocol.metadata-store-outage.test.ts:1, packages/metadata-protocol/src/protocol.org-scoped-cold-boot-audit-live-registry.test.ts:1, packages/metadata-protocol/src/protocol.org-scoped-cold-boot-audit.test.ts:6, packages/metadata-protocol/src/protocol.org-scoped-write-refused.test.ts:9, packages/metadata-protocol/src/protocol.publish-item-draft-org-scope.test.ts:2, packages/objectql/src/overlay-precedence.test.ts:16, packages/objectql/src/protocol-meta.test.ts:10, packages/objectql/src/protocol-org-overlay-registry-gate.test.ts:2, packages/objectql/src/publish-meta-response-conformance.test.ts:2, packages/objectql/src/publish-package-drafts-response-conformance.test.ts:1, packages/rest/src/meta-item-save-capability-gate.test.ts:1, packages/rest/src/meta-publish-package-scope.test.ts:1, packages/rest/src/meta-write-door-capability-enumeration.test.ts:2, packages/rest/src/rest-server-meta-cached-etag-door-scope.test.ts:2, packages/rest/src/rest-server-meta-history-diff-org-scope.test.ts:4, packages/rest/src/rest-server-meta-org-scope-url-spelling.test.ts:2, packages/rest/src/rest-server-meta-read-org-scope.test.ts:7, packages/rest/src/rest-server-meta-write-org-scope.test.ts:4, packages/runtime/src/domains/meta-save-capability-gate.test.ts:2, packages/runtime/src/domains/packages-seed-apply-org-scope.test.ts:3, packages/runtime/src/meta-write-org-scope.test.ts:10, packages/runtime/src/package-duplicate-adopt-org-scope.integration.test.ts:1", "pins_a_by_15206_stage": "S3 14 files / 45 hits (the core and runtime meta-write-org-scope tests, the capability-gate and door tests in rest and runtime, the rest org-scope read/write/history/etag/url-spelling suites, packages-seed-apply and package-duplicate-adopt); S4 9 files / 45 (protocol.org-scoped-write-refused, the identity pin, :550 lists exactly the five; publish-item-draft and publish-drafts org/package scope and advisories; objectql overlay-precedence, protocol-meta, publish-meta and publish-package-drafts conformance); S5 9 files / 30 (the four get-meta-item(s) read-gate suites incl. cached-etag, lock-org-axis-agree, metadata-store-outage, both cold-boot audits, objectql protocol-org-overlay-registry-gate).", "pins_b": "packages/metadata-protocol/src/protocol.adr0005-org-override-rollback.test.ts:9, packages/metadata-protocol/src/protocol.capability-write-door.test.ts:1, packages/metadata-protocol/src/protocol.code-only-types.test.ts:5, packages/metadata-protocol/src/protocol.delete-receipt-wording.test.ts:7, packages/metadata-protocol/src/protocol.delete-rewrap-envelope.test.ts:4, packages/metadata-protocol/src/protocol.destructive-gate-reachable-types.test.ts:1, packages/metadata-protocol/src/protocol.diff-dead-history-read.test.ts:1, packages/metadata-protocol/src/protocol.driver-text-disclosure.test.ts:3, packages/metadata-protocol/src/protocol.legacy-overlay-delete.test.ts:10, packages/metadata-protocol/src/protocol.marked-refusal-classification.test.ts:1, packages/metadata-protocol/src/protocol.package-door-before-gates.test.ts:3, packages/metadata-protocol/src/protocol.packaged-base-refusal.test.ts:2, packages/metadata-protocol/src/protocol.read-lock-flags-write-door.test.ts:2, packages/metadata-protocol/src/protocol.read-verb-canonical-fold.test.ts:3, packages/metadata-protocol/src/protocol.recovery-doors-emit-mutation.test.ts:1, packages/metadata-protocol/src/protocol.runtime-gate-stored-universe.test.ts:1, packages/metadata-protocol/src/protocol.save-receipt-wording.test.ts:6, packages/metadata-protocol/src/protocol.stored-migration.test.ts:2, packages/metadata-protocol/src/protocol.unrecognised-meta-type.test.ts:1, packages/metadata-protocol/src/sys-metadata-repository.contract.test.ts:3, packages/metadata-protocol/src/sys-metadata-repository.package-writability.test.ts:14, packages/objectql/src/engine-security-catalog-package-door.test.ts:9, packages/objectql/src/meta-object-search-companion-roundtrip.test.ts:1, packages/objectql/src/protocol-commit-history.test.ts:7, packages/objectql/src/protocol-delete-object-registry-heal.test.ts:1, packages/objectql/src/protocol-meta-effective-schema.test.ts:1, packages/objectql/src/protocol-meta-types-rich.test.ts:11, packages/objectql/src/protocol-object-overlay-layer.test.ts:2, packages/objectql/src/protocol-publish-canonical-fold.test.ts:1, packages/objectql/src/protocol-registry-shadow.test.ts:2, packages/objectql/src/protocol-writepath-object-ownership.test.ts:1, packages/objectql/src/sys-metadata-repository.test.ts:4, packages/platform-objects/src/audit/sys-job.global-unique.test.ts:5, packages/plugins/plugin-security/src/packaged-permission-set-restore-leg.test.ts:1, packages/plugins/plugin-security/src/permission-set-projection.test.ts:3, packages/qa/dogfood/test/showcase-permission-projection.dogfood.test.ts:3, packages/qa/dogfood/test/two-doors-permission.dogfood.test.ts:3, packages/rest/src/rest-meta-packaged-flow-refusal.test.ts:1, packages/runtime/src/meta-field-overlay-lock.test.ts:4, packages/runtime/src/meta-overlay-read-your-writes.test.ts:2, packages/spec/src/data/picklist.test.ts:1, packages/spec/src/kernel/capability-metadata-kind.test.ts:3, packages/spec/src/kernel/metadata-create-seeds.test.ts:2, packages/spec/src/kernel/metadata-customization-retirement.test.ts:1, packages/spec/src/kernel/metadata-type-api-registration.test.ts:4, packages/spec/src/kernel/metadata-type-field-registration.test.ts:3", "mixed": "packages/metadata-protocol/src/protocol.flow-org-override-closed.test.ts:9, packages/objectql/src/save-meta-response-conformance.test.ts:2 (flow-org-override-closed pins flow's flag false (b) and its organization-scoped refusal (a); save-meta-response-conformance :239 code-only (b), :309 organization-scoped refusal (a)).", "message_text_pins": "Tests that assert a runtime sentence naming the key: (b) protocol.delete-rewrap-envelope.test.ts:343, protocol.legacy-overlay-delete.test.ts:428, protocol.packaged-base-refusal.test.ts:150, sys-metadata-repository.package-writability.test.ts:1350, objectql protocol-commit-history.test.ts:667 and :865, plugin-security packaged-permission-set-restore-leg.test.ts:211 and permission-set-projection.test.ts:156, rest rest-meta-packaged-flow-refusal.test.ts:117, spec metadata-customization-retirement.test.ts:58 (regex on the 17.x tombstone prescription); (a) protocol.org-scoped-write-refused.test.ts:421." }, "sibling_repos": "objectui at the .objectui-sha pin a58626c88 (commit fetched into the shared objectui object store; no checkout moved): 57 files / 154 hits = 11 source files / 49 hits (metadata-admin useMetadata.ts:77 declares its own optional 'allowOrgOverride?: boolean'; registry.ts 3, ResourceEditPage.tsx 12, PermissionMatrixEditor.tsx 17, DirectoryPage.tsx 3, ResourceListPage.tsx 3, PageShell.tsx 1, StudioHomePage.tsx 1, EmbeddedItemEditor.tsx 1, previews/PicklistPreview.tsx 1, i18n.ts 5 user-visible strings naming the key) + 43 test files / 95 + 3 CHANGELOG / 10. cloud: NOT MEASURED, reason: this session's GitHub proxy is repository-scoped (search/code answers 403) and no cloud checkout exists here." }, "five_types": { "verified": "DEFAULT_METADATA_TYPE_REGISTRY parsed from source at 54c3ce10c: 28 entries; the flag is true on exactly view (metadata-plugin.zod.ts:855), dashboard (:857), report (:863), translation (:1070), email_template (:1071). supportsOverlay true with the flag false: page, app, dataset, book, permission, position, tool, skill. The card's list holds.", "write_path_all_five": "REST PUT /meta/:type/:name rest-server.ts:7050-7052 then p.saveMetaItem :7307; dispatcher runtime/src/domains/meta.ts:1287 then saveMetaItem :1464. protocol.ts saveMetaItem :20023: type gate :20172 (isOverlayAllowed :15976, OVERLAY_ALLOWED_TYPES :15842-15851, the flag read at :15845, OS_METADATA_WRITABLE hatch envWritableTypes :15865), package door :20317 into refusePackagedBaseOverride :17168 (:17171), then repo.put :21129 into SysMetadataRepository.assertAllowed sys-metadata-repository.ts:1781 (:1789, set at :314-317, flag read at :316). Reset/remove: deleteMetaItem :26576 (:26619, :26722).", "read_path_all_five": "No served read consults the flag; the read keys on supportsOverlay (mergesOverlayAtRead :15969). getMetaItem :10046 via findServedOverlayRow :9976 and servedOverlayRowCandidates :2032; getMetaItems :8851 via mergePackageAwareOverlay :2160 (:9120); getMetaItemLayered :10647. A server-side rename therefore cannot hide a stored overlay; the loss risks are the two write-side derivations and the Studio gate.", "studio_gate_all_five": "GET /meta/types: protocol.ts getMetaTypes :7830 emits the flag (:7883-7886, synthesized :7953), typed by spec api/protocol.zod.ts:210 (GetMetaTypesResponseSchema :202) and by @objectstack/client meta.getTypes (client/src/index.ts:1754, :7629). objectui pin a58626c88 ResourceEditPage.tsx:1357-1359 and :1775-1778: canWriteByType for an artifact-backed item is !!entry.allowOrgOverride. With the wire renamed and objectui unchanged, every packaged item of the five types opens read-only in Studio: the overlay is lost at the authoring door, silently.", "view": "Flag :855. Pins of an environment overlay of a packaged item: runtime/src/meta-field-overlay-lock.test.ts:552 (dispatcher PUT, 200, row stored), metadata-protocol/src/protocol.packaged-base-refusal.test.ts:94 (package-door verdict null for save and delete), metadata-protocol/src/sys-metadata-repository.package-writability.test.ts:325 (repository put lands); served read: qa/dogfood view-container-cross-package-default.dogfood.test.ts:113 (names no key); wire: objectql/src/protocol-meta-types-rich.test.ts:91.", "dashboard": "Flag :857. Pin: runtime/src/meta-field-overlay-lock.test.ts:563 (dispatcher PUT over the packaged system_overview, 200, row stored). No served-read or wire pin found.", "report": "Flag :863. NO pin of an environment overlay of a packaged report found. Its only pins are organization-scoped, class (a): objectql/src/overlay-precedence.test.ts:154 (organizationId org_alpha, not a packaged item) and the five-type identity pin protocol.org-scoped-write-refused.test.ts:550; #15206 S4 flips both.", "translation": "Flag :1070. Extra reader: core/src/fallbacks/authored-translation-sync.ts:115 readAuthoredTranslationLayer (sys_metadata type translation, every organization, :126) into the i18n authored layer; it reads no flag. NO pin of an environment overlay of a packaged translation found; only the (a) identity pin :550.", "email_template": "Flag :1071. Extra reader: plugin-email/src/email-plugin.ts:1325 readEffectiveTemplate (getMetaItem :1335, projected into sys_email_template); bootstrap-declared-email-templates.ts readDeclared reads the Default-Organization layer (class a, #15206 S3). Pin: objectql/src/engine-security-catalog-package-door.test.ts:262 (saveMetaItem over a packaged template on both topologies, row stored). The served-read dogfood email-template-overlay-survives-boot.dogfood.test.ts is organization-scoped by construction (orgContext: true) and names no key.", "search_method": "Test files naming the type literal AND an artifact marker (_packageId, getArtifactItem, packaged) AND a write (saveMetaItem, PUT), each read; plus dogfood 'PUT /meta/TYPE' scans. 'None found' is that search's reading, not a proof of absence." }, "conversion": { "entry_points": "None carries a registry entry. (1) The type registry: MetadataTypeRegistryEntryBaseSchema metadata-plugin.zod.ts:207-267; its only writer is the TypeScript literal DEFAULT_METADATA_TYPE_REGISTRY :714 typed MetadataTypeRegistryEntryParsed[] (never parsed), installed by setTypeRegistry at packages/metadata/src/plugin.ts:411; MetadataTypeRegistryEntrySchema has 0 non-test parse sites. (2) Plugin manifest: contributes.kinds[] is a closed strictObject of id, globs (tombstone) and description (kernel/manifest.zod.ts:696-715), so the key would be an unrecognized key. (3) MetadataPluginConfig.additionalTypes is a retiredKey tombstone (:561) since 17. (4) defineStack / stack.zod.ts: no type-registry collection. (5) Stored rows: no sys_metadata type stores registry entries. The key only leaves the platform, on GET /meta/types.", "precedent": "conversions/registry.ts has no entry that renames a key on a registry or config entry; every rename walks a stack (objectCompactLayoutRename :225, stackRolesToPositions :264; manifestPermissionsStringListRemoved :8961 reaches stack.manifest and packages[].manifest). The non-stack rename precedent is in migrations: retired-keys/18.kernel__HotReloadConfig__debounceDelay.ts quotes 'Tombstoned with `retiredKey()`: `HotReloadConfigSchema` is not `.strict()`, so a bare deletion would silently strip the key ... No D2 conversion: not a stack collection member, not a stored row' (schema shape: debounceDelayMs plugin-lifecycle-advanced.zod.ts:380 beside 'debounceDelay: retiredKey(...)' :384; D3 entry kernel-health-check-and-hot-reload-durations-unit-in-key). The same registry's retired-keys/18.kernel__MetadataPluginConfig__additionalTypes.ts quotes 'Registered here but NOT in `src/conversions/registry.ts` ... a MetadataConversion here would be a transform with no seam that ever runs.' The converse, retired-keys/18.api__ApiEndpoint__cacheTtl.ts, gets a D2 entry only because 'apis: is a stack collection'.", "d2_reading": "A D2 conversion for this key would be a declared transform the loader never applies. The acceptance pin 'a manifest with the old key loads and converts to the new one' cannot be written: no manifest can carry the key. See open question 1.", "baselines_that_change": "packages/spec/authorable-surface/kernel.json:347 (gen:schema; the old path becomes ' [RETIRED]' under a tombstone), authorable-defaults/kernel.json:70 (the '= false' fingerprint), src/migrations/registry.ts (generated barrel, new retired-key + D3 entries), spec-changes.json and docs/protocol-upgrade-guide.md (gen:spec-changes, gen:upgrade-guide), content/docs/references/kernel/metadata-plugin.mdx:271, references/api/protocol.mdx:1422/:1428 (wire field), references/system/email-template.mdx:15 (TSDoc of system/email-template.zod.ts:16).", "baselines_that_do_not_change": "Measured by grep, the property name is absent from: api-surface/ (export names only: MetadataTypeRegistryEntry*, GetMetaTypesResponse*), export-origins/, json-schema.manifest/kernel.json (schema ids), declaration-map/, api-surface-signatures.json (define* factory hashes; none takes a registry entry), dropped-refinements.baseline.json (sites carry no key), liveness/ (no ledger row for kernel/MetadataTypeRegistryEntry; 3 prose notes only). authorable-surface/api.json tracks GetMetaTypesResponse:entries and :types only, so the wire rename is invisible to every spec gate except the regenerated reference page.", "not_written_by_the_rename_pr": "authorable-surface.base.json:4262: only gen:authorable-surface-base writes it; the deletion gate needs the RETIRED_KEYS_BY_MAJOR registration instead (packages/spec/scripts/build-schemas.ts :805-813, :954-961).", "gates_reached": "check:authorable-surface (live to absent refused without a retired-key registration), check:overlay-whitelist-table (scripts/check-overlay-whitelist-table.mjs COL_FLAG :255 reads the registry column and the table header content/docs/concepts/metadata-lifecycle.mdx:109; legs 1-3 plus its self-test fixtures), check:platform-checklist (anchors 'metadata-plugin.zod.ts#allowOrgOverride' at docs/qa/platform-checklist/areas/platform-core.json:492 and studio-authoring.json:1257; a tombstone keeps the word at line start, so those stale anchors would still resolve), check:docs, check:spec-changes, check:upgrade-guide, check:adr-0087-registration." }, "name_candidates": [ { "name": "allowEnvironmentOverlay", "reads_at_call_sites": "Row: { type: 'report', supportsOverlay: true, allowEnvironmentOverlay: true, allowRuntimeCreate: true }. Derivation: if (!entry.allowEnvironmentOverlay) continue. Sentence: 'the type does not allow environment overlays (allowEnvironmentOverlay=false)'. objectui: isArtifactItem ? !!entry.allowEnvironmentOverlay : ...", "collisions": "Exact (git grep -w, whole tree): 0. Near: supportsOverlay on the same entry (the read-path capability; the intended pair); overlayScope 'env' (api/protocol.zod.ts:537, the same scope vocabulary); capabilities.overlay on MetadataPluginConfig (metadata-plugin.zod.ts:672, a plugin capability); the retired persistence.overlayWritable (kernel/metadata-loader.zod.ts:193). Confusable: the wire entry's overrideSource 'env' and OS_METADATA_WRITABLE use 'env' for an environment VARIABLE.", "four_axes": "Business need (measured): writers are DEFAULT_METADATA_TYPE_REGISTRY, test fixtures and the getMetaTypes synthesis only; readers are two write-gate derivations, the wire field and 11 objectui source files. That holds for every candidate, so this axis does not separate them. Long-term: names the scope in the canonical ADR-0006 spelling and the mechanism noun ADR-0005 and supportsOverlay use; stays unambiguous if a per-organization axis returns (ADR-0131 D6 retires it 'for now'). AI error: the capability/permission pair (supportsOverlay vs allowEnvironmentOverlay) states the #6960 distinction in the names; the env-variable reading is the residual risk. Startup: one rename, no new capability." }, { "name": "allowEnvironmentOverride", "reads_at_call_sites": "Row: { type: 'view', supportsOverlay: true, allowEnvironmentOverride: true }. Sentence: '(allowEnvironmentOverride=false)'. Wire entry: { allowEnvironmentOverride: true, overrideSource: 'env' }.", "collisions": "Exact: 0. Near: overrideSource 'registry'|'env' on the same /meta/types entry (api/protocol.zod.ts:216), where 'env' means OS_METADATA_WRITABLE; the code NOT_OVERRIDABLE and the write intent override-artifact (metadata-core/src/types.ts:158) share the stem; configOverrides (system/app-install.zod.ts:113) is unrelated.", "four_axes": "Business need: as above. Long-term: the smallest edit from the old name, but it keeps the override/overlay split (ADR-0005 and supportsOverlay say overlay). AI error: the most guessable old-to-new mapping; the strongest misreading of the three, since 'environment override' beside overrideSource 'env' reads as 'overridden by an environment variable'. Startup: one rename." }, { "name": "allowPackagedOverlay", "reads_at_call_sites": "Row: { type: 'translation', supportsOverlay: true, allowPackagedOverlay: true }. Sentence: 'the type does not allow overlays of packaged items (allowPackagedOverlay=false)'. objectui: isArtifactItem ? !!entry.allowPackagedOverlay : ...", "collisions": "Exact: 0 ('packagedOverlay' appears once, in prose). Near: ADR-0126's 'packaged base' vocabulary (29 hits in packages/spec/src) reads as intended; but 'packaged overlay' also reads as an overlay that ships in a package (ADR-0070 package-first authoring; overlay rows carry the package_id of the package they customize).", "four_axes": "Business need: as above. Long-term: names the object (the artifact-backed item that the override-artifact intent gates) and drops the scope; if a per-organization axis returns, one scope-less key would again govern two scopes, the shape ruling C closed. AI error: free of the env-variable clash; open to the 'overlay from a package' misreading. Startup: one rename." } ], "name_candidates_excluded": "overlayWritable (a retired key's name on MetadataManagerConfig.persistence, metadata-loader.zod.ts:193; a live key sharing a tombstone's name confuses both prescriptions); allowOverlay / overlayable (scope- and object-less, beside capabilities.overlay). The choice is the contract review's; no pick is made here.", "stage_plan": [ { "stage": "S0", "what": "This measurement. No file, no PR.", "clause_2": "n/a" }, { "stage": "S1", "what": "Environment-overlay pins for the five types, tests only, under the current key name.", "files": "One new test file in packages/objectql/src on the real SchemaRegistry + protocol, both topologies (engine-security-catalog-package-door.test.ts is the harness model); optionally a dispatcher case in packages/runtime/src. Per type: PUT env-wide (no organization) over a packaged item is accepted, the row is stored with organization_id NULL, GET serves the overlay body, /meta/types advertises the flag true; identity: the true set is exactly the five.", "clause_2": "no (tests only; nothing in any package's files[] moves).", "serial": "Independent of every open PR and of #15206; must not pass organizationId so S3/S4 leave it standing. Reverse verification: commit, then flip report's flag to false, its case goes red; restore.", "why_now": "Report and translation have no environment-overlay pin, and #15206 S3/S4 rewrite the write doors all five pass through (organizationIdForMetaWrite, orgScopedWriteRefusal). Landing before S3 makes 'no overlay is lost' checkable during #15206, not only after it.", "estimate": "S" }, { "stage": "S2", "what": "The rename: new key, retiredKey() tombstone on the old one, retired-keys + D3 entries (no D2 entry, per open question 1), the wire field renamed with objectui in the same landing (open question 2).", "files": "spec: kernel/metadata-plugin.zod.ts (key :267 + TSDoc, 28 rows, comments, the customizationPolicies prescription :513-520), api/protocol.zod.ts :210/:216/:1667, system/email-template.zod.ts:16, kernel/index.ts:31, kernel/metadata-create-seeds.ts:141, kernel/metadata-type-schemas.ts:142, new migrations/entries/retired-keys/18.kernel__MetadataTypeRegistryEntry__allowOrgOverride.ts and a semantic 18.* entry, the two unreleased 18.* prose entries, regenerated registry.ts / authorable-surface/kernel.json / authorable-defaults/kernel.json / spec-changes.json / protocol-upgrade-guide.md / 3 reference pages, 6 spec tests. Runtime: protocol.ts (the 41 class-(b) lines; code :15845, :7883/:7884/:7953; sentences :16099/:16118/:17247/:21650), sys-metadata-repository.ts (13), package-writability.ts, metadata-core types.ts/contract-suite.ts, runtime domains/meta.ts:967, objectql engine.ts, plugin-security (4 files, comments), platform-objects sys-job.object.ts, lint validate-ai-agent-authoring.ts, examples/app-showcase coverage.ts. Tooling: check-overlay-whitelist-table.mjs (+ self-test), 2 adr-anchors. Docs: 11 content/docs pages, 8 docs/qa/platform-checklist files (re-point both '#allowOrgOverride' anchors explicitly), 3 liveness notes. Tests: the 46 class-(b) files + residue of the 2 mixed; the 10 message-text pins move with their sentences. Cross-repo: an objectui PR (11 source + about 43 test files at the pin) and, in this PR, the .objectui-sha bump with the regenerated SDUI manifest.", "clause_2": "yes (narrowing): widens (a new authorable key and wire field), narrows (the authorable key and the GetMetaTypesResponse field are retired). BREAKING; changeset carries FROM allowOrgOverride TO NEW_KEY on MetadataTypeRegistryEntry and GET /meta/types entries, and 'adr-0087: registered' naming the two new ids. Contract-tier review owed (dispatch-gates --tier: no path mandate; clause-② suspect on both spec paths).", "serial_vs_15206": "After #15206 S5 (hence after S2-S4). S1 (sys_view_definition): only the migrations barrel, regenerate. S2 (seal): rewrites refusePackagedBaseOverride/Removal :17168-17290 and the repository hatch :370, the same sentence (:17247) and repository messages (:1854-1983) this stage renames. S3: deletes ORG_OVERRIDABLE_TYPES (a flag reader), the manage_org_presentation arm and capability, door threading, 14 test files. S4: deletes the :16210 exemption, the :16215 sentence and the identity pin, 9 test files. S5: removes the :27554 flag read and 9 read-gate test files.", "serial_vs_open_prs": "#22215 (PROTOCOL_VERSION 18; spec-changes.json, protocol-upgrade-guide.md): land after it and regenerate. #22323 (protocol.ts :22697-22778, sys-metadata-repository.ts :81-1379, api/protocol.zod.ts :1781-1800, references/api/protocol.mdx): textually disjoint from this stage's hunks, regenerate the reference page. #22322 (references/api/protocol.mdx) and #22315 (migrations/registry.ts + an 18.* semantic entry): regenerate. #22352, #22351, #22354, #22353, #22347, #22341, #22339, #22327, #22268: no file in common. #21988 (Version Packages): CHANGELOGs only, never edited here (its file list read to page 1 of 2).", "pins": "The five S1 cases flip to the new key and gain the served-read leg; a tombstone pin (parse of the old key is refused with a prescription naming the new key, code + path); /meta/types carries the new key true for the five and no old key; check:overlay-whitelist-table self-test on the new column. Reverse verification: flip one of the five to false, its case red.", "estimate": "L (about 110 files with the objectui companion; changed lines well under 5,000)" }, { "stage": "S3", "what": "Governed text, Tier H, its own PR after S2 merges; fold #15206 S7 (ADR-0005 note) into it if both are cut.", "files": "AGENTS.md:190-192, skills/objectstack-ai/SKILL.md:347, docs/adr/0005 (top note in the shape of its :3 v5.0 note, plus :47/:68), optionally docs/adr/0010 status note.", "clause_2": "no. dispatch-gates --tier on this surface: MANDATORY claude-fable-5-1 (skills/**, no one-line exemption). skills line budget: one-line in-place edits, report whole-file and whole-catalog line counts.", "estimate": "S" } ], "order_recommendation": "S1 now; S2 after #15206 S5 merges; S3 after S2. Measured reasons: (1) wasted work: 166 of the 408 code and test hits (46 source hits in 14 files, 120 test hits in 32 files) are in code #15206 S3-S5 delete or rewrite. (2) Meaning: until S4, orgScopedWriteRefusal exempts organization-scoped writes of exactly the flag's types (protocol.ts:16210); until S3, organizationIdForMetaWrite/Read thread the organization only for them (meta-write-org-scope.ts:99, :141, :189); until S5, reportUnhydratableOrgScopedRows treats their organization rows as legitimate (:27554). A key renamed to an environment name before then still governs organization scope, ruling C's rejected B shape inverted. (3) Conflict: S3-S5 are L-sized PRs over protocol.ts, rest-server.ts, runtime meta.ts and the same test files. The card's 'either order or in parallel' holds for the DELETION (it never needs the new name), not for the rename.", "falsified_premises": [ "F1 'An ADR-0087 D2 load-time conversion, so a manifest naming the old key still loads unchanged' / acceptance 'a manifest with the old key loads and converts': no manifest, stack, plugin config or stored row can carry a type-registry entry (conversion.entry_points), so a D2 entry has no seam. The recorded non-stack precedent is tombstone + retired-key + D3 (conversion.precedent). The ruling's intent, that no author hand-edits anything, holds vacuously: no author can write the key today.", "F2 'Clause-②: yes' (card) / 'yes (widening: a new published key ...)' (claim): the rename also retires an authorable key and a published wire field, so by the arm table in scripts/pm/clause2-line.mjs, where yes (narrowing) is 'a diff that widens one surface and narrows another', it is yes (narrowing), BREAKING, with a FROM/TO migration and a registered ADR-0087 disposition.", "F3 Scope 'generated baselines and docs follow' is incomplete: the rename also reaches GET /meta/types entries[].allowOrgOverride (spec api/protocol.zod.ts:210, @objectstack/client meta.getTypes) and objectui at the pin (11 source files, Studio's write gate for packaged items of the five types), runtime sentences naming the key (8 sites, pinned at 9 test sites in 8 files), two gates (check:overlay-whitelist-table reads the column name in code and in the docs table; check:platform-checklist resolves two symbol anchors), and the 17.x customizationPolicies tombstone prescription.", "F4 'The two cards land in either order or in parallel': true for #15206's deletion; for this card's rename, landing before #15206 S5 renames 166 hits of code S3-S5 delete and ships an environment-named key that still decides organization scope (order_recommendation).", "F5 'No overlay is lost' pins: today only view, dashboard and email_template have a pin of an environment overlay of a packaged item; report and translation have none, and their organization-scoped pins are #15206 S4's to flip (five_types).", "Confirmed, not falsified: the five types (28 entries, exactly five true); the key at metadata-plugin.zod.ts:267 with describe 'Allow per-org overlay writes via runtime metadata API'; the claim's 176 files and 60 protocol.ts hits; #15206 uses the key under its current name and its deletion does not depend on the name; isOverlayAllowed has no organization branch of its own (it is :15976; the per-organization path is orgScopedWriteRefusal :16203-16231 and the door helpers)." ], "open_questions": [ { "question": "Q1. The ruled 'ADR-0087 D2 load-time conversion' has no seam (F1). What carries the old key's prescription instead?", "options": [ "A. retiredKey() tombstone on MetadataTypeRegistryEntryBaseSchema + retired-keys entry 18.kernel__MetadataTypeRegistryEntry__allowOrgOverride + a D3 semantic entry; no D2 entry (the HotReloadConfig.debounceDelay and MetadataPluginConfig.additionalTypes precedents). Cost: two ledger files and a tombstone pin.", "B. Register a D2 conversion anyway to meet the ruling's letter. Cost: a conversion entry no loader ever applies; the precedent text calls it 'a transform with no seam that ever runs'.", "C. Plain rename with no tombstone and no ledger entry. Refused mechanically: check:authorable-surface rejects live-to-absent without a retired-key registration, and AGENTS.md requires a tombstone for a removed authorable key." ], "recommendation": "A. Business need: measured, zero manifests or stored rows can name the key, so no author needs a load-time rewrite; the readers that matter are TypeScript code and wire consumers, which the compiler and the tombstone reach. Long-term: two recorded precedents of the same shape. AI error: the tombstone refuses the old key loudly at parse and compile with the new name in the prescription; B declares a capability the runtime never exercises (declared is not enforced). Startup: no window, no dead ledger row. This replaces the ruling's mechanism, not its direction, so it needs triage or the contract review to record it." }, { "question": "Q2. The /meta/types wire field (GetMetaTypesResponseSchema entries[].allowOrgOverride) is the same key's projection and objectui's Studio write gate. How does it move?", "options": [ "A. Rename it in S2, in one landing with an objectui PR that reads the new key and the .objectui-sha bump (+ SDUI manifest regeneration). Cost: a cross-repo landing; the objectui PR merges just before S2 so objectui main does not run long against the old server.", "B. Keep the wire name allowOrgOverride for now and rename only the authoring key. Cost: the wire keeps a key that says 'organization' and governs environments, read by Studio and by AI clients of /meta/types.", "C. Emit both names for one release. Cost: a dual-spelling window and a consumer-side fallback in objectui." ], "recommendation": "A. Business need: objectui is the measured reader (11 source files at the pin; every packaged item of the five types). Long-term: one name end to end. AI error: B is ruling C's rejected B shape on the wire; and objectui's own optional field type means neither the Console Pin Gate nor api-surface would see a missed objectui side, so the same landing is the only safe form. Startup: C is a dual-spelling window, which this stage of the company does not take without named external users; none measured (cloud NOT MEASURED)." } ], "out_of_scope_findings": [ "class: b (stated contract) · reach: NOT MEASURED, no public-door run · sys-metadata-repository.ts:362-363 states both write gates 'must consult the same elevated set', but the repository reads readEnvWithDeprecation('OS_METADATA_WRITABLE', []) (:370) while protocol.ts:15867 also honours the legacy OBJECTSTACK_METADATA_WRITABLE; by reading, with only the legacy variable set /meta/types advertises the type writable and the protocol gates pass, while the repository's assertAllowed would refuse the write. objectui PageShell.tsx:110 and useMetadata.ts:87 name the legacy variable to users · dedupe words: OS_METADATA_WRITABLE, OBJECTSTACK_METADATA_WRITABLE, envWritableMetadataTypes, readEnvWithDeprecation · carrier: #15206 S2 (the seal stage edits both hatch readers); noted, not filed", "The key's TSDoc (metadata-plugin.zod.ts:262-265) says only view and dashboard opt in and names 'not_overridable' in lower case; five opt in and the code is NOT_OVERRIDABLE · carrier: S2 of this card (it rewrites that TSDoc); noted, not filed", "The 17.x customizationPolicies tombstone prescription (metadata-plugin.zod.ts:513-520), the unreleased 18.metadata-customization-protocol-retired entry and kernel/index.ts:31 direct authors to 'ADR-0005's org-scoped overlay (opt-in via allowOrgOverride)', false twice once #15206 and this card land; pinned by metadata-customization-retirement.test.ts:58 · carrier: S2 of this card; noted, not filed", "qa/dogfood email-template-overlay-survives-boot.dogfood.test.ts pins the email-template overlay as organization-scoped by construction (orgContext: true); after #15206 S3 the save lands env-wide · carrier: #15206 S3; noted, not filed", "A tombstone keeps 'allowOrgOverride' at line start in metadata-plugin.zod.ts, so the two checklist anchors naming metadata-plugin.zod.ts#allowOrgOverride would keep resolving to the tombstone with check:platform-checklist green; docs/adr/0029:386 holds a third, ungated · carrier: S2 (re-point the checklist anchors) and S3 (ADR) of this card; noted, not filed" ], "tests": "No build and no test ran; stage 0 writes no file. Read-only probes in ../objectstack-issue-22340 at 54c3ce10c, git status --porcelain empty throughout: (1) git grep census with glob pathspecs and controls (census.command_and_controls); every hit line read and classed. (2) Registry entries parsed from source with a node one-liner (28 entries, five true). (3) objectui at the pin: git fetch of a58626c88 into the shared objectui object store, then git grep at that commit (no checkout change). (4) node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack --tier on the S2 and S3 surfaces (no path mandate for S2, clause-② suspect on spec paths; MANDATORY fable for S3). (5) npm view @objectstack/spec dist-tags: latest 17.7.0, so 18.* entries are unreleased. (6) REST reads: card #22340 + 1 comment, #15206 + 11 comments incl. report 6067752061, #22007 + ruling 6028809298, open-PR list (14) and each PR's file list, PRs 22338/22331/22319 state, the 22323 patch hunks.", "mcp_calls": "0 — no MCP tool called", "api_writes": "1 — POST /repos/objectstack-ai/objectstack/issues/22340/comments (this os-dev-report, through scripts/pm/post-stamped.mjs and the fleet-write relay)", "gates": "not applicable — stage 0 writes no file. Derived: none (dispatch-gates without paths: 'this branch changes nothing against origin/main ... nothing to derive'). Run: none. Exit codes: none. --ran verdict line: not run.", "line_budget": "0 changed lines against merge base 54c3ce10c (git diff --stat empty); governed paths touched: 0.", "deviations": [ "Did not push the empty branch (os-dev rule 1). The dispatch forbids any push in stage 0 and no edit happened, so neither the landing marker nor the write-route probe had anything to mark; os-dev says the role file prevails on conflict, so this is flagged for the seat rather than chosen silently.", "No pnpm install in the worktree: no edit, build or test.", "Classification reads each hit line by what it governs; three source files are split by line and two test files are mixed (census.method).", "objectui was measured at the .objectui-sha pin a58626c88, not at objectui main; cloud NOT MEASURED (repository-scoped proxy).", "origin/main moved to b7e01fbbd during the run (shared refs); the census stays anchored to 54c3ce10c and the one new commit names the key on 0 lines.", "The worktree ../objectstack-issue-22340 is removed after this report (clean, never pushed)." ], "files_changed": [] }
Generated by Claude Code
objectstack-fleet commented
on Oct 8, 2026 ContributorAuthorMore actionsSeat review of #22340 stage 0: census accepted. Order set by the seat; the conversion mechanism goes back to the decision box (its ruled premise is falsified). Stage S1 is claimed
domain:specseat 2 (#18549) ·os-sales· sessionsession_01DhTqaEHqPVSVnAkjG3jywn· 2026-10-08T21:51Z · holder of claim6069288725. This amends that claim. ⛔ Not a new claim.The stage-0 report is on this card (
os-dev-report, measured at54c3ce10c;b7e01fbbdnames the key on 0 lines). The seat checked the method: the census uses glob pathspecs with non-vacuous controls, and every hit line was read and classed. The seat accepts it.Accepted readings:
- 765 hits in 176 files. 46 source hits (14 files) and 120 test hits (32 files) sit in the per-organization path feat(metadata-core,metadata-protocol,objectql,plugin-security): the
sys_metadatafamily goes tenant-less; the per-organization overlay axis retires; managed content is sealed (ADR-0131 D6/D7/D13) #15206 S3–S5 delete. - The five types are confirmed (28 registry entries; the flag is true on exactly view, dashboard, report, translation, email_template).
- Report and translation have no environment-overlay pin today.
- The rename reaches the published wire field
GET /meta/typesentries[].allowOrgOverride(api/protocol.zod.ts:210). objectui reads it at the pin as Studio's write gate for every packaged item of the five types, through its own optional field type. So no spec gate and no Console Pin Gate would see a missed objectui side. - The rename is
Clause-②: yes (narrowing), not(widening): it retires an authorable key and a wire field.
Set by the seat (ordering and dependency, not a product question):
- S1 now, tests only. Environment-overlay pins for all five types under the current key name, each case a packaged item overlaid env-wide (no
organizationId), so feat(metadata-core,metadata-protocol,objectql,plugin-security): thesys_metadatafamily goes tenant-less; the per-organization overlay axis retires; managed content is sealed (ADR-0131 D6/D7/D13) #15206 S3/S4 leave it standing. They make "no overlay is lost" checkable while feat(metadata-core,metadata-protocol,objectql,plugin-security): thesys_metadatafamily goes tenant-less; the per-organization overlay axis retires; managed content is sealed (ADR-0131 D6/D7/D13) #15206 rewrites the write doors. - S2, the rename, after feat(metadata-core,metadata-protocol,objectql,plugin-security): the
sys_metadatafamily goes tenant-less; the per-organization overlay axis retires; managed content is sealed (ADR-0131 D6/D7/D13) #15206 S5 merges. Until S3/S4/S5, the flag still decides organization-scoped writes, threading and reads (protocol.ts:16210,meta-write-org-scope.ts:99/:141/:189,:27554). Renaming it to an environment name first would ship ruling C's rejected B shape inverted, and it would rename 166 hits of code that is about to be deleted. - S3, governed text (Tier H), after S2:
AGENTS.md:190-192,skills/objectstack-ai/SKILL.md:347, ADR-0005:47/:68. - The card's "either order or in parallel" holds for feat(metadata-core,metadata-protocol,objectql,plugin-security): the
sys_metadatafamily goes tenant-less; the per-organization overlay axis retires; managed content is sealed (ADR-0131 D6/D7/D13) #15206's deletion, not for this rename. The engine seat is told on feat(metadata-core,metadata-protocol,objectql,plugin-security): thesys_metadatafamily goes tenant-less; the per-organization overlay axis retires; managed content is sealed (ADR-0131 D6/D7/D13) #15206.
Back to the decision box: the ruling's mechanism. Ruling C (
6028809298, #22007) prescribes "an ADR-0087 D2 load-time conversion so an existing manifest naming the old key still loads unchanged". Measured (F1), no manifest, stack, plugin config or stored row can carry a type-registry entry:contributes.kindsis a closed shape;additionalTypesis a tombstone;- the registry is a TypeScript literal that nothing parses.
So a D2 conversion has no seam. A premise of the ruling is falsified, which is the case that returns to the box; the seat does not swap the mechanism itself. The sub-question the ruling did not cover, the wire field (Q2), is put in the same act. The four-axis analysis (Chinese) is the next comment. The state moves to
needs-user-decisionwhen S1 lands.S1 claim amendment:
- Branch
claude/issue-22340-s1-env-overlay-pins, worktreeobjectstack-issue-22340-s1. - File surface: one new test file in
packages/objectql/srcon the real SchemaRegistry and protocol, both topologies (harness model:engine-security-catalog-package-door.test.ts), plus optionally one dispatcher case inpackages/runtime/src. No source file. Clause-②: no(tests only). No changeset if the repo's rules exempt a test-only diff. Cross-lane on [PM seat] domain:engine — ⏳ vacant #6367 (packages/objectql,packages/runtimetests).
Noted, not filed (each with its carrier):
OS_METADATA_WRITABLEvs the legacy variable: the two write gates read different sets → feat(metadata-core,metadata-protocol,objectql,plugin-security): thesys_metadatafamily goes tenant-less; the per-organization overlay axis retires; managed content is sealed (ADR-0131 D6/D7/D13) #15206 S2.- The key's TSDoc lists two types, and five opt in → this card's S2.
- The 17.x
customizationPoliciesprescription and the two unreleased 18.* prose entries → S2. - The organization-scoped email-template dogfood → feat(metadata-core,metadata-protocol,objectql,plugin-security): the
sys_metadatafamily goes tenant-less; the per-organization overlay axis retires; managed content is sealed (ADR-0131 D6/D7/D13) #15206 S3. - The checklist
#allowOrgOverrideanchors → S2 / S3.
Generated by Claude Code
- 765 hits in 176 files. 46 source hits (14 files) and 120 test hits (32 files) sit in the per-organization path feat(metadata-core,metadata-protocol,objectql,plugin-security): the
objectstack-fleet commented
on Oct 8, 2026 ContributorAuthorMore actions待维护者裁决:#22340 —— 改名
allowOrgOverride时,「旧键照常加载」靠什么兑现;/meta/types返回里的同名字段是否一起改domain:specseat 2 (#18549) ·os-sales· sessionsession_01DhTqaEHqPVSVnAkjG3jywn· 2026-10-08T21:53Z · 认领6069288725的持有席。依据:阶段 0 报告(本卡os-dev-report,54c3ce10c实测)与本席复核6069745527。一句话问题: 你在 #22007 裁的 C 是给这个键改名,并用「加载期转换」让旧写法照常加载。实测发现,没有任何作者能在 manifest 或配置里写这个键,所以那道转换没有地方可以生效。另外,这个键还以同名字段出现在 Studio 读取的接口返回里,原裁决没有提到它。
背景(实测):
- 这个键只写在平台自己的类型注册表里。注册表是一段 TypeScript 字面量,没有生产代码去解析它。
- 插件 manifest 的
contributes.kinds是封闭结构,写这个键会被拒;MetadataPluginConfig.additionalTypes已经是墓碑;stack 里没有类型注册表集合;存储行里也不存注册表条目。 - 仓里有同类改名的先例:
HotReloadConfig.debounceDelay、MetadataPluginConfig.additionalTypes。做法是旧键留墓碑(retiredKey(),解析时响亮拒绝,并提示新名字),加一条 retired-keys 记录和一条 D3 语义记录,不加 D2 转换。先例原文:"a MetadataConversion here would be a transform with no seam that ever runs"。 GET /meta/types的entries[].allowOrgOverride(api/protocol.zod.ts:210)是同一个键投影到接口上的字段。objectui 在当前 pin 上有 11 个源文件读它,用来决定 Studio 能不能编辑这五类已打包条目。objectui 用的是自己定义的可选字段类型,所以服务端改了名、objectui 没跟上时,没有任何门禁会报错。结果是这五类条目在 Studio 里静默变成只读。
Governing text:
- decision: ADR-0131 C5 —
allowOrgOverridealso decides environment overlays of packaged items. When the per-organization axis retires, does the key split, keep its name with a new meaning, or get renamed? #22007 裁决 C(6028809298,2026-10-07 维护者「其他同意」):"renamed to a key that says what it will then mean … with an ADR-0087 D2 load-time conversion so an existing manifest naming the old key still loads unchanged … The new name is fixed by the contract review of the C5 change"。 - ADR-0087 D2 只适用于 stack 集合与存储行。不在 stack 上的键,按先例走墓碑加 D3,见
migrations/entries/retired-keys/18.kernel__HotReloadConfig__debounceDelay.ts。 - AGENTS.md:删除一个可被作者编写的键时,必须留墓碑。
协议声明: 不改协议,只是落实已裁的改名方向。被证伪的是裁决中「旧 manifest 要靠转换」这一条前提。
前提(附复验方式):
- P1:注册表条目没有可被作者写入的入口 ——
git grep -n "MetadataTypeRegistryEntrySchema" -- ':(glob)packages/**/src/**' ':!*.test.ts',非测试的解析点为 0。 - P2:objectui 在 pin 上读这个字段 —— 在 objectui 的
.objectui-sha提交上git grep -n allowOrgOverride -- 'packages/**/src/**',命中 11 个源文件。
Q1:旧键的提示由什么承载
选项 做什么 客户/作者能感知到的后果 A 墓碑 + D3(推荐) 新键上线,旧键留墓碑,解析时响亮拒绝并给出新名字;加 retired-keys 与 D3 语义记录,不加 D2 今天没有作者能写这个键,所以没有人需要被自动改写;平台代码和接口消费方由编译器和墓碑拦住 B 照裁决原文加 D2 另外登记一个 D2 转换 账本里多一条永远不会执行的转换,等于声明了一个运行时不兑现的能力 C 直接改名,不留墓碑 — 会被机械拒绝: check:authorable-surface不允许没有退役登记的删除,AGENTS.md 也要求墓碑Q2:
/meta/types的同名字段选项 做什么 后果 A 一起改名(推荐) S2 改服务端字段,同一次落地配一个 objectui PR 改读新名字,并升级 .objectui-sha从注册表到 Studio 用同一个名字;代价是一次跨仓协同落地 B 接口保留旧名 只改作者侧的键 接口上留下一个写着「组织」、实际管「环境」的字段,Studio 和读 /meta/types的 AI 客户端都会读错。这正是裁决 C 已经否掉的 B 形态,只是换到了接口上C 新旧两个名字同时发一个版本 — 双拼写过渡期,objectui 还要写回退逻辑。没有具名外部用户的证据,按「过渡也从紧」不荐 业务含义直译:
- Q1-A ≈「门牌换了,旧门牌钉一块『已搬到 X』的牌子」。Q1-B ≈「再装一台永远不会有人按的转接电话」。
- Q2-A ≈「店名和招牌同一天一起换」。Q2-B ≈「新店名开张,门口招牌还挂旧名」。
四轴论证:
- 长远合理性: Q1-A 与仓里两条同形先例一致,账本里不留死记录。Q2-A 从头到尾只有一个名字。
- 实际业务拉动: 写这个键的只有平台自己;读它的是两处写门推导、一个接口字段,以及 objectui 的 11 个源文件。所以 Q1 没有作者需要迁移,Q2 有一个实测的真实消费方(Studio)。
- 防 AI 犯错(出错时谁看到什么):
- Q1-A:旧键在解析和编译时响亮报错,并给出新名字;Q1-B 声明了一个不会执行的转换。
- Q2-B / Q2-C:AI 读接口时会把「org」理解成组织。Q2 漏改 objectui 时是静默只读,所以必须同一次落地。
- 创业阶段不扩散: Q1-A 不新增任何长期维护面;Q2-C 是双拼写窗口,不荐。
os-decision-facets
- ① 项目长远合理性:Q1-A 沿用同形先例、不留死转换;Q2-A 一个名字贯穿注册表、接口和 Studio。
- ② 实际业务拉动:没有作者写这个键(Q1 零拉动,选最轻的 A);Studio 是 Q2 的实测读者。
- ③ 防 AI 犯错:墓碑响亮拒绝优于无效转换;接口字段名必须说真话,漏改 objectui 会是静默只读。
- ④ 创业阶段不扩散:不留死账本行,不开双拼写窗口。
Prior rulings read:allowOrgOverride/D2 conversion registry key rename/retiredKey no D2→ decision: ADR-0131 C5 —allowOrgOverridealso decides environment overlays of packaged items. When the per-organization axis retires, does the key split, keep its name with a new meaning, or get renamed? #220076028809298(裁决 C);feat(metadata-core,metadata-protocol,objectql,plugin-security): thesys_metadatafamily goes tenant-less; the per-organization overlay axis retires; managed content is sealed (ADR-0131 D6/D7/D13) #152066037959748、6066851770;先例18.kernel__HotReloadConfig__debounceDelay、18.kernel__MetadataPluginConfig__additionalTypes;thread: 本卡6069288725、6069745527。
推荐:Q1 选 A,Q2 选 A。 只看①选 A / A;②③④ 是否翻转:否(④ 只影响落地形式:Q2-A 要求 objectui PR 与 S2 同一次落地)。回退项:Q1 无(B、C 都不成立);Q2 回退到 C 需要具名的外部用户证据。
置信缺口: cloud 仓有没有读
/meta/types的allowOrgOverride,未实测(本会话的 GitHub 代理按仓限定)。objectui 只测了 pin 上的提交,没测它的 main。裁后执行:
- S1(五类环境覆盖的测试 pin)不依赖这两问,本席已认领并照常推进。
- S2(改名)排在 feat(metadata-core,metadata-protocol,objectql,plugin-security): the
sys_metadatafamily goes tenant-less; the per-organization overlay axis retires; managed content is sealed (ADR-0131 D6/D7/D13) #15206 S5 之后,按你的字母落实。选 Q2-A 时,本席同时请 objectui 车道开配套 PR,并在 S2 里升级.objectui-sha。 - 新名字仍由 S2 的合约复核定,阶段 0 给出的候选是
allowEnvironmentOverlay、allowEnvironmentOverride、allowPackagedOverlay。
状态: S1 落地后,本卡转入
needs-user-decision。请在本卡回两个字母,例如「Q1 A,Q2 A」。
Generated by Claude Code
objectstack-fleet commented
on Oct 8, 2026 ContributorAuthorMore actionsos-dev-report
{ "issue": 22340, "status": "done", "branch": "claude/issue-22340-s1-env-overlay-pins", "pr": "https://github.com/objectstack-ai/objectstack/pull/22363", "session": "session_01DhTqaEHqPVSVnAkjG3jywn (subagent of the PM session; identity = the S1 branch named by the seat amendment 6069745527, verified against the newest Claim: 6069288725, which names stage 0's branch; no second claim posted)", "premise_still_valid": true, "summary": "S1 landed as one new test file, packages/objectql/src/environment-overlay-five-types.test.ts (tests only, no source file). It runs on the real ObjectQL engine, SchemaRegistry and ObjectStackProtocolImplementation on both topologies, and no organizationId reaches any call. For each of view, dashboard, report, translation and email_template it pins three things. (1) saveMetaItem over the packaged item is accepted, stores exactly one row with organization_id NULL, and the read serves the written body. (2) A stored env-wide row put straight into the store is what getMetaItem serves, with a control that the read serves the packaged item before the row exists. (3) getMetaTypes advertises allowOrgOverride true with overrideSource registry, with OS_METADATA_WRITABLE cleared. One more case pins the identity: the flag is true on exactly these five. On origin/main 1cb0edb82, all five env-wide overlays are accepted on both topologies, so no stop condition fired. Two of the dispatch's premises were measured and corrected; neither changes the stage. (a) The served read getMetaItem keys on neither the flag nor supportsOverlay. Its only skip is declinesStoredRow (shipped flows and code datasources), and supportsOverlay's sole protocol reader, mergesOverlayAtRead, is called only by the delete door. So the pin claims only that the read consults no flag, and the ablation shows it. (b) No in-repo producer puts a packaged translation into the SchemaRegistry, so the translation case registers its packaged item through registerItem(type, item, 'name', packageId), the entry isArtifactBacked reads. Existing pins are listed per type in the file header and the PR body. No runtime dispatcher case was added: for an env-wide write the dispatcher reads the flag nowhere.", "tests": "All runs at head db89d66f0. New file: pnpm --filter @objectstack/objectql exec vitest run --project local src/environment-overlay-five-types.test.ts gives 26 passed (26): 1 identity, 2 topologies x 5 types x write/read = 20, 5 wire. Package: pnpm --filter @objectstack/objectql exec vitest run --project local --maxWorkers=2 gives Test Files 388 passed (388), Tests 7633 passed (7633). Typecheck: pnpm --filter @objectstack/objectql run typecheck exits 0, with 'check:test-typecheck: OK ... 40 file(s) / 234 error(s) / 65 pinned signature(s) held'; the new file is unledgered, so it compiles with zero errors. ABLATION, report flag true to false in DEFAULT_METADATA_TYPE_REGISTRY via scripts/ablation-replace.mjs: anchor x1 to x0, replacement x0 to x1, blob 47f68e76cb5c to 01bfde907a81. @objectstack/spec was rebuilt (exit 0), and ablation-dist-preflight found the marker in 4 built files: dist/kernel/index.js, dist/kernel/index.mjs, dist/browser/kernel/index.js, dist/browser/kernel/index.mjs. Predicted before running: 4 red / 22 green, with report's read cases staying green. Measured 'Tests 4 failed | 22 passed (26)', exactly the predicted four. Identity received [dashboard, email_template, translation, view]. Report write on both topologies received refused { code NOT_OVERRIDABLE, status 403 }. Report wire received allowOrgOverride false, overrideSource registry. RESTORE: by the tool, blob == HEAD 47f68e76cb5c, git diff HEAD empty. Spec was rebuilt (exit 0), ablation-dist-preflight --absent reported the marker absent from all 232 built files and the whole tree clean, and the re-run gave 26 passed (26). Lint, a declared narrowing: eslint --no-inline-config --format json on the one file reports 1 file, 0 errors, 0 warnings. The file is in eslint's population (--print-config returns a config with 5 rules for it). The invariance holds because type-aware linting is not enabled (no parserOptions.project, no typed rules; eslint.config.mjs states the same), so a new file cannot change another file's result. Changeset measurement: objectql was built (exit 0). The test file's fixture strings appear 0 times in files[] (dist, README.md, CHANGELOG.md), against a positive control of registerMetadataCollections in 6 dist files.", "mcp_calls": "0 (no MCP tool used)", "api_writes": "3 from this session, all POST /repos/objectstack-ai/objectstack/dispatches (the fleet-write relay). The relay executed 4 writes as objectstack-fleet[bot]: (1) pr_create, POST /repos/objectstack-ai/objectstack/pulls, giving #22363 draft, with the read-back reporting 9933 bytes sent and 9933 stored, identical; (2) labels_add, POST /repos/objectstack-ai/objectstack/issues/22363/labels [skip-changeset]; (3) assign, POST /repos/objectstack-ai/objectstack/issues/22363/assignees [os-sales]; (4) this os-dev-report comment, POST /repos/objectstack-ai/objectstack/issues/22340/comments via post-stamped.mjs. Separately, 2 git pushes of the branch (the empty branch probe and db89d66f0), which are not REST writes. The card's assignee was not written.", "gates": { "derived": "node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack, with no paths, at db89d66f0. Change set: 1 path vs merge base 1cb0edb82. 56 commands, byte-identical after sort to the dispatch file g22340s1.txt (derived at b7e01fbbd), so nothing was added.", "run": "56 run, unlocked and in the foreground (nohup plus a tail --pid wait), at head db89d66f0. 55 exit 0. pnpm check:dual-build-cjs-loads exit 3: PREREQUISITE NOT MET, 65 packages have no dist. It needs every workspace package built and is NOT MEASURED; the diff adds a test file that no build emits (measured, 0 hits in objectql dist), and CI builds the workspace.", "notable_exit_0": "check:nul-bytes 'OK (scanned 10343 text file(s) ... no raw ASCII control bytes)'; check:engine-double-contract 'OK — 990 pinned, 129 in the DEBT ledger, 3 exempt'; check:objectql-double-limit '... none new'; check:test-source-alias OK; check:driver-memory-census OK; check:type-check-debt OK; check-issue-citations '0 file(s) read' (test files are a DEFERRED surface of that gate).", "ran_verdict": "✓ dispatch-gates --ran: 56 derived famil(ies) accounted for — 55 run, 1 NOT-MEASURED (1 DERIVED from a recorded exit 3).", "ci_at_report": "in_progress (12 of 30 check runs completed at report time, none failed; Check Changeset skipped by the skip-changeset label). CI convergence is the PM's." }, "line_budget": "+359 / -0 in 1 file vs merge base 1cb0edb82 (dispatch-gates: 359 changed lines, under the 5000 human-merge threshold). Governed paths touched: 0.", "deviations": [ "Read leg: the dispatch said the served read keys on supportsOverlay, not on the flag. Measured: getMetaItem consults neither, so the pin asserts only that the read consults no flag. The ablation shows this: report's read cases stay green while its write and wire cases go red.", "Translation: no stack collection carries a packaged translation, so its packaged item is registered through SchemaRegistry.registerItem(type, item, 'name', packageId) instead of the manifest; the other four go through the manifest's own stack collections.", "The read leg seeds the env-wide row directly into the store, with the shape the repository writes: type, name, organization_id null, package_id null, metadata as JSON, state active, version 1. This asks the read alone, with no write door in front of it. The write leg also reads back through the protocol, which ties what the door stores to what the read serves.", "organization_id is asserted as (row.organization_id ?? null) toBeNull. It is NULL today, and the form stays standing if #15206 makes the family tenant-less and drops the column.", "email_template's packaged save on both topologies already exists as the control in engine-security-catalog-package-door.test.ts. Here it is the first step of a round trip that goes on to read the stored row's organization, the served body and the wire flag, so it is not a duplicate single-fact case. No other existing pin was restated.", "Commit trailers use AGENTS.md's model-free pair (Claude-Session plus Co-authored-by: Claude). The harness reminder's model-named Co-Authored-By trailer was not used because the pre-push hook refuses a model identifier; the PR footer uses AGENTS.md's session-URL form.", "check:dual-build-cjs-loads is NOT MEASURED (exit 3, prerequisite: a whole-workspace build)." ], "files_changed": [ "packages/objectql/src/environment-overlay-five-types.test.ts" ], "open_questions": [], "out_of_scope_findings": [ "carrier: #22340 S2 contract review (the domain:spec seat) · noted, not filed · No in-repo producer puts a packaged translation into the SchemaRegistry. This is a reading of four seams at 1cb0edb82, not a boot measurement: METADATA_ARRAY_KEYS in objectql/src/engine.ts and ARTIFACT_FIELD_TO_TYPE in metadata/src/plugin.ts carry no translations collection; MetadataPlugin._loadFromFileSystem stamps _packageId but registers into MetadataManager's own map; ObjectQLPlugin.loadMetadataFromService copies only object, view, app, flow and hook. So isArtifactBacked is false for every translation, and an env-wide translation write is admitted by allowRuntimeCreate, not by the flag. The flag's live effect on translation is the per-organization path (#15206) and the Studio wire gate, which bears on what 'no overlay is lost' means for translation in S2 · dedupe words: translation packaged artifact, isArtifactBacked translation, translations METADATA_ARRAY_KEYS", "carrier: #22340 S2 (the domain:spec seat) · noted, not filed · Stage 0's reading 'the read keys on supportsOverlay (mergesOverlayAtRead)' does not hold for getMetaItem. That read serves any stored active row unless declinesStoredRow holds, and mergesOverlayAtRead is called only by refusePackagedBaseRemoval, the delete door. The rename still cannot hide a stored overlay from this read: it consults no flag, as pinned and as the ablation shows · dedupe words: mergesOverlayAtRead, supportsOverlay served read, declinesStoredRow", "Governed text: none made false by this diff (AGENTS.md:190-192, skills/objectstack-ai/SKILL.md:347 and ADR-0005 are S3's, unchanged)." ] }
Generated by Claude Code
objectstack-fleet commented
on Oct 8, 2026 ContributorAuthorMore actionsSeat review of #22340 S1, PR #22363 at
db89d66f0: ACCEPT. Lands when green; the card then moves toneeds-user-decision(6069763011)domain:specseat 2 (#18549) ·os-sales· sessionsession_01DhTqaEHqPVSVnAkjG3jywn· 2026-10-08T22:44Z · holder of claim6069288725(S1 per6069745527).The report is on this card. The PR adds one test file and touches no source file:
packages/objectql/src/environment-overlay-five-types.test.ts, +359.-
What it pins. For each of view, dashboard, report, translation and email_template, on both topologies, with no
organizationIdreaching any call:- an env-wide
saveMetaItemover the packaged item is accepted and stores one row withorganization_idNULL, and the read serves the written body; - a stored env-wide row is what
getMetaItemserves (with a before-row control); getMetaTypesadvertises the flagtruefromregistry, withOS_METADATA_WRITABLEcleared.
The identity case pins that exactly these five carry the flag. 26 tests pass.
- an env-wide
-
Ablation (report's flag
true→false, spec dist rebuilt, marker proven in 4 built files):4 failed | 22 passed, exactly the predicted four.- The identity case drops
report. - Report's write is refused with
403 NOT_OVERRIDABLEon both topologies. - Report's wire field reads
false. - Report's read cases stay green, because the read consults no flag.
It was restored to the HEAD blob, with the dist clean (marker absent from all 232 files).
- The identity case drops
-
Level: test-only,
skip-changeset(0 fixture hits in objectql'sfiles[]),Clause-②: no. No contract review is owed. 55 / 56 derived gates ran;check:dual-build-cjs-loadsis NOT MEASURED (a whole-workspace build).
Two stage-0 readings corrected by measurement, recorded for S2 and the decision. Neither changes S1.
getMetaItemkeys on neither the flag norsupportsOverlay. Its one skip isdeclinesStoredRow, andmergesOverlayAtReadis called only by the delete door. So a rename cannot hide a stored overlay from the served read.- No in-repo producer puts a packaged translation into the SchemaRegistry (
METADATA_ARRAY_KEYS,ARTIFACT_FIELD_TO_TYPE,loadMetadataFromServicecarry none). SoisArtifactBackedis false for every translation, and an env-wide translation write is admitted byallowRuntimeCreate, not by the flag. The flag's live effect on translation is the per-organization path (feat(metadata-core,metadata-protocol,objectql,plugin-security): thesys_metadatafamily goes tenant-less; the per-organization overlay axis retires; managed content is sealed (ADR-0131 D6/D7/D13) #15206) and the Studio wire gate. This bears on the decision's Q2 (6069763011): the wire field still gates Studio for the other four types, and for translation only through that gate.
Next: every check green → landing (
Part of #22340) →needs-user-decision.
Generated by Claude Code
-
objectstack-fleet commented
on Oct 8, 2026 ContributorAuthorMore actionsLanded: PR #22363 →
7fbfef4c7(S1,Part of #22340). The card moves toneeds-user-decisionfor Q1 / Q2domain:specseat 2 (#18549) ·os-sales· sessionsession_01DhTqaEHqPVSVnAkjG3jywn· 2026-10-08T23:53Z · holder of claim6069288725(S1 per6069745527), released by this act.- Landed: PR test(objectql): pin the environment overlay of a packaged item for the five overlay-enabled types #22363 merged through the merge queue at 2026-10-08T23:52Z as
7fbfef4c7. It has one parent,6a53564b9, and is an ancestor oforigin/main. - Content check:
packages/objectql/src/environment-overlay-five-types.test.tson7fbfef4c7is blob-equal to the reviewed headdb89d66f0(ACCEPT6070542431). - What now holds: each of the five overlay-enabled types (view, dashboard, report, translation, email_template) has a pin that holds on both topologies, with no
organizationId. An environment-wide overlay of a packaged item is accepted and stored withorganization_idNULL, the read serves it, and/meta/typesadvertises the flag. "No overlay is lost" is now checkable while feat(metadata-core,metadata-protocol,objectql,plugin-security): thesys_metadatafamily goes tenant-less; the per-organization overlay axis retires; managed content is sealed (ADR-0131 D6/D7/D13) #15206 rewrites the write doors and when the rename lands. - Waiting on the maintainer: Q1 (no D2 seam: tombstone + retired-keys + D3 instead) and Q2 (the
/meta/typeswire field, renamed with an objectui companion in one landing). The four-axis analysis is6069763011; the seat recommends A / A. S1's measurement adds one reading to Q2 (6070542431): translation is never artifact-backed today, so the wire gate matters for the other four types. - After the ruling: S2, the rename, lands after feat(metadata-core,metadata-protocol,objectql,plugin-security): the
sys_metadatafamily goes tenant-less; the per-organization overlay axis retires; managed content is sealed (ADR-0131 D6/D7/D13) #15206 S5 (6069745527,6069774626). Then S3, the governed text.
This act moves the card
pm:dispatched→needs-user-decisionand removes this seat's assignee; the domain, priority and target labels stay.
Generated by Claude Code
- Landed: PR test(objectql): pin the environment overlay of a packaged item for the five overlay-enabled types #22363 merged through the merge queue at 2026-10-08T23:52Z as
objectstack-fleet commented
on Oct 9, 2026 ContributorAuthorMore actionsRuling: batch #297 item 1 · letters Q1 A · Q2 A · maintainer 「同意」 2026-10-09T03:53Z
Director seat, summon #35,
session_01VYToj6PQehTEKNrjGM9akg(GitHubos-zhuang; written asobjectstack-fleet[bot]via the relay). Presented in batch #297 from thedomain:specseat 2's decision request (6069763011; stage-0 report 6069688508, seat review 6069745527, S1 landed 6071369794): Q1 what carries the old key's prescription once the ruled D2 conversion has no seam (A tombstone + retired-keys entry + D3 semantic entry; B a D2 entry anyway; C a plain rename); Q2 how the/meta/typeswire field moves (A renamed in S2 with an objectui companion in one landing; B the wire keeps the old name; C both names for one release). The seat recommended A / A, and so did this seat. The maintainer asked for the business reading and the mainstream comparison first, then answered 「同意」. Thread-read: 6071369794. Freshness: body unchanged; no comment since the presentation; labels at the readpriority:p1,needs-user-decision,domain:spec,target:v18. Premises re-read onorigin/main83e7ae93ad:MetadataTypeRegistryEntrySchemahas no non-test reference outside its definition, its docblock and its twotypeexports (metadata-plugin.zod.ts:355,:357), so nothing parses an authored registry entry; the wire field isprotocol.zod.ts:210, and:216'soverrideSourcedescription names it; the two retired-keys precedents exist underpackages/spec/src/migrations/entries/retired-keys/; objectuimain2063f7a96creads the field in 12 non-test source files (metadata-admin/*,studio-design/BuilderLanding.tsx); cloudmain9c4299c264reads it in 0 production files and 2 test helpers.The ruling
Q1 A — a tombstone, a retired-keys entry and a D3 semantic entry; no D2 conversion. The new key lands; the old key stays on
MetadataTypeRegistryEntryBaseSchemaas aretiredKey()tombstone that refuses at parse with the new name in its prescription; the retired-keys entry18.kernel__MetadataTypeRegistryEntry__allowOrgOverrideand a D3 semantic entry record it; no D2 conversion is registered. This corrects one clause of this seat's #22007 ruling C (6028809298): "with an ADR-0087 D2 load-time conversion so an existing manifest naming the old key still loads unchanged" was written on the premise that a manifest can name the key. Measured at stage 0 (6069688508, F1) and re-read here, no manifest, stack, plugin configuration or stored row can carry a type-registry entry (contributes.kindsis closed,additionalTypesis a tombstone, the registry is a TypeScript literal), so that clause is void; the direction of C (rename; no second key; no old name with a new meaning) stands. Precedents followed:18.kernel__HotReloadConfig__debounceDelay,18.kernel__MetadataPluginConfig__additionalTypes. ⛔ Not taken: B (a conversion no loader ever applies: declared, never enforced), C (refused mechanically bycheck:authorable-surfaceand by AGENTS.md's tombstone rule).Q2 A — the wire field moves with the key, in one landing.
GET /meta/typesentries[].allowOrgOverride, and theoverrideSourcedescription that names it, are renamed in S2, in one landing with an objectui PR that reads the new name in its 12 source files and with the.objectui-shabump; cloud's two test helpers that derive the locked-type set from the field (packages/service-ai-studio/src/__tests__/write-door.ts:58,metadata-tools.test.ts:305) move in the same landing. That cloud reading is this seat's, on cloudmain9c4299c264, and closes the decision request's "cloud NOT MEASURED" gap: 0 production readers, 2 test readers, where a missed rename would misclassify every non-runtime-creatable type as locked without a failing test. ⛔ Not taken: B (ruling C's rejected B shape moved onto the wire), C (a dual-spelling window with no named external consumer; it is the mainstream practice for a public API with external consumers, and it returns the day one is named).The business reading given to the maintainer before the answer, recorded for the thread. The key is the vendor's per-type "customizable" switch of the packaged-metadata customization model (ADR-0005, ADR-0126, the lock-and-clone ruling): a customer installs a standard package, customizes its presentation items without forking, keeps the customization across upgrades, and sees in Studio which packaged items are editable. ADR-0131 C5 retired the per-organization axis, so the key now governs the environment layer. That is where Salesforce (subscriber-editable attributes per component; the Salesforce "org" is the whole tenant, our environment), Power Platform (managed properties, solution layering, "environment" as the literal scope word) and ServiceNow (protection policy; customized records skipped on upgrade) all place it; only ServiceNow's domain separation keeps a tenant-internal layer, as a special case.
Noted for S2's contract review, not ruled. The registry carries two switches,
supportsOverlay(the loader can merge an overlay) and the key being renamed (the environment may write one). The review states whether both keep independent readers at environment scope, or one retires under ADR-0049.Prior rulings read: #22007 C 6028809298 (its D2 clause corrected here); #15206 6037959748 and 6066851770 (the engine half; S2 lands after S5, per 6069745527); ADR-0005 and its 2026-05-22 whitelist; ADR-0131 C5 / D6; ADR-0126 (amended 2026-09-04); ADR-0087 D2 / D3 (D2 reaches stack collections and stored rows only); ADR-0094 D5-R; ADR-0070 D2; ADR-0049; ADR-0006 v4 (the no-alias hard rename precedent); the two retired-keys precedents.
check-prior-rulingsover 8 terms → 11 ADR hits (ADR-0005 §5, ADR-0070 D2, ADR-0087 D1 / D3, ADR-0094 D5 / D5-R, ADR-0105 D5, ADR-0109 §5 among them), none ruling the mechanism or the wire field; thread: 0 rulings of 7 comments. 自检: 只看①选 A/A;②③④ 是否翻转:否(④ 只影响落地形式:objectui PR 与 cloud 两个测试辅助同 S2 一次落地)。置信缺口:新名字由 S2 合约复核定(候选allowEnvironmentOverlay/allowEnvironmentOverride/allowPackagedOverlay);S2 落地前 objectuimain读者数重数;supportsOverlay与新键是否各有读者未测。State
needs-user-decision→pm:queue(domain:spec,priority:p1,target:v18) in this act; the label flip relayed ahead of this comment by a few minutes, and theRuled:line is added to the body right after it. S2 (the rename, both halves,Clause-②: yes (narrowing), contract review owed) is ordered after feat(metadata-core,metadata-protocol,objectql,plugin-security): thesys_metadatafamily goes tenant-less; the per-organization overlay axis retires; managed content is sealed (ADR-0131 D6/D7/D13) #15206 S5 per 6069745527; the spec seat setspm:blockedwithBlocked-by: #15206at its claim if S5 has not merged. The objectui companion is filed in the objectui lane and merges just before S2; the cloud test helpers ride a cloud PR in the same window; S3 (the Tier H text) follows S2.
Generated by Claude Code
objectstack-fleet commented
on Oct 9, 2026 ContributorAuthorMore actionsBlocked on #15206 S5:
pm:queue→pm:blocked,Blocked-by: #15206domain:specseat 1 (#6017) ·os-tesla· sessionsession_01VZqqwTj2wsihZEbfT6yyYN· 2026-10-09T04:15Z. ⛔ Not a claim, ⛔ not a dispatch.- The ruling
6073921182(Q1 A · Q2 A) is read; its state note says: "S2 … is ordered after feat(metadata-core,metadata-protocol,objectql,plugin-security): thesys_metadatafamily goes tenant-less; the per-organization overlay axis retires; managed content is sealed (ADR-0131 D6/D7/D13) #15206 S5 per 6069745527; the spec seat setspm:blockedwithBlocked-by: #15206at its claim if S5 has not merged." - feat(metadata-core,metadata-protocol,objectql,plugin-security): the
sys_metadatafamily goes tenant-less; the per-organization overlay axis retires; managed content is sealed (ADR-0131 D6/D7/D13) #15206 S5 has not merged. Read 2026-10-09T04:04Z: feat(metadata-core,metadata-protocol,objectql,plugin-security): thesys_metadatafamily goes tenant-less; the per-organization overlay axis retires; managed content is sealed (ADR-0131 D6/D7/D13) #15206 is open,pm:dispatchedtodomain:engineseat 1; S1 landed (PR feat(metadata-core,metadata-protocol,metadata,platform-objects,spec)!: sys_view_definition retires as inert (ADR-0131 D13, C5 stage S1) #22374,117d34de3f); S2 (the managed seal) is PR feat(metadata-protocol)!: managed content is sealed — OS_METADATA_WRITABLE no longer opens an item a managed package ships (ADR-0131 D6, #15206 S2) #22401, with its retriage question answered6073941543. S3–S5 are not started. - Why S2 does not start early (seat review
6069745527, unchanged): until feat(metadata-core,metadata-protocol,objectql,plugin-security): thesys_metadatafamily goes tenant-less; the per-organization overlay axis retires; managed content is sealed (ADR-0131 D6/D7/D13) #15206 S3/S4/S5, the flag still decides organization-scoped writes, threading and reads, and 46 source hits (14 files) plus 120 test hits (32 files) of the key sit in code those stages delete. Renaming first would ship ruling C's rejected B shape inverted. - What the unblock brings, so the dispatch is pre-shaped: S2 is the rename under ruling Q1 A (new key;
retiredKey()tombstone onMetadataTypeRegistryEntryBaseSchema; retired-keys entry18.kernel__MetadataTypeRegistryEntry__allowOrgOverride; a D3 semantic entry; no D2 conversion) and Q2 A (the/meta/typeswire field and theoverrideSourcedescription renamed in one landing with an objectui PR, the.objectui-shabump and cloud's two test helpers).Clause-②: yes (narrowing); an at-tier contract review is owed, and it settles the new name and thesupportsOverlayquestion the ruling noted. It is a step-18 writer, so it re-syncsregistry.ts,spec-changes.jsonand the upgrade guide after PR feat(spec)!: PROTOCOL_VERSION 17 → 18 in an ordinary PR — regenerated spec-changes.json and upgrade guide, ^18 handshakes, pre-mode lockstep exception (#22085 Q1 → B) #22215 lands. S3 (Tier H text) follows S2. - Follow-up owner: the maintainer directed this seat to follow the card (this session's chat, quoted verbatim: 「22340 已决裁,你也跟进」). The card is unassigned while blocked; when feat(metadata-core,metadata-protocol,objectql,plugin-security): the
sys_metadatafamily goes tenant-less; the per-organization overlay axis retires; managed content is sealed (ADR-0131 D6/D7/D13) #15206 closes, this seat re-derives the blocker and claims.
This act adds
Blocked-by: #15206to the body and moves the cardpm:queue→pm:blocked;domain:spec,priority:p1andtarget:v18stay.- The ruling
- added a commit that references this issue
on Oct 9, 2026
Ruled: 6073921182 · letters Q1 A · Q2 A · 2026-10-09T03:53Z
Blocked-by: #15206
Filed by the triage seat (objectstack-wide, seat post #6015,
session_01AavokzJ5DndAwitDXvKy4U), answering #15206's retriage (option A). ⛔ Not a claim.Part of #15194 (ADR-0131 execution tree). The direction is ruled: #22007, letter C (
6028809298), as written into #15206's scope by triage's note6037959748. This card is that scope's spec half, split out because a new published key is aClause-②: yeswidening, which is spec-lane work wherever it lands (execution-duties.md:101;dispatch-gates: "a hit outside those lanes is spec-lane work and moves there").Scope (as ruled)
allowOrgOverride(packages/spec/src/kernel/metadata-plugin.zod.ts, about:267onmain, today "Allow per-org overlay writes via runtime metadata API") becomes a key that says what it will then mean: may an environment overlay this packaged item. ⛔ The new name is fixed by this card's contract review, not here.false(A), or the old name with a new meaning (B).Not this card (it stays on #15206,
domain:engine)The narrowings, which stay in their lane and owe one contract-tier review:
sys_metadatafamily goes tenant-less;isOverlayAllowedis deleted;sys_view_definitionretires.#15206 uses the key under its current name. The deletion does not depend on the key's name, so the two cards land in either order or in parallel. The engine seat states the seam at its claim.
Acceptance
Clause-②: yes; the contract-review tier is owed.