Skip to content

[finding] AdminScope.manageBindings and authorEnvironmentSets are honoured by no write path after C3; the ADR-0090 D9/D12 junction gates are unmeasured; permissions docs describe removed gates #23488

Closed

Description

@objectstack-fleet

Ruled: 6181988435 · letter A · 2026-10-15T01:05Z

Filing gate ①, class (b), declared ≠ enforced: the enforce-or-remove channel. Filed by domain:engine seat 2 (#20966) · os-steve · session_01ADzJtzYTLUfgrRZHxkagkX at #22975's landing (8-post, PR #23428 → 0d3ea295d1), as the seat review 6124054616 (finding 1) and the contract record 6162274013 (item 5) provide. Domain and area are triage's call: the positions span packages/spec, plugin-security and content/docs.

Positions (read on main at 0d3ea295d1)

  1. AdminScope.manageBindings and AdminScope.authorEnvironmentSets are declared and honoured by no write path.
    • The keys are declared in packages/spec/src/contracts/security-service.ts:92-93 and authorable in a permission set's adminScope (ADR-0090 D12; the form at packages/spec/src/security/permission.form.ts:62). The metadata form's help text (packages/platform-objects/src/apps/translations/*.metadata-forms.generated.ts:2326) tells authors that they grant binding and environment-set authoring.
    • delegated-admin-gate.ts still parses them (:213, :519, :883) and compares them in strict containment (:893).
    • But C3 retired the two objects they governed: sys_position_permission_set (the binding) and sys_permission_set (env-set authoring). Their gates left with them (assertBindingWrite, assertSetAuthoring). A position's sets are now part of its definition (ADR-0131 D3). Authoring a permission set is an environment metadata write, judged by manage_metadata, not by a delegate's scope.
    • So an author can declare a delegate who "may bind" or "may author env sets", and no door honours it.
  2. The ADR-0090 D9 (guest) and D12 junction gates are not measured on the new model.
    • The D5/D7 door counterparts are pinned (suggested-binding-acceptance.dogfood.test.ts: the risky accept answers 403, and PUT /meta/position/everyone answers 403).
    • D9's guest binding and D12's delegate gates had their junction-row enforcement removed with the junction, and no carrier re-states them against the definition write (record 6162274013, ③).
  3. Text that still describes the removed gates.

Wanted

  • Positions 1 and 2: enforce or remove, under ADR-0049. Either the definition write judges the delegate's manageBindings (a delegate edits a position's permissionSets only inside its subtree and allowlist) and authorEnvironmentSets, or the two keys retire through the ADR-0087 route with the forms, help text and pins that a removal needs (spec-property-retirement). Which one is a product question when the measurement shows no user. Per the four axes, the default with zero pull is to retire.
  • Position 3: correct the text to the model on main. This is docs-accuracy class, with no contract change.
  • One pin per position kept, or the ADR-0087 marker per key retired.

Not here

Dedupe: no open card names these keys or pages. The search covered manageBindings, authorEnvironmentSets and the permissions pages; #23323 and #22379 are closed and cover other sentences.

Activity

  1. objectstack-fleet commented on Oct 14, 2026

    @objectstack-fleet
    ContributorAuthor

    Triage: first grade · priority:p1 + target:v18 · domain:spec · area:access · pm:queue · triage seat · session_01AavokzJ5DndAwitDXvKy4U · 2026-10-14T13:56Z

    Read fresh. Declared ≠ enforced: AdminScope.manageBindings and authorEnvironmentSets (packages/spec/src/contracts/security-service.ts:92-93) are authorable, but no write path honours them after C3.

  2. objectstack-fleet commented on Oct 14, 2026

    @objectstack-fleet
    ContributorAuthor

    Claim: PM loop round 3 · 2026-10-14T15:47Z · measure-first: a reading-only dispatch
    Session: session_0121ooBYRmS8ziFXm9ai5YHm
    Account: sunhaolin (the seat's linked user as GET /user answers it; this card's assignee)
    Branch: none (reading-only: no closure, no test run, no PR)
    Worktree: none (reads origin/main through git show / git grep and REST GETs)
    Domain: domain:spec
    Seat: domain:spec#4 (seat post #18917)
    File surface: none written. Reads cover packages/spec/src/contracts/security-service.ts (AdminScope), packages/spec/src/security/permission.form.ts, packages/plugins/plugin-security/src/delegated-admin-gate.ts, the position definition write path, the three content/docs/permissions/*.mdx pages, examples/app-showcase/src/security/permission-sets.ts, and objectstack-ai/objectui (Studio's adminScope authoring).
    Container & model: S, mode:subagent, model: default tier (reading-only; no build).
    Clause-②: no
    Responsibility: delegated-admin-gate.ts still parses AdminScope.manageBindings / authorEnvironmentSets after C3 retired the two objects they governed, so no write path honours them | none: the definition write is judged by manage_metadata, not by a delegate's scope (to be measured) | an author who declares a delegate that "may bind" or "may author env sets"; whether anyone does is the reading this round takes
    Thread-read: 6170064364
    Ruling-ref: none. Triage 6170064364 gives the card's default under the four axes: retire on zero pull; a measured user turns it into a product question. Per the measure-first rule, this round only measures. The building dispatch waits on a ruling recorded on this card.
    Serial constraints cleared: reading-only, no write surface. The building round will re-run the overlap scan against open PRs in packages/spec/src/contracts/ and plugin-security.

  3. objectstack-fleet commented on Oct 14, 2026

    @objectstack-fleet
    ContributorAuthor

    os-dev-report (reading)
    Reading-only round on #23488 · dispatched by the domain:spec seat 4 PM · session session_0121ooBYRmS8ziFXm9ai5YHm · identity = Claim 6172320924 (Branch: none) · read at 2026-10-14T16:03Z

    Trees read. objectstack origin/main = 1418de4c7e (git show / git grep, read-only). objectstack-ai/objectui main = df05784ae4 (one REST tarball GET, grepped locally) plus a contents GET of the facet at the pin .objectui-sha = b9814b334d. /search/* is denied, so no code search was used anywhere. No branch, no worktree, no build, no test, no PR, no file edited. The card read 0d3ea295d1; git merge-base --is-ancestor 0d3ea295d1 origin/main exits 0, and git log 0d3ea295d1..origin/main over the gate, the spec files, the three pages and the showcase file is empty, so the card's line numbers still hold.

    Summary

    • Writers (reading 1): no production author sets either key to true. The only production author, the showcase, writes the default false. The true writers are two test fixtures. Control leg: the live sibling manageAssignments: true has 3 production authoring sites, so the census can see a writer. Two authoring doors make the keys settable: Setup's nested adminScope form and Studio's two switches.
    • Readers (reading 2): no door that writes what the keys name (a position's permissionSets, or a permission set) consults them. That door's judge is manage_metadata plus the wall, and no delegate-scope check runs there. Correction to the card: the keys do change one decision, the strict-containment comparison on the two assignment doors, and in one direction an inert flag satisfies the "equal scope refused" rule (traced from source, not run).
    • D9 / D12 (reading 3): D9's guest binding is carried by ADR-0138 D3 items 4-5. No door binds guest, and that absence is pinned (403 NOT_OVERRIDABLE). D12's binding and authoring rights have no judge and no pin, and the new model gives a position definition no business-unit dimension to judge them against.
    • False text (reading 4): the three pages no longer name assertBindingWrite, assertSetAuthoring or a walled data door (0 hits tree-wide). Docs-side false text narrows to two "or authoring" sentences. The showcase carries seven false sentences: five in permission-sets.ts and two in its tour doc. Same-class text also sits in the spec JSDoc and .describe(), the liveness ledger, two plugin-security comments, metadata-core's wall docstring and objectui's help string.
    • Recommendation: A (retire both keys now).

    Reading 1: who writes manageBindings / authorEnvironmentSets

    Spelling: git grep -n -E 'manageBindings|authorEnvironmentSets' 1418de4c7e returns 73 lines in 36 files. 13 are release history (8 CHANGELOG.md files, .changeset/pre/15204-walled-catalog-refusal.md:24, content/docs/releases/v13.mdx:91). The rest are declarations, readers and fixtures.

    Spelling for value-carrying writers: git grep -n -E '(manageBindings|authorEnvironmentSets)"?\s*:\s*(true|false)' 1418de4c7e returns 18 lines.

    surface production authors tests / fixtures
    examples/** app-showcase/src/security/permission-sets.ts:400-401, both false, which is the schema default (packages/spec/src/security/permission.zod.ts:692,694) none
    apps/** 0 0
    packages/** sources 0 (readers only, reading 2) true: plugin-security/src/delegated-admin-gate.test.ts:27-28, plugin-security/src/can-write-object-admission.test.ts:256-257. false: delegated-admin-gate-catalog.test.ts:33-34, delegated-admin-gate-cross-organization.test.ts:76-77, delegated-admin-gate.test.ts:752-753,864-865, granted-by-writer-provenance.test.ts:89-90, objects/reserved-identity-names.test.ts:216-217
    dogfood stack 0 packages/qa/dogfood/test/showcase-permission-zoo.dogfood.test.ts:98 reads the showcase's false
    content/docs/** examples 0. Both adminScope samples (permissions/delegated-administration.mdx:25-30, permissions/permission-sets.mdx:323-327) set manageAssignments only. The keys appear in the table row delegated-administration.mdx:59 and in the generated reference references/security/permission.mdx:34-35,128,185-186 none
    skills/** 0 (skills/objectstack-data/rules/security.md:53 teaches adminScope with manageAssignments only) none
    • Control leg: git grep -n -E 'manageAssignments"?\s*:\s*true' 1418de4c7e finds 3 production authoring sites (showcase permission-sets.ts:399, delegated-administration.mdx:28, permission-sets.mdx:325) and tests in 10 files. The page's other hit, delegated-administration.mdx:113, is a response sample, not an author. The same census that sees those writers finds none for the two keys.
    • objectui df05784ae4: grep -rn -I -E 'manageBindings|authorEnvironmentSets' over the tarball returns 10 lines in 5 files.
      • Studio's permission editor renders the keys as author-settable switches: packages/app-shell/src/views/metadata-admin/PermissionAdvancedFacets.tsx:659-674. The manageAssignments, manageBindings and authorEnvironmentSets switches each write { [key]: !!v } into the draft adminScope (:670), typed from the spec AdminScope (:31, :123).
      • Labels: i18n.ts:2477-2478 ("Manage bindings", "Author env sets"), zh :5761-5762. Help: :2467-2468 and zh :5754. Pin: PermissionAdvancedFacets.adminScopeAnchor.test.tsx:106-117. objectui ADR: docs/adr/0056-permission-editing-studio.md:142.
      • At the pin b9814b334d the same two switches stand at :662-663.
      • This is an authoring door, not an author: objectui ships no set with either key set.
      • No objectui code reads the two fields from my-delegable-scope: console/organizations/manage/InviteMemberDialog.tsx:92-107,301,316 reads placeableBusinessUnitIds and assignablePositions only.
    • Setup (objectstack): packages/spec/src/security/permission.form.ts:55-62 gives adminScope a json widget that renders its six keys as a nested form.
    • NOT MEASURED: objectstack-ai/cloud, every other consumer repo, and any deployment's stored rows are unreadable from this fleet. The stored rows are sys_metadata permission items and legacy sys_permission_set.admin_scope.

    Reading 2: readers, traced from the door to the judge

    Readers on 1418de4c7e:

    1. Schema. AdminScopeSchema (permission.zod.ts:654-702, strictObject) parses both keys at :692,694 with default false.
    2. Contract. DelegableAdminScope (packages/spec/src/contracts/security-service.ts:85-98, fields :92-93) is the response shape of describeDelegableScope (:1014).
      • Served by GET /api/v1/security/my-delegable-scope (packages/rest/src/rest-server.ts:10836-10880; ledger packages/rest/src/rest-route-ledger.ts:399).
      • Reached through the SDK (packages/client/src/index.ts:6096-6097) and objectui's auth client (packages/auth/src/createAuthClient.ts:719-731).
      • It is a report only, and no consumer reads the two fields.
    3. Gate normalisation. resolveHeldScopes (packages/plugins/plugin-security/src/delegated-admin-gate.ts:913-935) forces both keys to booleans at :925-926. Two consumers:
      • 3a. describeDelegableScope copies them into the report (:519-520). Placement uses manageAssignments only (:525-528).
      • 3b. definitionContainsStrictly (:879-909): the rights axis at :892-894 and strictness at :900-908. It is reached through assertScopeGrantContainment (:861-872) from three callers: assertAssignmentWrite (:789, a delegate's sys_user_position write), assertDirectGrantWrite (:825, sys_user_permission_set) and the picker (:546).
    4. Help text. permission.form.ts:62 and the generated bundles packages/platform-objects/src/apps/translations/{en,es-ES,ja-JP,zh-CN}.metadata-forms.generated.ts:2326 name the keys and their defaults only. The grant claim lives in the .describe() strings (permission.zod.ts:692,694), which feed the generated reference content/docs/references/security/permission.mdx:34-35,185-186. objectui's help text also carries it (reading 4).
    5. Tests. packages/spec/src/security/permission.test.ts:24-25 checks the defaults; the dogfood zoo :98 checks the showcase value.
    6. Lint. git grep -n -E 'manageBindings|authorEnvironmentSets' 1418de4c7e -- packages/lint/src returns 0. No rule reads either key.

    scopesCoverUser (:420-433) reads no flag: any held scope authorises explaining a user in its subtree. Its JSDoc (:411-413) still says "assignments, bindings, authoring".

    The door that writes what the keys name, and its judge.

    • The binding. A position's permissionSets is written by PUT /api/v1/meta/position/:name (REST rest-server.ts:7115 through metaSaveVerdict :3739-3745; dispatcher packages/runtime/src/domains/meta.ts:911-916), and by its publish, rollback and reset twins.
    • The judges, in order:
      • metaWriteCapabilityVerdict (packages/metadata-core/src/meta-write-capability.ts:88-102) admits isSystem or manage_metadata and nothing else. In the code's own words, "the verdict reads the caller alone: no type and no organization enters it" (rest-server.ts:3734-3737).
      • walledCatalogWriteVerdict (:202-232) admits only the platform operator under group / isolated.
      • saveMetaItem's item refusals follow, for example the packaged-base NOT_OVERRIDABLE (packages/metadata-protocol/src/protocol.ts:17291-17320, 17411-17417).
    • Permission-set authoring goes through the same door (PUT /api/v1/meta/permission/:name) under the same judges. The clone door (packages/runtime/src/domains/permission-set-clone.ts:238, 252) adds nothing and does not copy adminScope (:109, :119).
    • No delegate-scope check runs at any of these doors. DelegatedAdminGate.assert returns at once unless the object is in GOVERNED_OBJECTS, which is sys_user_position, sys_user_permission_set and sys_member (delegated-admin-gate.ts:133-149, :284). No /meta door calls it. Its one other caller is the data-door preview canWriteObject (plugin-security/src/security-plugin.ts:6189-6200).
    • Outcome: a delegate without manage_metadata cannot write any definition whatever its flags, and a delegate with manage_metadata can write any definition whatever its flags. Neither key changes either outcome.

    A write path the keys do change (refines "honoured by no write path"). Traced from source, NOT MEASURED by a run:

    • (i) Refusal. The granted set's scope has manageBindings: true (or authorEnvironmentSets: true) and the delegate's scope does not. Result: "not strictly contained" (:893-894), refused on sys_user_position / sys_user_permission_set.
    • (ii) Admission. The delegate's scope has manageBindings: true, and the granted set's scope is identical except for false. :900-908 counts that as strictly smaller and admits it. Since the flag confers nothing, the delegate hands out an effectively identical scope. That is the lateral propagation the equal-scope refusal exists to stop (delegated-admin-gate.test.ts:311-318).
    • No test pins (i) or (ii): the gate fixture sets both flags true on both sides (:23-30, :48). This sits inside the card's own class (b) and is not a new card.

    Reading 3: Position 2, D9 and D12 on the new model (source only)

    D9 (guest binding).

    • No door writes a set into guest's definition. ADR-0138 D3 item 5 (docs/adr/0138-guest-model-anonymous-principal-doors-grants-and-organization.md:379-396) records it: a save of the packaged guest position answers 403 NOT_OVERRIDABLE, the package door refuses a built-in name, and confirmAudienceBindingSuggestion refuses a guest row.
    • The tier predicate describeAnchorForbiddenBits(set, 'guest') has no caller on main (ADR-0138 :170-173; packages/lint/src/validate-security-posture.ts:29-37).
    • ADR-0138 D3 item 4 (:370-378) assigns the tier check to the bind door when that door is built.
    • So no junction-row gate is owed today. The card's "D9 unmeasured" is superseded: the absence is measured.

    D12 (a delegate's subtree and allowlist at the definition write).

    • No judge exists (reading 2).
    • PositionSchema has no business-unit key (packages/spec/src/identity/position.zod.ts: name :82, label, description, permissionSets :106, delegatable :132). A position is one environment definition shared by every organization (ADR-0131 D3). So "position↔set bindings within that subtree" (ADR-0090 D12, docs/adr/0090-permission-model-v2-concept-convergence.md:521-522) has nothing on the definition to anchor to.
    • Under a wall, ADR-0131 D12 items 7-8 refuse any non-operator definition write outright (meta-write-capability.ts:156-201).

    Pins that exist, by ADR-0090 row:

    row pin what it covers
    D5 acceptance by manage_metadata only packages/qa/dogfood/test/suggested-binding-acceptance.dogfood.test.ts:108-124, :136-150, :161-167 accept turns a member's 403 into 200; a tenant admin's accept is refused 403; revoke
    D5 anchor sealed same file :152-156 PUT /meta/position/everyone answers 403
    D7 high-risk set same file :126-134 the risky accept answers 403
    D9 guest packages/qa/dogfood/test/guest-grants-channel.dogfood.test.ts:479-491 (no door: PUT /meta/position/guest answers 403 NOT_OVERRIDABLE), :509-523 (reference-app census: no guest binding in showcase, crm or multi-package), :290-365 (grants channel over a test-installed binding); packages/plugins/plugin-security/src/audience-anchors.test.ts:93-106 (the predicate only) the absence of a bind door, and the predicate
    D12 assignments delegated-admin-gate.test.ts:225-318 (subtree, allowlist, anchors, equal-scope refusal) and :950 (judged by the definition's permissionSets, not a junction row); delegated-admin-gate-catalog, -cross-organization and -holding-organization tests; showcase-permission-zoo.dogfood.test.ts; delegated-admin-invite.dogfood.test.ts the assignment half of D12 on the new model
    D12 bindings and env-set authoring none there is no judge to pin
    who writes definitions packages/qa/dogfood/test/wall-catalog-refusal.dogfood.test.ts:131-245 (walled refusal plus the single control); packages/metadata-core/src/meta-write-capability.test.ts the generic capability gate; no pin names a delegate scope at that door

    Reading 4: Position 3, the false text

    Spellings and counts:

    • git grep -n -E 'assertBindingWrite|assertSetAuthoring' 1418de4c7e returns 0 hits tree-wide, changelogs included.
    • git grep -n sys_position_permission_set 1418de4c7e over the three pages returns 1 hit per page (delegated-administration.mdx:59, permission-sets.mdx:241-242, positions.mdx:11). Each states the junction is retired, which is true.
    • git grep -n -i 'data door' over content/docs/permissions finds only "metadata door".

    So the docs half of Position 3 is narrower than the card states. In the list below, FALSE means the sentence is wrong on main; PROSPECTIVE means it describes a door that does not exist yet.

    Docs

    • FALSE content/docs/permissions/delegated-administration.mdx:60: "Granting or authoring a set that itself carries an adminScope requires a held scope that strictly contains it". Authoring goes through the metadata door, which reads no adminScope; the row above, :59, already says so.
    • FALSE content/docs/permissions/permission-sets.mdx:338-339: "strict containment when granting or authoring a set that itself carries an adminScope". Same reason.
    • PROSPECTIVE permission-sets.mdx:268: "the wildcard ban is the stricter guest tier's rule". True of the predicate, but no door runs it for guest.
    • Accurate on main: delegated-administration.mdx:59 (the flags admit no write and still count in containment). positions.mdx: no false sentence found (:11-12 and :111-120 read correctly).

    Showcase

    • FALSE examples/app-showcase/src/security/permission-sets.ts:357-359: "Guest bindings face the STRICTEST lint tier". The lint reads isDefault sets against everyone only (validate-security-posture.ts:29-37, :905).
    • FALSE :361-362: "Binding is an ADMIN action (Setup → Access Control → bind guest → showcase_guest_portal)". No door binds a set to guest (reading 3).
    • FALSE :384-385: "manageBindings stays false: this delegate re-staffs positions, they do not re-compose what a position means". This implies true would let a delegate re-compose a position; no door honours that.
    • FALSE :26-27 "(lint-blocked on everyone/guest bindings)" and :166-167 "the D7 lint blocks binding this set to the everyone/guest anchors". showcase_auditor is not isDefault, so no lint rule reads it, and the guest half has no door.
    • FALSE examples/app-showcase/src/docs/showcase_tour_security.md:41-42 ("the publish linter blocks it from everyone/guest bindings") and :51-52 ("binding it is an admin action in Setup"). Same reasons as above.

    Contract and code text, same class, beyond the card's list

    • FALSE packages/spec/src/security/permission.zod.ts:
      • :643-644: the D12 WHAT includes "position↔set bindings, author environment-owned sets".
      • :648-650: enforcement "on sys_user_position / sys_position_permission_set / sys_user_permission_set / sys_permission_set".
      • :691: "May create/delete sys_position_permission_set bindings…".
      • :946-948: "may manage assignments/bindings (and optionally author environment sets)".
      • .describe() at :692, :694 ships into the generated reference content/docs/references/security/permission.mdx:34-35, :185-186.
    • FALSE packages/spec/liveness/permission.json:39: the adminScope evidence names gates on sys_position_permission_set / sys_permission_set.
    • FALSE packages/plugins/plugin-security/src/objects/sys-user-permission-set.object.ts:30: "delegated manageBindings direct grants write this". Direct grants are judged on manageAssignments (delegated-admin-gate.ts:814).
    • FALSE delegated-admin-gate.ts:411-413: "Any scope kind qualifies (assignments, bindings, authoring)".
    • FALSE packages/metadata-core/src/meta-write-capability.ts:184-185: lists "the security plugin's data door on the catalog objects" among the doors that ask the walled verdict. 0d3ea295d1 removed that door: git grep -n 'walledCatalogWriteVerdict(' 1418de4c7e -- packages finds callers in cloud-connection, rest and runtime only. This is the walled-data-door sentence the card looked for, found here rather than in the docs.
    • PROSPECTIVE packages/plugins/plugin-security/src/builtin-positions.ts:92 (the served guest description, "Bindings face the strictest checks…") and packages/core/src/security/resolve-authz-context.ts:1099-1100 ("A set bound to guest passed the strictest anchor tier where it was bound"). Both hold only once a bind door exists (ADR-0138 D3 item 4).
    • FALSE docs/design/permission-model.md:306-310: the authoring sample names both keys and CRUD on sys_position_permission_set.
    • Governed (Tier H): docs/adr/0086-…:446 ("authorEnvironmentSets keeps package-managed rows publish-only"); ADR-0090 D12 :521-522, not amended for ADR-0131.
    • objectui: packages/app-shell/src/views/metadata-admin/i18n.ts:2467-2468, help "(assign users, manage bindings)", and zh :5754.
    • Release-owned, not touched by a code PR: content/docs/releases/v13.mdx:91.

    Reading 5: what each outcome would touch

    RETIRE (both keys, tombstone route under spec-property-retirement)

    spec (about 12 files, generated ones included)

    • security/permission.zod.ts:691-694: two retiredKey() tombstones with prescriptions. JSDoc :643-652, :696-700, :946-948.
    • contracts/security-service.ts:92-93: drop both fields from DelegableAdminScope.
    • security/permission.form.ts:62: the helpText drops both names.
    • security/permission.test.ts:24-25: becomes tombstone pins (the prescription and the parse refusal), plus a tree-scoped absence pin with its CROSS_PACKAGE_TEST_INPUTS radius.
    • ADR-0087:
      • one conversion ending in -removed in conversions/registry.ts MAJOR_18_CONVERSIONS (spec is 18.0.0-next.1), stripping permissions[].adminScope.{manageBindings,authorEnvironmentSets}, with a fixture carrying 2 notices;
      • migrations/entries/retired-keys/18.security__AdminScope__manageBindings.ts and …__authorEnvironmentSets.ts, spelled security/AdminScope:manageBindings / security/AdminScope:authorEnvironmentSets as authorable-surface/security.json:19,23 spells them;
      • one D3 semantic entry that states the containment change in (i) and (ii);
      • a STEP18_RATIONALE fragment in registry.ts.template, then gen:migration-registry.
      • Template for the semantic entry: migrations/entries/semantic/18.admin-scope-business-unit-blank-refused.ts.
    • Generated:
      • authorable-surface/security.json:19,23 (the rows turn RETIRED), authorable-defaults/security.json:8,11, the reference page (gen:docs);
      • authorable-surface.base.json:4936,4940 only through gen:authorable-surface-base;
      • the manifest and api-surface should not move (the AdminScope def survives); the build confirms it.
    • liveness/permission.json:36-41: rewrite the evidence. The adminScope row is a leaf and stays live.
    • Changeset for @objectstack/spec and @objectstack/plugin-security: minor, BREAKING, FROM → TO plus a one-line fix, Clause-②: yes (narrowing), and ADR-0087 disposition registered naming the new conversion id.

    plugin-security (about 10 files)

    • delegated-admin-gate.ts:204-222, :411-413, :519-520, :525-526, :879-909, :925-926. Containment then compares includeSubtree, manageAssignments and the allowlist; case (ii) turns into a refusal.
    • objects/sys-user-permission-set.object.ts:30.
    • Tests: delegated-admin-gate.test.ts:27-28, :752-753, :864-865; delegated-admin-gate-catalog.test.ts:33-34; delegated-admin-gate-cross-organization.test.ts:76-77; granted-by-writer-provenance.test.ts:89-90; objects/reserved-identity-names.test.ts:216-217; can-write-object-admission.test.ts:256-257; __tests__/legacy-catalog-objects.testkit.ts:333-336 (text only).
    • Plus one pin for the containment change.

    Other objectstack files

    • platform-objects (4 generated): translations/{en,es-ES,ja-JP,zh-CN}.metadata-forms.generated.ts:2326.
      • Regenerate with node scripts/check-i18n-bundles.mjs --write; pnpm i18n:extract regenerates platform-objects alone.
      • en is rewritten from the form source. The three translated helpText values are hand-written, and merge mode keeps them, so each needs a hand edit.
      • pnpm check:i18n gates the result.
    • dogfood: showcase-permission-zoo.dogfood.test.ts:98.
    • cli: the legacy-row ceremony packages/cli/src/utils/organization-ownership-apply.ts:685 saves sys_permission_set rows through the metadata door via packages/core/src/security/permission-set-row-definition.ts:101-111, carrying admin_scope verbatim.
      • Whether that save replays the conversion is NOT MEASURED. If it does not, the row mapper must drop the retired keys.
    • examples: app-showcase/src/security/permission-sets.ts:384-385, :400-401 (tsc finds them under the tombstone), plus the reading-4 sentences.
    • docs: delegated-administration.mdx:59-60, permission-sets.mdx:338-339, docs/design/permission-model.md:306-310.
    • ADR (Tier H): an ADR-0090 status-line amendment retiring D12's bindings and env-set authoring rights (the definition write is manage_metadata's, per ADR-0131 D3), and the ADR-0086 row :446.

    objectui (same landing plus the .objectui-sha bump, per AGENTS.md Definition of done item 4)

    • PermissionAdvancedFacets.tsx:49, :659-674 (drop the two switches); i18n.ts:2467-2468, :2477-2478, :5754, :5761-5762; PermissionAdvancedFacets.adminScopeAnchor.test.tsx:106-117; objectui docs/adr/0056-…:142.
    • Whether the pinned objectui build reds against the tombstoned type is NOT MEASURED (no build in a reading round). The facet change is owed either way, because the switches would write a value the spec refuses.

    Not touched: skills/** (0 hits), apps/** (0), content/docs/releases/** (release-owned).

    Size. About 30 objectstack files, roughly 12 of them hand-written, and 4-5 objectui files plus the pin. Measured precedents:

    Expect one objectstack PR and one objectui PR, plus the ADR amendment split off as its own Tier H piece.

    Stored rows: can sys_metadata permission-set rows carry the keys? Yes, from source.

    • PermissionSetSchema.adminScope (permission.zod.ts:953) admits both keys.
    • permission items are environment-authorable (packages/objectql/src/security-catalog-namespace.ts:128-139).
    • saveMetaItem stores the submitted body verbatim (permission.zod.ts:669-671).
    • Setup's nested form and Studio's switches both write the keys explicitly, and legacy admin_scope rows reach the door through the ceremony.
    • So a registered strip conversion is owed; rehydration replays it through applyConversionsToStoredItem.
    • How many deployments hold such rows: NOT MEASURED.

    ENFORCE

    • Decision first. A new ADR, or an ADR-0090 D12 amendment (Tier H), has to settle two things:
      • what "within the subtree" means for an environment-wide position definition that has no business unit;
      • what bounds a delegate-authored set body.
      • Without that second bound, authorEnvironmentSets plus an allowlisted set the delegate may self-assign is a self-escalation path: edit the set's body, then assign it to yourself.
      • The ADR would apply to single only, because ADR-0131 D12 items 7-8 forbid this under a wall.
    • Door. meta-write-capability.ts:88-102 admits manage_metadata only. Enforcing means admitting a non-manage_metadata delegate for position and permission items, which makes the verdict type- and item-aware:
      • on both transports (rest-server.ts:3739-3745, :7115; runtime/src/domains/meta.ts:911-916);
      • in the read exemption that mirrors it (rest-server.ts:3503 mayWriteItem);
      • in the publish, rollback and reset twins, and in the clone door.
    • Judge. A new plugin-security judge reached from the metadata door; DelegatedAdminGate governs data-door objects only.
      • For position: diff the old and new permissionSets; require every changed set to be allowlisted; apply containment to sets that carry adminScope; refuse anchors and built-ins; and add a blast-radius rule over every holder's sys_user_position.business_unit_id, across organizations.
      • For permission: a body-containment judge (the set grants no more than the delegate's own effective grants) plus the allowlist on the name.
    • Pins.
      • Unit pins for each rule, with controls.
      • Dogfood pins: an allowlisted in-subtree save answers 200; out-of-allowlist, anchor, walled and authoring-escalation saves answer 403.
      • objectui would need a delegate-reachable editor (Studio is manage_metadata-gated).
    • Effort: no precedent. At least 3 PRs across domain:spec, plugin-security, the metadata doors and objectui, plus a Tier H ADR.

    Decision: retire or enforce AdminScope.manageBindings and AdminScope.authorEnvironmentSets

    os-decision-facets

    • ① 项目长远合理性 (long-term soundness): A shrinks the contract to what the model on main carries (assignments, allowlist, containment). B regrows a binding and authoring surface on environment-wide definitions that have no business-unit dimension and are closed under a wall by ADR-0131, which is a new special case.
    • ② 实际业务拉动 (business pull): zero. No production author sets either key true; the control key manageAssignments: true has 3. Cloud and deployments are NOT MEASURED.
    • ③ 防 AI 犯错 (AI-authoring safety): today an AI that writes manageBindings: true through Setup, Studio or source gets a right that is silently never granted, and that quietly loosens strict containment. A turns each such write into a tsc error and a parse-time prescription.
    • ④ 创业阶段不扩散 (startup focus): removing beats declaring and maintaining. B adds a gate nobody named. Retire immediately; no named external user justifies a staged window.

    Prior rulings read: manageBindings, authorEnvironmentSets over docs/adr, AGENTS.md and .claude on 1418de4c7e → 1 hit (ADR-0086 :446). ADR-0090 D12 states the two rights in prose (:521-522). ADR-0049, ADR-0131 D3 and D12 items 7-8, and ADR-0138 D3 items 4-5 also govern. Thread: 6170064364 (triage: retire on zero pull).

    The problem in one sentence. Setup and Studio let an administrator tick "Manage bindings" and "Author env sets" on a delegate's scope, and the platform never grants either right, while the ticked box still changes which scopes that delegate may hand out.

    Option What it does What a customer sees Effort
    A: retire both keys now tombstones, a registered strip conversion, the containment axes dropped, D12 amended, the two Studio switches and help removed, false text corrected a delegate's scope offers what it actually grants: placement and assignment inside a subtree from an allowlist. A source that writes either key fails tsc and parse with the fix. A stored row is stripped on load. measured precedent: a two-key retirement, 40 files, +1096 / −187 (#22990); 1 objectstack PR + 1 objectui PR + a Tier H ADR line
    B: enforce both at the definition write widens the metadata door to delegates for position and permission, adds a new binding judge and a set-body containment judge, single only a subsidiary admin could edit what a company-wide position grants and author sets, with new refusals to learn, and nothing under a wall no precedent; 3 or more PRs across 4 surfaces plus a new ADR

    What each option means for the business.

    • A: a branch manager may staff the branch's people into company-defined jobs. This is how Salesforce Delegated Administration (assign allowlisted permission sets to users in a role subtree) and Microsoft Entra administrative units (scoped role assignment; role definitions stay tenant-level) model it. Those references come from product knowledge, not from an in-repo measurement.
    • B: a branch manager may also redefine what a company-wide job title grants everywhere.

    Two years out. Delegated administration on ObjectStack is assignment-scoped (subtree, allowlist, containment), and definitions are authored centrally per environment. That is the mainstream shape named above and the shape ADR-0131 D3 already gives the catalog.

    Recommendation: A.

    • Fallback: B, only if a named deployment shows a delegate who must recompose positions, and then with the ADR first.
    • Confidence gaps (NOT MEASURED): cloud and deployment stored rows; whether the pinned objectui build reds; whether the legacy-row ceremony replays conversions.
    • Self-check: 只看①选 A;②③④ 是否翻转:否.

    After the ruling (if A).

    • One building dispatch on this card (domain:spec): one objectstack PR covering spec, plugin-security, the containment pin, docs, showcase, the reading-4 text and the changeset.
    • One objectui PR covering the facet, i18n and test, landed with the pin bump.
    • The ADR-0090 and ADR-0086 lines go as a separate Tier H docs PR.
    • Position 2: D9 needs no new carrier (ADR-0138 D3 item 5 and the pinned absence). D12's unjudged rights retire, so the card's "marker per key retired" is the two RETIRED_KEYS_BY_MAJOR entries under the registered conversion.
    • Position 3 rides the same PR as docs accuracy with no contract change, including the non-key sentences (showcase guest text, meta-write-capability.ts:184-185, the tour doc).

    Generated by Claude Code

  4. objectstack-fleet commented on Oct 14, 2026

    @objectstack-fleet
    ContributorAuthor

    待裁:AdminScope 的两个「委派管理」开关,退役还是补执行 · domain:spec seat 4 · session_0121ooBYRmS8ziFXm9ai5YHm · 2026-10-14T16:06Z

    读数来源:只读轮报告 6172654646(objectstack origin/main 1418de4c7e,objectui main df05784ae4)。本条把它呈为决定;本席不裁。

    一句话问题

    权限集的「委派管理范围」里有两个开关:「可管理绑定」(manageBindings)和「可编写环境级权限集」(authorEnvironmentSets)。Setup 和 Studio 都能把它们打开,但打开之后没有任何地方兑现它们承诺的权限。它们唯一的实际作用是:在一个方向上放松了「不许授出与自己同等范围」这条防自我提权规则。所以要二选一:把这两个开关退役,还是补齐执行。

    Governing text

    • ADR-0049 Decision:安全属性「must be in exactly one of three states」,即 Enforced / experimental / Absent。「parsed, unmarked, unenforced — is prohibited」。两个开关今天处在被禁的第四态。
    • ADR-0090 D12 声明了它们:「manage user↔position assignments and position↔set bindings within that subtree; optionally author environment-owned sets there」。
    • ADR-0131 D3 收回了它们的执行面:C3 退役了绑定行与权限集行,集合成了岗位定义的一部分。D12 第 7–8 条在隔离姿态下禁止这类委派。
    • Prior rulings:工具点名的 ADR-0060 D2、ADR-0124 D3 只是关键词「delegate」撞上,没有一条裁过这两个键(见下方四棱块)。

    协议声明,以及是否改协议

    两个选项都改协议:

    • A 是收窄。两个键按 ADR-0087 退役,并需要一条 registered 剥离转换,因为存量 sys_metadata 权限集行可能带着它们。
    • B 是新增判定。在岗位定义写入门上新加委派范围的裁判。

    前提(各附 re-check 命令)

    1. 没有生产作者把任一键设为 true。re-check:git grep -n -E '(manageBindings|authorEnvironmentSets)"?\s*:\s*true' origin/main -- ':!**/*.test.ts',应为 0。对照:manageAssignments: true 有 3 处生产作者。
    2. 写岗位 permissionSets 或权限集的门(PUT /meta/position|permission/:name),裁判是 manage_metadata 加隔离墙,不跑委派范围检查。re-check:git show origin/main:packages/metadata-core/src/meta-write-capability.ts | sed -n 88,102p。
    3. 两键只被严格包含比较读到(delegated-admin-gate.ts:892-908)。按源码推演(未实跑):一个空转的 true 可以让「与自己同等的范围」通过「同等范围拒绝」。re-check:git show origin/main:packages/plugins/plugin-security/src/delegated-admin-gate.ts | sed -n 879,909p。
    4. 看不见的部分:cloud 仓与各部署的存量行,未实测。

    选项 × 真实代价 × 工作量

    做什么 客户可感知后果 工作量
    A 退役 两键加墓碑。走 ADR-0087 剥离转换。包含比较去掉这两轴。表单、帮助文字和译文同步。Studio 移除两个开关并跟 pin。ADR-0090 D12 一行 Tier H 修订。 委派管理员的界面少两个从不生效的开关。授予范围的包含判断回到只看真实权力。已存的 true 被迁移剥掉,并有说明。 spec 车道 · objectstack 1 PR(约 30 个文件)+ objectui 1 PR(4–5 个文件)+ 1 次 pin hop + 1 行 Tier H。先例 #22990:40 个文件,+1096/−187。
    B 补执行 先立新 ADR,定义「子树内」对一个没有业务单元维度的全环境岗位意味着什么,并限定权限集正文的范围,否则「可编写权限集 + 可自分配白名单」就是一条自我提权路径。再把元数据写入门改成按类型与条目判定(两条传输都改,含发布、回滚、重置、克隆各门),新增两个裁判,并给委派管理员做编辑界面。 委派管理员真能在子树内管绑定、写权限集。只在 single 姿态下成立,隔离姿态按 ADR-0131 禁止。 ADR + 至少 3 个 PR,无先例。

    业务含义直译

    • A:门禁系统里有两个「可开某类门」的勾选项,从来没接到任何门上,把它们从表单上拿掉。
    • B:为了让这两个勾选项生效,先重新设计「分公司管理员能改总部岗位到什么程度」的规则,再给每扇相关的门装读卡器。

    四轴(业务立场)

    • ① 项目长远合理性(权重 ≥50%):A 让合同回到 ADR-0131 D3 的模型,委派只管人员到岗位的分配、白名单和包含关系,特例减少。B 要在一个全环境、无业务单元维度的定义上新开特例,且隔离姿态下 ADR-0131 已把它关掉。两年后的平台样子:参照 Salesforce 的委派管理员:它委派的是「从指定清单里分配档案与权限集」,权限集本身的编写留给全局管理员。
    • ② 实际业务拉动:实测为零。没有生产作者打开过任一键,对照键有 3 个作者。cloud 与部署未测。
    • ③ 防 AI 犯错:今天 AI 通过 Setup、Studio 或源码写 manageBindings: true,得到的是一个静默不兑现的权限,还顺带放松了包含规则,没有任何人看到报错。A 之后同样的写法会得到编译错误和解析期处方,是响亮拒绝。
    • ④ 创业阶段不扩散:移除优于「声明并维护」。没有具名外部用户支撑分阶段窗口,所以立即退役。B 是一个没人点名要的新门禁,新门禁默认否。

    推荐 A,回退 B,置信缺口

    • 只看①选 A;②③④ 是否翻转:否。
    • 回退 B 的条件:出现一个具名部署,真的需要委派管理员重组岗位。那时先立 ADR,再动代码。
    • 置信缺口:cloud 与部署的存量行;objectui 跟 pin 时类型墓碑会不会让构建变红;os migrate organization-ownership 的仪式是否会对旧 admin_scope 行重放转换。

    裁后执行段

    • 裁 A:本卡回 pm:queue,由 spec 车道认领。一个 objectstack PR 做墓碑、剥离转换、包含比较改两轴、表单、译文、测试、文档和展示应用。卡上 Position 3 列出的失真文字随同修正。objectui Studio 的两个开关另开 objectui 卡(消费方首发卡)。ADR-0090 D12 一行走 Tier H,等维护者批准。附带关掉第 3 条前提里「空转开关放松包含规则」的口子。
    • 裁 B:本卡挂 pm:blocked,Blocked-by: 指向新立的 ADR 卡。

    四棱块

    os-decision-facets

    • ① 项目长远合理性:A 缩小特例,与 ADR-0131 D3 的分配模型一致;B 在全环境定义上新增特例。
    • ② 实际业务拉动:零实测作者,对照键有 3 个;cloud 与部署未测。
    • ③ 防 AI 犯错:A 把静默不兑现、且放松包含的开关变成编译期加解析期的响亮拒绝。
    • ④ 创业阶段不扩散:remove 优于 declare-and-maintain;B 是无人点名的新门禁。
      Prior rulings read: managebindings,authorenvironmentsets,adminscope,delegate → 3 hits; ADR-0060 D2, ADR-0124 D3 (keyword matches only, neither rules on these keys); thread: none; repo: objectstack-ai/objectstack

    状态

    Release: session_0121ooBYRmS8ziFXm9ai5YHm releases the reading-only claim 6172320924; that round ended as dispatched, with readings in 6172654646. Destination: needs-user-decision, assignee cleared, out of the active queue. Resume-from: none (no branch was created).

  5. 6 remaining items

  6. objectstack-fleet commented on Oct 15, 2026

    @objectstack-fleet
    ContributorAuthor

    Claim: PM loop round 1 (second shift) · 2026-10-15T01:21Z · letter A of ruling 6181988435
    Session: session_01LFqdRsuM5nkqoJdHd5ztVk
    Account: os-bill (the seat's linked user as GET /user answers it; always the card's assignee)
    Branch: claude/issue-23488-retire-adminscope-binding-keys
    Worktree: objectstack-issue-23488
    Domain: domain:spec
    Seat: domain:spec#2 (seat post #18549)
    File surface (at origin/main cdc42eb49 or later; stop on a breach and explain it in the report):

  7. objectstack-fleet commented on Oct 15, 2026

    @objectstack-fleet
    ContributorAuthor

    os-dev-report
    {
    "issue": 23488,
    "status": "done",
    "branch": "claude/issue-23488-retire-adminscope-binding-keys",
    "pr": "#23601",
    "session": "session_01LFqdRsuM5nkqoJdHd5ztVk — this run's id (subagent = the dispatching PM session's)",
    "premise_still_valid": true,
    "summary": "Ruling 6181988435 letter A executed. Main PR #23601 (draft, assignee os-bill, head 3854d14): two retiredKey tombstones on AdminScopeSchema with the ruled prescription; DelegableAdminScope drops both fields; D2 conversion permission-admin-scope-binding-authoring-removed (MAJOR_18_CONVERSIONS order 65, retiredFromLoadPath, retiredAfter 17.7.0) strips both keys; two RETIRED_KEYS_BY_MAJOR[18] entries; D3 semantic entry permission-admin-scope-binding-authoring-retired stating the containment change, plus its STEP18_RATIONALE fragment; form help text, the four platform-objects metadata-forms bundles, the generated reference and authorable-surface/defaults, and the liveness note follow. plugin-security definitionContainsStrictly drops both axes, and case (ii) is now a 403 PERMISSION_DENIED refusal pinned with a CONTROL; case (i) reversed is pinned too. Ruling item 3 measured: the ceremony's save (permissionSetDefinitionFromRow, then saveMetaItem) does NOT replay the conversion — 422 INVALID_METADATA at adminScope.manageBindings; CONTROL row without the keys saved. So core's row mapper drops the two keys, with a pin. Position 3 text corrected: two docs sentences, the showcase's seven, spec JSDoc, the ledger note, two plugin-security comments, meta-write-capability.ts:184-185, docs/design/permission-model.md. ADR PR #23602 (draft, Tier H, Part of #23488, label skip-changeset, assignee os-bill, head c45ec43): ADR-0090 one Amended status line, ADR-0086 line 446. objectui untouched (objectstack-ai/objectui#12406). Mechanism assumptions: 1 holds (EAST_SCOPE had both flags true on both sides, so the pins are new). 2: PR #23568 landed first, origin/main merged through os-regen-merge.sh, the bundles text-merged clean (they are not os-regen-routed), en regenerated by pnpm i18n:extract, the three translated values edited by hand. 3 does not hold: nothing was written under skills/, so the main PR is not governed. 4 holds: organization-ownership-apply.ts read only, the fix is in core.",
    "tests": "Pre-merge head 0221b16, each under os-verify-lock, exit captured before any pipe: spec test 677 files / 20729 tests passed; spec test:repo 62 files / 1051 passed (run once, before the PM lock-pressure note); spec typecheck 0, including check:test-typecheck, so the two ts-expect-error lines compile; plugin-security typecheck 0; core test 0; platform-objects test 0; plugin-security 7 targeted files, 206 passed. Merged head 3854d14, one lock hold: spec 8 targeted files (retirement pin incl. tree-scoped absence walk, permission.test, removal-version pin, migrate-sentence pin, conversions, migrations, major18-merge, step18-rationale-merge), 612 passed; plugin-security whole suite 185 files / 3693 passed, 45 skipped; core mapper 13 passed; typecheck exit 0 for platform-objects, core, plugin-auth, lint, client, rest, runtime, cli, example-showcase; client return-type-precision 2 passed; rest security-routes + security-explain-envelope 42 passed; runtime permission-set-clone 22 passed; showcase inert-wirings 39 passed; dogfood showcase-permission-zoo 10 passed (dogfood is not in per-PR CI). Reverse verification at 0221b16: (1) the gate file at branch point fa6a608 written over HEAD's — 2 failed (case ii, case i reversed) and 2 passed (CONTROL, existing equal-scope refusal); restored, blob == HEAD and git diff HEAD empty. (2) ablation-replace.mjs removed the withoutRetiredAdminScopeKeys call (anchor 1 to 0, blob 03f32ba6f610 to 01d09ba3e389) — the mapper pin failed (1 failed, 12 passed); restored to the HEAD blob. No dist ablation was needed: both pins import src relatively. Ceremony measurement: a scratch vitest in metadata-protocol at 43c50c8, deleted and never committed. Door outcome: ok false, code INVALID_METADATA, status 422. CONTROL: ok true, success true. objectui pin b9814b334d: a typing reproduction of PermissionAdvancedFacets.tsx (AdminScope Partial alias, setScopeDetail, scope[key], the computed-key cast) plus DelegableAdminScope, compiled against this branch's spec dist with TS 6.0.3 and objectui's strict/bundler options — exit 0. CONTROL: literal manageBindings fails TS2322 and DelegableAdminScope.manageBindings fails TS2339. A full objectui build at the pin is NOT MEASURED.",
    "gates": "Main PR, derived at 3854d14 (merge base 16abea8): node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack gives 118 commands, each run with its exit recorded. 113 exit 0. check:skill-examples first exited 3 (PREREQUISITE: client-react dist missing); after building client-react it exited 0. NOT MEASURED, exit 3: check-plugin-teardown-shape --self-test (its positive control is pinned to a commit this shallow clone cannot reach; it grades the script, not this diff) and check:dual-build-cjs-loads (needs a whole-workspace build). check:platform-checklist exit 1, 8 ABSENT SYMBOL problems in metadata-protocol/protocol.ts and service-storage anchors this diff does not touch. Control: the identical 8 on the ADR tree (main 32e03d2 plus docs/adr only). The gate is deliberately out of per-PR CI (checklist-status.yml). --ran with exit codes: 118 derived, 116 run, 2 NOT-MEASURED derived from exit 3, 0 UNRUN. ADR PR, derived at c45ec43 (merge base 32e03d2): 19 commands. 18 exit 0. lint check:doc-formula-expressions first exited 3 (formula not built), then 0 after building formula and lint. --ran: 19 run, 0 NOT-MEASURED. CI check-runs at read time: #23601 head 3854d14 — 41 runs, 36 success, 5 skipped. Among them: Lint & Repo Gates, TypeScript Type Check, Test Core (6/6 shards), Build Core, Governed Surface Queue Guard, Check Changeset, Spec property liveness and Type Check consumer gates are all success; Console Pin Gate, Dogfood shards and Temporal Conformance are skipped (full battery only). #23602 head c45ec43 — 43 runs, 32 success, 11 skipped, none failing.",
    "line_budget": "main PR #23601: 38 files, +1016 / -112 (1128 changed lines) vs the 3000 human-merge threshold — under. ADR PR #23602: 2 files, +2 / -1. No skills/
    or other governed path in #23601; #23602 is all docs/adr (Tier H).",
    "files_changed": "#23601: .changeset/23488-admin-scope-binding-authoring-retired.md; content/docs/permissions/{delegated-administration,permission-sets}.mdx; content/docs/references/security/permission.mdx (gen:docs); docs/design/permission-model.md; examples/app-showcase/src/security/permission-sets.ts; examples/app-showcase/src/docs/showcase_tour_security.md; packages/core/src/security/permission-set-row-definition{,.test}.ts; packages/metadata-core/src/meta-write-capability.ts (docstring); packages/platform-objects/src/apps/translations/{en,es-ES,ja-JP,zh-CN}.metadata-forms.generated.ts; packages/plugins/plugin-security/src/delegated-admin-gate.ts plus 6 test fixtures and objects/sys-user-permission-set.object.ts (comment); packages/qa/dogfood/test/showcase-permission-zoo.dogfood.test.ts; packages/spec: authorable-defaults/security.json, authorable-surface/security.json, liveness/permission.json, src/contracts/security-service.ts, src/conversions/registry.ts, src/migrations/entries/retired-keys/18.security__AdminScope__{manageBindings,authorEnvironmentSets}.ts, src/migrations/entries/semantic/18.permission-admin-scope-binding-authoring-retired.ts, src/migrations/registry.ts.template, src/security/{permission.zod,permission.form,permission.test,admin-scope-binding-authoring-retirement.test}.ts, src/shared/retired-key-removal-version.pin.test.ts, vitest.repo-tests.json. #23602: docs/adr/0090-permission-model-v2-concept-convergence.md, docs/adr/0086-authz-metadata-config-boundary-and-cross-package-composition.md.",
    "deviations": [
    "File surface beyond the claim's list, each in the PR body with its reason: docs/design/permission-model.md (Reading 5 lists it); showcase_tour_security.md (two of the ruling's seven sentences); the dogfood zoo test (it asserted manageBindings false); the plugin-security test fixtures; retired-key-removal-version.pin.test.ts (its guard requires a JOIN row); vitest.repo-tests.json; the generated authorable and reference files.",
    "Changeset carries @objectstack/core patch and @objectstack/platform-objects patch beside the ruled @objectstack/spec and @objectstack/plugin-security (both minor, BREAKING): both publish changed content (the mapper, the help-text bundles). metadata-core is a docstring change only, so it has no changeset row.",
    "PR body declaration line copied verbatim from the claim (Clause-② yes, no arm). The changeset carries the ruled Clause-② yes (narrowing) with the ADR-0087 registered marker naming both ids. Check Changeset is success.",
    "Declared narrowing: after the PM lock-pressure note I ran no further test:repo locally. Consumer packages got typecheck plus grep-selected tests only: plugin-auth and lint typecheck only, because none of their tests touch adminScope; client, rest and runtime typecheck plus targeted files. Their whole suites are CI's (Test Core 6/6 success).",
    "cli: organization-ownership-apply.test.ts is integration tier (the --project unit filter selected nothing, exit 1, which is not a measurement). The diff touches no cli file, so it is declared to CI (Test Core success).",
    "Not run locally: metadata-core test and typecheck (docstring-only change; I stopped my run sequence before them to relieve lock pressure); showcase objectstack validate. CI TypeScript Type Check and Test Core success.",
    "The PR #23601 body was written once, when the merged-head readings were still in progress, and says so. The final readings are in this report; nothing in the body is false, so no edit is requested.",
    "Run-A queue timeouts (exit 99: plugin-security test, core typecheck, platform-objects typecheck) were re-run in the merged-head batch, all exit 0. The ADR-worktree lint build was re-acquired under its kept slot."
    ],
    "mcp_calls": "0 — no MCP GitHub tool was called",
    "api_writes": "4 relay dispatches, 6 REST requests, as objectstack-fleet[bot] through fleet-write: (1) pr_create #23601 = POST /repos/objectstack-ai/objectstack/pulls (draft) + POST /repos//issues/23601/assignees; (2) pr_create #23602 = POST .../pulls (draft) + POST .../issues/23602/assignees; (3) label-write: POST .../issues/23602/labels (skip-changeset; read back: documentation, size/xs, skip-changeset); (4) this report: POST .../issues/23488/comments. Plus git push to the two branches (not REST). Reads: GET the issue, its comments and the check-runs.",
    "open_questions": [],
    "out_of_scope_findings": [
    "carrier: platform-checklist watchdog (board-snapshot.yml, daily) · noted, not filed — check:platform-checklist reds on main with 8 ABSENT SYMBOL anchors (metadata-protocol/src/protocol.ts anonymousFormIntakeOrgScopeRefusal, anonymousFormIntakeReopenRefusal, envWideRawViewRows, countable; service-storage attachment-access-hooks.ts canEdit; attachments-storage floor 27 of 28). Reproduced identically on main 32e03d2 plus docs only; outside this card's three classes (docs/qa drift). Dedupe words: platform-checklist ABSENT SYMBOL, anonymousFormIntakeReopenRefusal, envWideRawViewRows.",
    "carrier: 承接者:无 · noted in #23601 Acceptance notes, not filed — the plugin-security legacy-catalog testkit (legacy-catalog-objects.testkit.ts:333-336) still lists both keys in the retired sys_permission_set.admin_scope column description, which is accurate for a model of a retired object.",
    "carrier: 承接者:无 · noted in #23601 Acceptance notes, not filed — three PROSPECTIVE sentences describe the guest bind door ADR-0138 D3 item 4 assigns for later (plugin-security builtin-positions.ts:92, core resolve-authz-context.ts:1099-1100, permission-sets.mdx wildcard-ban sentence)."
    ]
    }


    Generated by Claude Code

  8. objectstack-fleet commented on Oct 15, 2026

    @objectstack-fleet
    ContributorAuthor

    ACCEPT → PR #23601 comment 6184549497 · domain:spec seat 2 · session session_01LFqdRsuM5nkqoJdHd5ztVk · 2026-10-15T04:39Z · Tier H half: PR #23602 (seat record 6184515986, review requested from os-zhuang and hotlong); the contract review at CONTRACT_REVIEW_TIER is owed on both before landing.

  9. objectstack-fleet commented on Oct 15, 2026

    @objectstack-fleet
    ContributorAuthor

    Landing record · domain:spec seat 2 · session session_01LFqdRsuM5nkqoJdHd5ztVk · 2026-10-15T05:39Z · claim 6182195274 ends here: pm:dispatched is removed in this act, and the card is closed (completed, by PR #23601's Fixes)

    Probe PR #23601 (main) PR #23602 (Tier H, ADR docs)
    origin/main be48c5ec3 "feat(spec)!: retire AdminScope.manageBindings and authorEnvironmentSets …" dd4ef3850 "docs(adr): ADR-0090 D12 status line and ADR-0086 row …" (merged by os-zhuang)
    merge queue no gh-readonly-queue/main/* ref names it no queue (human merge)
    squash vs reviewed head tree d4a25aa5d equals the clean merge of bc0538a42 (its parent, #23600) with the reviewed head 3854d1403 (ACCEPT 6184549497, contract review PASS on that head). The patch-ids differ only by hunk context, because #23600 landed underneath it. tree equals the clean merge of its parent with the reviewed head

    Carried, not owed here: objectstack-ai/objectui#12406 (the console half) stays pm:blocked until a spec release carries the retirement. It is listed on objectstack-ai/objectui#12334 (6182227766).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

area:accessPermissions that actually hold — RLS/FLS, sharing model, write-path guardsdomain:specpriority:p1High: required for production / M2target:v18

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions