Skip to content

docs(permissions): describe the packaged permission-set lock in "One authoritative store" - #22391

Merged
objectstack-fleet[bot] merged 4 commits into
mainfrom
claude/issue-22379-permission-sets-packaged-lock
Oct 9, 2026
Merged

objectstack-fleet[bot] merged 4 commits into
mainfrom
claude/issue-22379-permission-sets-packaged-lock

Conversation

@objectstack-fleet

@objectstack-fleet objectstack-fleet Bot commented Oct 9, 2026 •

Copy link
Copy Markdown
Contributor

Fixes #22379
Clause-②: no

What changed

Two docs pages: four passages that still said a set shipped in code can be overlaid in the environment. +25 / -16 lines against main.

content/docs/permissions/permission-sets.mdx, "One authoritative store — the record is a projection (ADR-0094)"

  • First bullet. It said a Setup edit of any declared set, packaged sets included, "becomes an environment overlay" that "genuinely takes effect", and that the Studio layered view diffs it and a reset removes it. Since the packaged lock, that edit answers 403 NOT_OVERRIDABLE and stores nothing. The bullet now says:
    • which sets are locked: a set shipped in code, meaning a set an installed package declares (*.permission.ts, a stack's permissionSets) or a platform default such as member_default;
    • what the edit answers (403 NOT_OVERRIDABLE, nothing stored);
    • the remedy the refusal names: clone to a new name, with the Clone action or POST /api/v1/data/sys_permission_set;
    • which sets are still edited in place: one created in this environment, a clone, or one saved into a writable runtime package.
  • Closing sentence. "use an overlay where a packaged set must be narrowed" becomes: clone it, narrow the clone, and bind the clone in its place (restriction is done by not granting). Narrowing the packaged set itself is the package author's change (ADR-0086 two-doors). The position-binding half of the sentence did not change.
  • Third bullet ("Deleting through the data door … resets it"). It holds against main, so it is not edited. Evidence is under premise check 5.

content/docs/permissions/permission-sets.mdx, "Provenance — package vs environment sets (ADR-0086)", :311-312

  • Before: "(two-doors separation, evolved by ADR-0094 — ordinary edits of a packaged set become environment overlays, see below)".
  • After: "(two-doors separation, evolved by ADR-0094 — an edit of a set shipped in code is refused; clone it instead, see below)".
  • Why: its "see below" pointed at the corrected bullet, which now says the opposite.
  • Backed by the same pins as the bullet: permission-set-write-through-package-binding.dogfood.test.ts:167-173 (a data-door edit of showcase_contributor answers 403 NOT_OVERRIDABLE, no row minted), and permission-set-lock-row-provenance.dogfood.test.ts (the shipped set is refused at both doors, while a clone takes an edit). The clone remedy is the clone_permission_set action in packages/plugins/plugin-security/src/objects/sys-permission-set.object.ts:115-160.

content/docs/permissions/administrator-guide.mdx, Step 5 (:162-164) and the FAQ (:194-196)

  • Step 5.
    • Before: "Editing a set that shipped with an app creates an environment overlay — your change wins, survives upgrades, and can be reset back to the vendor baseline; the API name is immutable after creation."
    • After: "A set shipped in code — by an app you installed, or as a platform default such as member_default — can't be edited in the environment (a save answers 403 NOT_OVERRIDABLE): clone it and adjust the clone, which is your own set while upgrades keep reaching the original. Sets created here, clones, and sets saved into a writable runtime package are edited in place. The API name is immutable after creation."
    • The page's order of preference is unchanged: bind an existing set to a position, then clone and adjust, then author a new set.
  • FAQ, "Can I delete the built-in permission sets?"
    • Before: "Shipped sets can be overlaid (Step 5) or simply left unassigned."
    • After: "Shipped sets can't be edited in place either, but they can be cloned and adjusted (Step 5) or simply left unassigned."
  • Backed by:
    • The installed-app half: permission-set-write-through-package-binding.dogfood.test.ts:167-173 and two-doors-permission.dogfood.test.ts 块2 (showcase_contributor answers 403, and no overlay is minted).
    • The platform-default half: two-doors-permission.dogfood.test.ts (last 块2 case) and showcase-permission-projection.dogfood.test.ts §2 pin 403 on a member_default edit with no overlay minted. The status is pinned. The code is read from the producer (refusePackagedBaseOverride, NOT_OVERRIDABLE) and is not pinned.
    • The editable populations: permission-set-lock-row-provenance.dogfood.test.ts shapes 1-3 (runtime-package set, org set, clone).
    • The upgrade sentence: the lock's own userMessage in packaged-permission-set-lock.ts ("The clone is your organization's own permission set, and package upgrades keep reaching the original.").

Not touched: the "Declared ≠ enforced — diagnosing a frozen package set" section. PR #22365 (card #22307) adds a clause there in a hunk starting at :368, and the decision card #22371 may rewrite that section. content/docs/releases/ is not touched either.

Premise checks against main (base 117d34de3f, now merged up to 191543456f)

  1. The two passages were still pre-lock text. At 117d34de3f, permission-sets.mdx:332-339 ("it genuinely takes effect") and :350-352 ("use an overlay where a packaged set must be narrowed") read as the card quotes them. Holds.
  2. The Setup edit of a set a code package ships answers 403 and stores nothing. Holds:
    • Pin: packages/qa/dogfood/test/permission-set-write-through-package-binding.dogfood.test.ts:167-173. PATCH /data/sys_permission_set/:id on showcase_contributor answers { status: 403, code: 'NOT_OVERRIDABLE' }, and the active sys_metadata rows are unchanged.
    • Producer: PackagedPermissionSetLockedError in packages/plugins/plugin-security/src/packaged-permission-set-lock.ts. The data door's insert and update legs throw it (permission-set-projection.ts, createPermissionSetWriteThrough), and so does the metadata door (packaged-permission-set-lock-gate.ts).
  3. Scope: which package-held sets does the lock cover? The caution was right. "A packaged set cannot be edited" over-claims, so the text is written to a narrower group: sets shipped in code.
    • How the lock decides: classifyPackagedPermissionSet reads the engine SchemaRegistry. declaredPackageIdOf skips projection echoes, tenant-authored stored rows (isTenantAuthored, _provenance: 'org'), and _packageId: 'sys_metadata' shadows.
    • Editable shapes, pinned: a set saved into a writable runtime package (permission-set-write-through-package-binding.dogfood.test.ts:152-160, and permission-set-lock-row-provenance.dogfood.test.ts shape 1), an org-created set (shape 2), and a clone (shape 3).
  4. Both remedies exist and work on main. Holds.
    • Clone: the clone_permission_set action (sys-permission-set.object.ts:115-160) POSTs to /api/v1/data/sys_permission_set. It carries every permission facet but deliberately not admin_scope. The lock's userMessage and the producer's regime sentence (packaged-base-regime.ts:162-167) both name it.
    • Bind to a position: sys_position_permission_set rows ("Assigning permission sets" on this page; positions.mdx:7-10). showcase-permission-zoo.dogfood.test.ts:133-143 pins a tenant admin binding the package-shipped showcase_contributor to a position.
    • Binding only adds (union) and cannot narrow anything, so narrowing goes through clone-and-bind-in-its-place.
  5. Third bullet: holds. Measured per kind of set:
    • A set created in this environment is removed: showcase-permission-projection.dogfood.test.ts ("deleting a runtime-only set retires both the definition and the record") and permission-set-projection.test.ts:785.
    • A set shipped in code is not removed, and the row remains: two-doors-permission.dogfood.test.ts 块2 delete case and showcase-permission-projection.dogfood.test.ts §3 both answer 2xx with success: false, and the record stays.
    • "(the customization overlay is dropped)": a pre-lock overlay is removed by deleteMetaItem (the 2026-08-10 maintainer ruling, the mergesOverlayAtRead carve-out in refusePackagedBaseRemoval). Pinned by protocol.legacy-overlay-delete.test.ts and permission-set-projection.test.ts:926. With no overlay present, the delete changes nothing.
    • NOT MEASURED: deleting a set saved into a writable runtime package. No pin covers it. See Acceptance notes.
    • CI's Dogfood Regression Gate concluded success on 117d34de3f. Confirming that each cited file appears in the job logs is NOT MEASURED: the log host answered Forbidden from this container.

Gates

Derived with node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack on head fc3fdb1b69: 43 commands, the same list as round 1. All 43 ran on fc3fdb1b69 and exited 0. Exit codes were captured before any pipe.

  • Prerequisite builds: the spec, lint and client/client-react closures were built under the verify lock (VERDICT command-exit 0) before the run, so the dist-reading gates measured this tree.
  • Reconciliation (--ran with per-command exit codes): "43 derived famil(ies) accounted for — 43 run, 0 NOT-MEASURED (a DERIVED zero …)".
  • Sample verdict lines:
    • check:skill-examples: "262 prose examples type-check across 3 surface(s)".
    • check:doc-anchors: "468 internal #fragment link(s) across 417 source file(s) all resolve to a real heading".
    • check:nul-bytes: "OK (scanned 10373 text file(s) … no raw ASCII control bytes)".
    • check:doc-authoring: clean.
    • check:docs: "225 generated files in sync with packages/spec".
  • origin/main moved to 0ef9029da4 after this round's merge. That commit only retitles packages/spec test files and touches no docs, so it was not merged again; CI's merge ref covers it.
  • Outside the derived list, for CI: the path-scheduled Build Docs and Test Core jobs, and the type-check lanes.

Changeset

None. The diff is docs-only under content/docs/, which no package publishes, so the PR carries the skip-changeset label.

Acceptance notes

Accepted by the seat as notes, not filed:

  • Stale code comments, not docs. These are comment drift with no runtime effect.
  • Unmeasured: deleting a runtime-package set. This is a code reading only, not reproduced.
    • The data door's delete leg calls deleteMetaItem with no package. The repository delete matches any package (sys-metadata-repository.ts, whereFor).
    • readDeclaredBody (permission-set-projection.ts:494) skips _packageId: 'sys_metadata' shadows and projection echoes, but not the tenant-authored (_provenance: 'org') rows the lock's classifier learned to skip.
    • A delete after a list read might therefore re-project instead of retiring. A booted-stack probe would settle it.

Generated by Claude Code

claude added 2 commits October 9, 2026 01:25
…authoritative store"

The section's first bullet still said a Setup edit of a packaged set becomes
an environment overlay that takes effect, and its closing sentence still
named an overlay as the way to narrow a packaged set. Since the packaged
lock, that edit answers 403 NOT_OVERRIDABLE and stores nothing.

The bullet now names the locked population (sets shipped in code: a
package's declarations and the platform defaults), the clone remedy the
refusal itself names, and the sets that stay editable in place (created
here, clones, sets saved into a writable runtime package). The closing
sentence now routes narrowing through clone-and-bind-in-its-place, and
names narrowing the packaged set itself as the package author's change.

Claude-Session: https://claude.ai/code/session_0115N1oNnQS5WqofZ2DzaT3q
Co-authored-by: Claude <noreply@anthropic.com>
claude added 2 commits October 9, 2026 01:46
…th the packaged lock

Two more places still said a shipped permission set can be overlaid in the
environment. The Provenance paragraph's clause pointed "see below" at the
corrected bullet, which now says the opposite; administrator-guide Step 5 and
its FAQ answer told admins to overlay a shipped set.

Both now state the lock with the same population as the corrected bullet: a
set shipped in code (by an installed app, or a platform default) refuses an
environment edit with 403 NOT_OVERRIDABLE, and the remedy is clone and
adjust. Sets created here, clones and writable-runtime-package sets stay
editable in place. Step 5 keeps its order of preference, and the FAQ keeps
"or simply left unassigned".

Claude-Session: https://claude.ai/code/session_0115N1oNnQS5WqofZ2DzaT3q
Co-authored-by: Claude <noreply@anthropic.com>
@objectstack-fleet
objectstack-fleet Bot marked this pull request as ready for review October 9, 2026 02:01
@objectstack-fleet
objectstack-fleet Bot enabled auto-merge October 9, 2026 02:01
@objectstack-fleet
objectstack-fleet Bot added this pull request to the merge queue Oct 9, 2026
Merged via the queue into main with commit 11d119a Oct 9, 2026
41 checks passed
@objectstack-fleet
objectstack-fleet Bot deleted the claude/issue-22379-permission-sets-packaged-lock branch October 9, 2026 02:19
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation size/s skip-changeset PR has no user-facing published change; bypasses the changeset gate

Projects

None yet

2 participants