Repository navigation
feat(metadata-core,rest,runtime,plugin-email,spec)!: the /meta doors carry no organization; organization-admin metadata authoring closes (ADR-0131 D6, #15206 S3) - #22447
Conversation
Claude-Session: https://claude.ai/code/session_01T33XAHecf6UARBkb45mecB Co-authored-by: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01T33XAHecf6UARBkb45mecB Co-authored-by: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01T33XAHecf6UARBkb45mecB Co-authored-by: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01T33XAHecf6UARBkb45mecB Co-authored-by: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01T33XAHecf6UARBkb45mecB Co-authored-by: Claude <noreply@anthropic.com>
…only saves Claude-Session: https://claude.ai/code/session_01T33XAHecf6UARBkb45mecB Co-authored-by: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01T33XAHecf6UARBkb45mecB Co-authored-by: Claude <noreply@anthropic.com>
📓 Docs Drift CheckThis PR changes 6 package(s): 23 hand-written doc(s) name something this change touched — list omitted above 15 rows. Re-derive on the tree named below: ⛔ 12 release-owned page(s) also affected — read-only, see AGENTS.md Documentation Guardrails. What this run could not see
Coarse fallback — 146 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): Which tree this was computed onThis run read A worktree cut from an older # while this PR is open — GitHub drops the merge commit once it closes
git fetch origin 41603d1f36d24915e7384f56d954e1826052e195 && git checkout 41603d1f36d24915e7384f56d954e1826052e195
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin f66c440de93c1733683a20b2a107ac8b9c98fc19 fa9f7b6483a755ab62b3d227c9c2c6e70c944b1e && git checkout -B drift-repro f66c440de93c1733683a20b2a107ac8b9c98fc19 && git merge --no-ff fa9f7b6483a755ab62b3d227c9c2c6e70c944b1e
node scripts/docs-audit/affected-docs.mjs --json f66c440de93c1733683a20b2a107ac8b9c98fc19
|
|
|
Claude-Session: https://claude.ai/code/session_01T33XAHecf6UARBkb45mecB Co-authored-by: Claude <noreply@anthropic.com>
Contract reviewServed-tier: Inputs: card #15206 (body and all 27 comments, through the S3 dev report 6077667992); PR #22447 (body, its 45-file list, the net diff against merge base ① Derived judgmentsGate verdicts on this head, as read. Required contexts: Accept-set and public-surface changes, each judged:
② Semver level
③ Boundary flagsDev deviations and questions, each answered:
Out-of-scope findings, each carried:
⛔ The head is not green: Implemented-by: VERDICT: PASS |
Contract reviewServed-tier: Inputs: card #15206 (body and all 27 comments, through the S3 dev report 6077667992); PR #22447 (body, its 45-file list, the net diff against merge base This head against the earlier record. ① Derived judgmentsGate verdicts on this head, as read. 35 check-runs: 33 Accept-set and public-surface changes, each judged:
② Semver level
③ Boundary flagsDev deviations and questions (report 6077667992), each answered:
New at this head:
Out-of-scope findings, each carried:
The head is green: 0 failure, 0 pending; the two skips are by contract. Nothing blocks the seat's ACCEPT and the ready flip beyond the one declaration escalated above. A new commit on the branch, a merge of Implemented-by: VERDICT: PASS |
|
Seat review, The finding. Three published sentences new in this PR say a legacy organization overlay is not served until ADR-0131 C7, and that a Studio re-save makes the edit live. Neither holds for a public form's view:
Read at this head. The patch. Scope the three sentences to the |
Claude-Session: https://claude.ai/code/session_01T33XAHecf6UARBkb45mecB Co-authored-by: Claude <noreply@anthropic.com>
…the public-form exception Claude-Session: https://claude.ai/code/session_01T33XAHecf6UARBkb45mecB Co-authored-by: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01T33XAHecf6UARBkb45mecB Co-authored-by: Claude <noreply@anthropic.com>
…oors and name the public-form exception Claude-Session: https://claude.ai/code/session_01T33XAHecf6UARBkb45mecB Co-authored-by: Claude <noreply@anthropic.com>
Contract reviewServed-tier: Inputs: card #15206 (body and all 31 comments, through the S3 patch-round-2 report 6080128648); PR #22447 (body, its 45-file list, the net diff against merge base This head against the earlier records. Since ① Derived judgmentsGate verdicts on this head, as read. 35 check-runs: 33 Accept-set and public-surface changes, each judged on this head's net diff:
② Semver level
③ Boundary flagsDev deviations and questions (reports 6077667992, 6078632031, 6078815440, 6079504896, 6080128648), each answered:
The FAIL and its patch (prose only; no code or test change): in all five copies, replace the clause from "and prefer its overlay" to the end of the sentence with words to this effect: prefer its overlay for the form's body, while a withdrawal in either layer closes the form — so a legacy organization overlay of a public form keeps serving its body there; a Studio re-save of that body (an environment row) does not change the body that public form serves, and a Studio withdrawal (an environment row) still closes it. Keep "fail-closed" in the two copies that have it or add it to all five, the seat's call. Fold ①14g in by dropping "one" ("Public forms are the exception among the doors that serve metadata to end users") or by naming the package-manifest read as S4's. The entry edit regenerates New at this head, otherwise:
Out-of-scope findings, each carried:
The head is green (0 failure, 0 pending; the two skips by contract) and the code contract is sound on every item above; the verdict turns on ①14f alone, a published sentence this diff's own pin contradicts. Nothing is armed or readied on this head. Implemented-by: VERDICT: FAIL |
Claude-Session: https://claude.ai/code/session_01T33XAHecf6UARBkb45mecB Co-authored-by: Claude <noreply@anthropic.com> # Conflicts: # packages/rest/src/execctx-consumer-census.test.ts
…for the body only; a withdrawal in either layer closes the form Claude-Session: https://claude.ai/code/session_01T33XAHecf6UARBkb45mecB Co-authored-by: Claude <noreply@anthropic.com>
Contract reviewServed-tier: Inputs: card #15206 (body and all 32 comments, through the S3 patch-round-3 report 6080865956); PR #22447 (body, its 45-file list, the net diff against merge base This head against the FAIL record. Since ① Derived judgmentsGate verdicts on this head, as read. 35 check-runs: 33 Accept-set and public-surface changes, re-made on this head's net diff (byte-identical code since the FAIL head):
② Semver level
③ Boundary flagsDev deviations and questions (reports 6077667992, 6078632031, 6078815440, 6079504896, 6080128648, 6080865956), each answered:
New at this head, otherwise:
Out-of-scope findings, each carried:
The head is green (0 failure, 0 pending; the two skips by contract), the code contract is byte-identical to the one the three earlier records judged sound, and the one published sentence that failed Implemented-by: VERDICT: PASS |
… merging main at e148ca9 (step 18: 64 conversions, 329 semantic entries) main added two step-18 semantic entries since 8b713fa, both from #22447 (7895671): manage-org-presentation-retired and meta-doors-organization-scope-retired. At protocol 18 both generators project every step-18 entry, so both documents gain them. The conversion ids are unchanged; the 16 -> 18 aggregate is 406, equal to the registry. Claude-Session: https://claude.ai/code/session_01VZqqwTj2wsihZEbfT6yyYN Co-authored-by: Claude <noreply@anthropic.com>
Refs #15206 (S3)
Clause-②: no (narrowing)
Stage S3 of the
sys_metadatatenant-less card (ADR-0131 D6): the/metadoors on both transports carry no organization into a metadata read or write, and the organization-admin authoring door closes. The card stays open for S4 and S5. Claim 6076144407 (seatdomain:engine#2); dev sessionsession_01T33XAHecf6UARBkb45mecB.What changes
PUT,DELETE,POST …/publishandPOST …/rollbackon/api/v1/meta/:type/:name, and the dispatcher's/metaPUT, hand the protocol no organization. The row, its audit row and its history row carryorganization_idNULL, whatever the caller's active organization, for every type./layersand?layers=true),/published,GET /meta/_drafts,/history,/diff,/audit(noworganizationId: null, the environment rows),GET /meta/diagnosticsand/references. On the dispatcher: the item read,?state=draft, the layered view,/published, the list and_drafts. The stage plan's ordering rule holds: reads-first would hide the Default-Org saves the doors still wrote, and writes-first would let legacy org rows shadow new environment saves.metaWriteCapabilityVerdictadmitsisSystemormanage_metadataonly. Itsmanage_org_presentationarm is gone, along with thecanonicalTypeandactiveOrganizationIdinputs, which nothing reads any more. A holder ofmanage_org_presentationwithoutmanage_metadatagets403on all four item doors, for every type and whatever its active organization:FORBIDDENon REST,PERMISSION_DENIEDon the dispatcher, with the plain sentence… requires the \manage_metadata` capability.`manage_org_presentationretires fromPLATFORM_CAPABILITIES, and so doesORG_PRESENTATION_AUTHORING_CAPABILITY. ADR-0087 entry:manage-org-presentation-retired.organizationIdForMetaWrite(@objectstack/metadata-core) andmetaReadOrganizationId(@objectstack/rest). ADR-0087 entry:meta-doors-organization-scope-retired.tenancysource is gone..changeset/15206-meta-doors-environment-only.md:minorwith!on metadata-core, rest, runtime, plugin-email and spec. It carries the FROM → TO map andadr-0087: registeredfor both entries.Route measured against the stage plan (Zone 2), and where it moved
packages/metadata-protocol/src/protocol.tsimportsorganizationIdForMetaRead. It applies it insidegetMetaItem,getMetaItems,getMetaItemLayered, history, diff and the page read, and the anonymous form doors rely on that gate. Their read of the Default Organization's layer must stay, fail-closed, until C7 (triage Q3 A). Deleting the read predicate in S3 would have meant editingprotocol.ts, which is S4/S5 territory, and would have broken the read Q3 A keeps. SodeclaresOrgOverrideandorganizationIdForMetaReadstay inmetadata-core, with their headers rewritten to say why and which stage deletes them. OnlyorganizationIdForMetaWriteis deleted: every caller it had was a door, plus the one site below.protocol.tsandsys-metadata-repository.tsare not touched. Some of the protocol's comments still name the deleted helper; they are left for S4/S5 to rewrite.packages/runtime/src/domains/packages.ts: one minimal edit (S4 territory, for the PM to re-declare). The ADR-0045 publish flip calledorganizationIdForMetaWrite('app', organizationId). SinceappisallowOrgOverride: false, that call always returnedundefined. The call and its spread are removed, so behaviour is byte-identical. The packages domain's raw organization threading is left as it is, for S4./referencesnames no organization either. The stage plan did not name it. Its sources are read environment → code, the world the doors now serve. A legacy org-scoped source is not swept: no door serves it until C7, and that ceremony re-judges what it carries.anonymousFormOrganization,resolveFormBySlug,readFormObjectDefinitions) still read the Default Organization's layer.Pins (both transports)
rest-server-meta-read-org-scope.test.ts): an org-activemanage_metadataauthor saves each of the five types, and the stored row hasorganization_idNULL. GET and the list serve it to the author's org, to another org and to an org-less caller. ⭐ A legacy org-scoped row, planted straight through the protocol, is served by no read door, not even to its own organization, on both cache arms, with or without an environment row beside it. Diagnostics (both arms), history and references follow the same contract./historyand/diff(rest-server-meta-history-diff-org-scope.test.ts): the environment log is served to every caller. A legacy org log is served by neither door.rest-server-meta-write-org-scope.test.ts(no organization for any registered type, on PUT, DELETE, publish, rollback and/published),rest-server-meta-org-scope-url-spelling.test.ts(every URL spelling) andrest-server-meta-cached-etag-door-scope.test.ts(the validator carries no organization, and §3 is the control that a supplied organization would move it).manage_org_presentationholder gets 403 FORBIDDEN on PUT, DELETE, publish and rollback, protocol never reached:meta-write-door-capability-enumeration.test.ts,meta-item-save-capability-gate.test.ts. An org-activemanage_metadatacaller is admitted on all four with no organization on the request.runtime/src/meta-write-org-scope.test.ts): aviewwritten by an org-active session storesorganization_idNULL (the former CONTROL, flipped), and GET answers 200. Flow and object stay env-wide (control).meta-save-capability-gate.test.ts: amanage_org_presentationholder gets 403 PERMISSION_DENIED on view, email_templates, object and flow.meta-read-org-scope-parity.test.ts: a CURRENT member of an organization holding a legacy overlay is served the environment row on both transports, and no read names an organization.orgContext, single posture with the Default Organization active) and the walledisolatedfixture both show an org-active admin's save landing env-wide (showcase-public-form-withdrawal,public-form-withdrawal-walled,email-template-overlay-survives-boot). ⭐showcase-public-form-withdrawal-layersnow plants legacy organization overlays through the protocol and pins that a legacy organization WITHDRAWAL still closes both anonymous doors while the form is open environment-wide. That is Q3 A's fail-closed read, kept.flowandobjectcases, and the flow-door dogfood files in the run below.Reverse verification (committed fix, then a mutation through
scripts/ablation-replace.mjs, restored)organizationIdForMetaWriteno longer exists, so the PUT door was re-threaded with the rawctx?.tenantId, the closest spelling. The anchor hit 1 time and moved 1 to 0. The blob wentf506ba04e5e5toe83f3e014a85.With the mutation,
rest-server-meta-write-org-scope.test.tsandrest-server-meta-read-org-scope.test.tsgave 37 failed / 16 passed (53). The failures include the stored-organization_id-NULL cases for all five types, the "no registered type carries the organization" sweep, and the legacy-row-not-shadowing cases. They also include write-then-read cases onobject, where the protocol refuses an org-scoped write. Restore: blob equals HEAD (f506ba04e5e5), andgit diff HEADis empty.Owed measurement: do persisted permission sets naming
manage_org_presentationstill load?Yes.
PermissionSetSchema.systemPermissionsisz.array(z.string()).validateCapabilityReferencesadds every name a stack grants to its known set, so a grant of a now-undeclared name lints clean.bootstrapSystemCapabilitiesupserts only and never prunes. A persisted set therefore parses, loads and keeps its other grants. Itsmanage_org_presentationgrant admits nothing, and the earlier platform-seededsys_capabilityrow stays until an operator deletes it. That makes the ADR-0087 entry asemantic/one: no retired key, and no conversion.Sibling repo (AGENTS.md Post-Task Checklist step 4)
git grepat the pinned.objectui-shaa58626c88dfinds no imports oforganizationIdForMetaWrite,organizationIdForMetaRead,declaresOrgOverride,metaReadOrganizationId,metaCallerOrganizationIdorORG_PRESENTATION_AUTHORING_CAPABILITY(exit 1 each). The control wordoverlayScopehits 52 times.manage_org_presentationappears 14 times, all as string data:CapabilityMultiSelectField.tsx's curated label set, the i18n packs and CHANGELOGs. The Console Pin Gate builds objectui and does not run its tests, so nothing here breaks the pin. objectui'sCapabilityMultiSelectField.specParity-6285.test.tsxorphan check will turn red once objectui bumps its spec dependency past this release, so the sibling fix rides that bump. Carrier: the next objectui spec bump. Not done here, and noted in the report.Cross-lane paths (declared on the lanes' seat posts by the PM)
domain:cli:packages/rest/src/rest-server.ts,meta-item-read-gate.ts,index.ts,rest-route-ledger.ts. Tests:execctx-consumer-census,meta-alternate-door-read-gates,meta-dashboard-view-i18n-explicit-override,meta-item-save-capability-gate,meta-publish-package-scope,meta-write-door-capability-enumeration,rest-server-audit-org-scope,rest-server-meta-cached-etag-door-scope,rest-server-meta-history-diff-org-scope,rest-server-meta-org-scope-url-spelling,rest-server-meta-read-org-scopeandrest-server-meta-write-org-scope. Runtime:packages/runtime/src/domains/meta.ts,domains/packages.ts(the minimal edit above),route-ledger.ts. Runtime tests:meta-write-org-scope,domains/meta-save-capability-gate,domains/meta-read-org-scope-parity.domain:spec:packages/spec/src/security/capabilities.tsandcapabilities.test.ts;packages/spec/src/migrations/entries/semantic/18.manage-org-presentation-retired.tsand18.meta-doors-organization-scope-retired.ts;packages/spec/src/migrations/registry.ts(regenerated).domain:services:packages/plugins/plugin-email/src/bootstrap-declared-email-templates.ts, its.test.ts, andemail-plugin.ts;packages/services/service-datasource/src/plugin.ts(comment only).domain:devx:content/docs/concepts/metadata-lifecycle.mdx,content/docs/kernel/contracts/metadata-service.mdx,content/docs/ui/create-vs-edit-form.mdx,content/docs/protocol/objectui/concept.mdx.packages/qa/dogfood/test/email-template-overlay-survives-boot,public-form-withdrawal-walled,showcase-public-form-withdrawal,showcase-public-form-withdrawal-layers.domain:engine):packages/metadata-core/src/meta-write-capability.ts,meta-write-org-scope.ts,index.tsand their two tests.content/docs/permissions/capabilities.mdx. It names neither the capability nor organization-scoped authoring, so it is unchanged.Verification (head
9a2d88035; the later heads are three prose patch rounds and three merges ofmain, each reported on #15206)pnpm --filter @objectstack/rest exec vitest run: 270 files / 5148 passed, 327 skipped.pnpm --filter @objectstack/rest typecheck(withcheck:test-typecheck): OK.pnpm --filter @objectstack/runtime exec vitest run: before the three runtime files were flipped, 343 passed and 3 failed. After the flip, all three files pass:meta-write-org-scope20,meta-save-capability-gate16,meta-read-org-scope-parity15.pnpm --filter @objectstack/runtime typecheck: OK.@objectstack/metadata-core: test 17 files / 387; typecheck OK.@objectstack/plugin-email: test 31 / 535; typecheck OK. speccapabilities.test.tsplussrc/migrations: 5 files / 210./metadoors, email templates and public forms gave 34 passed / 4 failed before the flips. After the flips, the 4 rerun to 18 / 18 passed.pnpm --filter @objectstack/spec buildthencheck:generated: all 15 artifacts up to date.gen:migration-registrywrote the two entries.node scripts/pm/dispatch-gates.mjs --commandsderived 119 commands, and all 119 exit 0. Three first answered exit 3 (PREREQUISITE NOT MET) and passed once the prerequisite was met:check-plugin-teardown-shape --self-testafter its pinned fixture commit was fetched, andcheck:skill-examplesandcheck:dual-build-cjs-loadsafter unrelated unbuilt packages were built.--ranreconciliation: 119 derived, 119 run, 0 NOT-MEASURED (a derived zero).fa9f7b6483: 1313 added, 2698 deleted (4,011, under the 5,000 human-merge line).Acceptance notes
/metadoors no longer serve it. C7 (feat(objectql,cli): inventory + migration — four fates per object, mirrors deleted only after the id→name rewrite is verified, per-table boot report (ADR-0131 D10) #15211) deletes it with the carry.sys_capabilityrow the platform seeded earlier formanage_org_presentationis not pruned; the seeder upserts only. It names a capability nothing consults.mainwas merged into the branch during the patch rounds. Atfa9f7b6483the merge base is166a94f75d;mainhas since moved 3 commits, which merge without conflict. CI judges the merge ref.Generated by Claude Code