Uh oh!
There was an error while loading. Please reload this page.
fix(studio): scope Access matrix by { packageId } + slice-merge on save (ADR-0086 P0) - #2222
Merged
Merged
Conversation
The Access pillar embedded the permission matrix at environment scope: it
listed every object in the environment (the "84-object leak"), and Save
overwrote the whole permission set — silently dropping authorization rows
other packages had contributed (ADR-0086 P0, upstream-blocking).
Opened inside a package, the matrix now:
- lists only the objects that package declares
(`list('object', { packageId })`), so a package's Access panel no longer
exposes unrelated objects; and
- saves via slice-merge — it re-reads the record and writes back only this
package's slice, leaving every row contributed by other packages
byte-for-byte intact.
Permission sets remain platform-level (the left rail still lists them all);
only the object matrix and its Save are package-scoped. Behavior is unchanged
when `PermissionMatrixEditPage` is rendered without a `packageId` (full object
list, whole-record save).
The scope/merge logic lives in a pure, unit-tested helper
(`metadata-admin/permission-slice.ts`). A component integration test drives the
real editor through load -> edit -> save -> reopen, asserting both the scoped
object list and byte-for-byte preservation of another package's slice.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QWcZRYnJXN3YXFTbm3PLg5The latest updates on your projects. Learn more about Vercel for GitHub. |
Contributor
✅ Console Performance Budget
📦 Bundle Size Report
Size Limits
|
Follow-up to the coordination update on #2221: framework ADR-0086 P1 (framework#2566) adds a record-level `package_id` / `managed_by` provenance axis to permission sets, and platform defaults (admin_full_access, member_default, …) are environment-owned with no package_id. Those must not appear in a package's Access panel. The Access pillar's left rail now filters permission sets to the ones this package owns (`scopePermissionSetList`). A mid-migration guard keeps the rail populated on backends that predate the P1 provenance seeding: if no set carries a package_id yet, all are shown, so the rail never collapses to empty. Tighten (drop the guard) once the provenance axis is guaranteed live. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QWcZRYnJXN3YXFTbm3PLg5
os-zhuang
marked this pull request as ready for review
July 4, 2026 07:44
4 tasks
Contributor
✅ Console Performance Budget
📦 Bundle Size Report
Size Limits
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for freeto join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes#2221.
Problem
The Access pillar (#2196) embedded the permission matrix at environment scope, producing two real security/safety issues:
Per ADR-0086 (framework #2559) — this PR executes, it does not re-decide.
What changed
Opened inside a package,
PermissionMatrixEditPagenow:{ packageId }—list('object', { packageId }), so the panel lists only the objects that package declares (the package's manifest-declared object set, discovered via the existing server-side?package=filter).mergePermissionSlice, leaving every row contributed by other packages byte-for-byte intact. The post-save refresh re-narrows to the slice too.The Access rail also scopes to permission sets this package owns (
scopePermissionSetList): environment-owned platform defaults (admin_full_access,member_default, …) are hidden once the backend tags sets with a record-levelpackage_id(framework ADR-0086 P1, framework#2566). A mid-migration guard keeps the rail populated on backends that predate the P1 provenance seeding — if no set carries apackage_idyet, all are shown, so the rail never collapses to empty. (Follows the coordination update on #2221.)When
PermissionMatrixEditPageis rendered without apackageId(e.g. the generic metadata-resource editor), behavior is unchanged: full object list, whole-record save.The scope/merge logic is a pure, dependency-free helper —
packages/app-shell/src/views/metadata-admin/permission-slice.ts(scopePermissionSet,mergePermissionSlice,scopePermissionSetList).Acceptance criteria
package_id) are hidden from a package's rail once provenance is tagged.type-check0 errors; existing tests do not regress (516 app-shell tests green).Testing
permission-slice.test.ts— unit tests for object scoping, slice-merge (other-package rows survive as the same reference, both directions), and rail scoping incl. the mid-migration guard.PermissionMatrixEditor.scope.test.tsx— component integration test that drives the real editor through load → edit (clear a package-A object) → save → reopen against a fake client that behaves like the server, asserting: only package A's objects render (b_ordernever appears), and the saved payload preserves package B'sobjects/fieldsrows verbatim.🤖 Generated with Claude Code
https://claude.ai/code/session_01QWcZRYnJXN3YXFTbm3PLg5