diff --git a/docs/adr/0086-authz-metadata-config-boundary-and-cross-package-composition.md b/docs/adr/0086-authz-metadata-config-boundary-and-cross-package-composition.md
new file mode 100644
index 0000000000..4737128c8a
--- /dev/null
+++ b/docs/adr/0086-authz-metadata-config-boundary-and-cross-package-composition.md
@@ -0,0 +1,354 @@
+# ADR-0086: Authorization metadata↔config boundary + cross-package composition — DEFINITION travels, ASSIGNMENT stays, packages ship their own sets
+
+**Status**: Proposed (2026-07-04)
+**Deciders**: ObjectStack Protocol Architects
+**Builds on**: [ADR-0066](./0066-unified-authorization-model.md) (three-way separation: Capability / Assignment / Requirement — this ADR operationalizes its **D5** for the packaging axis), [ADR-0056](./0056-permission-model-landing-verification.md) (whole-model enforce/prove; **D7** app-declared default profile), [ADR-0057](./0057-erp-authorization-core-business-units-and-scope-depth.md) (`readScope`/`writeScope` depth), [ADR-0005](./0005-metadata-customization-overlay.md) (per-org overlay = the subtract/adjust layer), [ADR-0028](./0028-metadata-naming-and-namespace-isolation.md) / [ADR-0048](./0048-cross-package-metadata-collision.md) (namespaced api names = collision-proof keys), [ADR-0078](./0078-no-silently-inert-metadata.md) (no declarable-but-never-seeded metadata), [ADR-0016](./0016-studio-package-authoring-and-publish.md) / [ADR-0033](./0033-ai-assisted-metadata-authoring.md) (draft/publish), [ADR-0084](./0084-application-builder-information-architecture.md) (Access pillar = matrix; defers composition), [ADR-0025](./0025-plugin-package-distribution.md) (manifest `permissions` = install-consent scopes, **not** RBAC grants), [ADR-0003](./0003-package-as-first-class-citizen.md) / [ADR-0070](./0070-package-first-authoring.md) (package-first), [ADR-0006](./0006-project-environment-split.v4.md) (environment split)
+**Consumers**: `@objectstack/spec` (`security/permission.zod.ts`, `stack.zod.ts`, `system/metadata-persistence.zod.ts`), `@objectstack/plugin-security` (`bootstrap-declared-*`, `permission-evaluator`), `@objectstack/core` (`resolve-authz-context`), `@objectstack/metadata` (overlay), `../objectui` (`AccessPillar`, `PermissionMatrixEditor`, `metadata-admin`)
+**Surfaced by**: [objectui#2196](https://github.com/objectstack-ai/objectui/issues/2196) — the Studio **Access** pillar, opened inside a *package* design surface (`/studio/:packageId/access`), lists **all** of the environment's objects ("84 objects") and edits **environment-level** permission sets live, with no package scope and no composition story. Same class of bug the Interfaces pillar just fixed ([objectui#2197](https://github.com/objectstack-ai/objectui/issues/2197)).
+
+---
+
+## TL;DR
+
+The platform has never drawn a **model-wide** line between the authorization primitives
+that are **package metadata** (versioned, shipped with a package, draft/published, portable
+across environments) and those that are **environment / system-admin configuration** (edited
+live, environment-specific, bound to real users/orgs) — and it has no story for **how grants
+from several installed packages compose** into one user's effective access.
+
+This ADR draws that line and fixes it to the near-universal mainstream invariant, already
+half-named by ADR-0066: **authorization DEFINITIONS travel with the app (metadata);
+SUBJECT bindings and environment-specific values stay as environment config/data** — with an
+overlay/subtract layer (ADR-0005; Salesforce *muting*; Dataverse layering) so a subscriber
+environment can adjust a packaged definition **without forking it**.
+
+Three decisions follow:
+
+1. **Classification (Q1/Q2).** Every primitive lands as **metadata**, **config**, or
+ **hybrid** on the DEFINITION↔ASSIGNMENT axis. The boundary is **already expressible** with
+ the existing `metadata-persistence.managedBy: 'package' | 'platform' | 'user'` provenance
+ axis on each permission-set record — `package` ⇒ metadata (versioned, draft/publish);
+ `platform`/`user` ⇒ config (env-admin, live). We populate and gate on it; we do **not**
+ invent a third axis (Prime Directive #8).
+2. **Composition (Q3/Q4).** A package ships **its own** permission sets (Salesforce
+ managed-package shape — **Shape B**), `managedBy:'package'` + owning `packageId`, seeded on
+ install by a new **`bootstrapDeclaredPermissions`** (the missing sibling of
+ `bootstrapDeclaredRoles`) that finally migrates the already-declarable
+ `stack.permissions` into `sys_permission_set`. Runtime composes by the **existing
+ most-permissive UNION** across a user's assigned sets, conflict-free because object api
+ names are package-namespaced. Cross-package assembly into one *shared* set (**Shape A**) is
+ retained strictly as an **environment-admin** convenience, never something a package writes.
+3. **Governance & two doors (Q5/Q6).** Because a package's own access is **metadata**, the
+ **package** Access pillar edits only that package's object slice and flows through
+ **draft/publish** (like Data/Interface). The **environment-admin** door keeps the
+ cross-package all-objects matrix **and** the assignment UI, edited **live** (config). Two
+ doors, one metadata.
+
+This is a design/ADR; the code lands in three independently-shippable phases (P0 objectui
+scope-fix, P1 framework `bootstrapDeclaredPermissions`, P2 two-door + overlay).
+
+---
+
+## Context
+
+### The trigger, and why it is not a one-off
+
+The Access pillar renders `` with **zero props**, so it never receives the
+route's `packageId` (`objectui StudioDesignSurface.tsx:589`). Its matrix loads **all** objects
+via `client.list('object')` with no filter (`PermissionMatrixEditor.tsx:171`) — the "84
+objects" — and **saves the whole permission-set record**, including its full `objects` dict
+(`:289`). Every other pillar already scopes by `client.list('object', { packageId })`
+(Interfaces `:822`, Data `:1377`); Access simply never got the same treatment. So the surface
+is doing three wrong things at once: it edits an **environment-level** thing from a **package**
+door, with **no package scope**, and **no composition model** to say what "the package's
+access" even means. The last one is the actual gap — the UI can't be scoped correctly until
+the model says which permission bits belong to the package and which to the environment.
+
+### What is already true (grounded, verified — not re-derived)
+
+- **Permission sets are environment-level today, with no owning-package field.**
+ `sys_permission_set` is `managedBy: 'config'` on the object-affordance axis
+ (`plugin-security/objects/sys-permission-set.object.ts:20`); `PermissionSetSchema`
+ (`spec/security/permission.zod.ts:108`) has **no `packageId`**. Grants live **inline as flat
+ dicts** on the record: `objects: Record` (`:120`) and
+ `fields: Record<"object.field", FieldPermission>` (`:122`). **One record holds every
+ package's grants.**
+- **Object api names are package-namespaced and collision-proof** (`crm_account`, `todo_task`);
+ the prefixed name is the physical table name, so two packages cannot collide
+ (`manifest.zod.ts:142-179`). ⭐ *This is what makes cross-package composition conflict-free at
+ the key level* — a package's grants are exactly the `objects._*` keys.
+- **Runtime resolution is a most-permissive UNION** across a user's assigned sets
+ (`permission-evaluator.ts:64-101`, `'*'` wildcard fallback `:40`); assignment resolution
+ user→roles→sets→union in `core/security/resolve-authz-context.ts:84-252`.
+- **No package-shipped access exists.** `bootstrapDeclaredRoles` seeds a package manifest's
+ `roles` into `sys_role` (`bootstrap-declared-roles.ts:61`), but there is **no
+ `bootstrapDeclaredPermissions`**. `stack.permissions: PermissionSetSchema[]`
+ (`stack.zod.ts:235`, "Permission Sets and Profiles") is **declarable but never migrated**
+ into `sys_permission_set`. The manifest's `permissions` field (`manifest.zod.ts:264`) is
+ **plugin install-consent scopes** (ADR-0025), *not* RBAC grants. → **a package cannot ship
+ default access for its own objects, and the metadata it *can* declare is silently inert** —
+ a live ADR-0078 violation. (ADR-0066 **D5** named this "gap to close"; this ADR closes it.)
+- **The vocabulary for the boundary already exists** — two `managedBy` axes:
+ provenance/layer `managedBy: 'package' | 'platform' | 'user'`
+ (`metadata-persistence.zod.ts:72`) and the object CRUD-affordance bucket
+ `'platform' | 'config' | 'system' | 'append-only' | 'better-auth'` (`object.zod.ts:408`).
+ We build on the **provenance** axis, not a parallel one.
+
+### Governing precedent (do not re-litigate)
+
+ADR-0066 already split authorization into **Capability** (definition), **Assignment** (who
+holds it — runtime records), **Requirement** (resource→capability contract), with the rule *"a
+resource declares the capability it requires (a stable contract); the capability and its
+assignment are dynamic, admin-maintained records."* This ADR does **not** re-open that split;
+it applies it to the **packaging & composition** dimension (which side of the metadata↔config
+line each primitive sits on, and how packages assemble) that 0066 left as D5.
+
+---
+
+## The mainstream invariant (confirmed)
+
+Every established metadata-driven / low-code platform draws the *same* line, and it is exactly
+the DEFINITION↔ASSIGNMENT split:
+
+| Platform | DEFINITION = packaged metadata | ASSIGNMENT / env value = data | Subtract/adjust without forking |
+|---|---|---|---|
+| **Salesforce** | Permission sets, permission-set **groups**, (legacy) profiles = Metadata API components; travel in managed/unmanaged packages. FLS + object CRUD ride the permission set. | User↔permission-set assignment = **org data, never packaged**. | **Muting** permission sets — a subscriber subtracts from a managed set without editing it. |
+| **ServiceNow** | Scoped apps + update sets carry **ACLs and roles** (`sys_scope`). | `sys_user_has_role` (user↔role) = **instance data**. | Scope isolation + overrides. |
+| **Power Platform / Dataverse** | **Solutions** carry **security roles** as metadata. | Team/user role assignment = data; env-specific values externalized via **environment variables** + **connection references** (never baked into packaged metadata). | Managed/unmanaged **solution layering**. |
+| **OutSystems / Mendix** | Module/app **roles** deploy with the app. | User→role mapping = runtime/admin. | Per-env config. |
+
+**Invariant to adopt:** *definitions travel with the app; subject bindings and
+environment-specific values stay as environment config/data; an overlay/subtract mechanism lets
+a subscriber adjust packaged definitions without forking them.* ObjectStack already has all
+three ingredients (namespaced definitions, runtime RBAC records, ADR-0005 overlay) — they were
+just never wired for authorization packaging.
+
+---
+
+## Decision
+
+### D1 — The classification (Q1) — one axis: DEFINITION (metadata) ↔ ASSIGNMENT (config)
+
+Each primitive lands as **metadata** (travels with the package; versioned; draft/publish),
+**config** (environment-specific; live; binds real subjects), or **hybrid** (a metadata *shape*
+that references a concrete subject — split it: shape = metadata, binding = config). The class is
+recorded on the record's **`managedBy` provenance** (`package` ⇒ metadata; `platform`/`user` ⇒
+config), not a new field.
+
+| Primitive | Class | Rationale (one line) | Precedent |
+|---|---|---|---|
+| **Role** (definition) | **metadata** | A named capability bundle the app ships. | SF role · SN role in scope · Dataverse role in solution · OutSystems module role |
+| **Role hierarchy `parent`** | **hybrid** | The *shape* (A reports to B) ships; when nodes bind a tenant's real org-unit tree that binding is config. | SF role hierarchy (metadata) vs the org's actual users (data) |
+| **PermissionSet / Profile** (definition) | **metadata** | The grant bundle is packageable content. | SF permission set / profile = Metadata API components |
+| **ObjectPermission** grant | **metadata** | Part of the set definition — **for the package's own objects only**. | SF object CRUD rides the permission set |
+| **FieldPermission (FLS)** | **metadata** | Rides the set; own fields only. | SF FLS on the permission set |
+| **`systemPermissions` / capabilities** | **metadata** | Capability *definitions* (ADR-0066 D1 registry). | SF Custom Permissions |
+| **`tabPermissions`** | **metadata** | UI-surface grant riding the set. | SF tab visibility on profile/set |
+| **RLS policy** (`using`/`check`) | **metadata** | A behavioural predicate shipped with the object. | SF sharing / Odoo record rules deploy with the app |
+| **Sharing rule** | **hybrid** | Rule shape = metadata; recipients naming a real team/queue = config. | SF sharing rule (metadata) recipients resolve to org data |
+| **OWD / `object.sharingModel`** | **metadata** (package default) → env may **tighten** via overlay | The object ships a default posture; the environment can only make it *stricter*. | Dataverse org-wide defaults; SF OWD (env tightens) |
+| **Package secure-default posture** (`access.default:'private'`, ADR-0066 D2) | **metadata** | A data-model posture on the object, ships with it. | SF "new object = no access until granted" |
+| **Permission-set / role ASSIGNMENT** (`sys_user_permission_set`, `sys_role_permission_set`) | **config / data** | Binds a **real** user/team — the universal line. | SF assignment = org data, never packaged · SN `sys_user_has_role` = instance data |
+| **Organization / tenancy** | **config / data** | Real tenants of the environment. | every platform |
+| **Actual users / teams** | **data** | Runtime subjects. | every platform |
+
+### D2 — The DEFINITION↔ASSIGNMENT principle (Q2), stated
+
+> **Authorization *definitions* are metadata; *subject bindings* are environment data.**
+> A permission set / role / RLS policy *defines* what some abstract holder may do — it ships,
+> versions, and publishes with the package. *Which real user or team holds it* is an
+> environment record an admin maintains at runtime. A definition **never bakes "who"**; it only
+> declares "what a holder can do".
+
+**Exceptions are the `hybrid` rows, and they are resolved by *splitting*, not by moving the
+whole primitive to config:** a sharing rule's *shape* is metadata but its *recipient that names
+a real team* is config; a role hierarchy's *structure* is metadata but its *binding to the
+tenant's org-unit tree* is config. This is exactly the split ADR-0066 already made for
+capabilities (definition = metadata, assignment = admin record). No primitive is *wholly*
+hybrid — each decomposes into a metadata shape + a config binding.
+
+### D3 — A permission set carries its owning package (`packageId` + `managedBy` provenance) [spec]
+
+Add to `PermissionSetSchema` (and persist on `sys_permission_set`):
+
+- `packageId?: string` — the owning package for a package-shipped set (absent ⇒ env-authored).
+- Populate the **provenance** `managedBy: 'package' | 'platform' | 'user'` on every
+ permission-set *record* (distinct from the table's object-affordance `managedBy:'config'`,
+ which stays — the table still *accepts* admin rows; the per-record provenance says whether a
+ given row is package metadata or env config).
+
+This is the single spec change that makes the boundary **machine-checkable**: a record with
+`managedBy:'package'` is versioned metadata (draft/publish, upgrade-owned, read-mostly for
+admins per ADR-0010); `platform`/`user` is env config (live). It also makes package **uninstall**
+well-defined (remove the package's own sets) — impossible today when one shared record holds
+everyone's grants.
+
+### D4 — Composition shape (Q3): package-owned sets (Shape B); shared slices stay env-only
+
+**Chosen: Shape B — a package ships its own permission sets** (`crm_sales_rep`,
+`todo_member`), namespaced, `managedBy:'package'` + `packageId`. The environment admin assigns
+one or more to a role/user; the **runtime unions** them (existing most-permissive semantics),
+conflict-free because each set only grants `objects._*` keys.
+
+**Shape A** — one shared "Contributor" set into which packages write disjoint object slices —
+is **retained only as an environment-admin construct** (the admin's own `managedBy:'platform'`
+set, into which they may hand-pick grants across packages). **A package never writes into a
+shared/foreign record.** Rejecting package-writes-shared-slice avoids: the objectui **data-loss
+trap** (naively saving "the rendered rows" deletes other packages' grants — issue Q3.A),
+coupling a package upgrade to a mutable shared row, and an undefined uninstall. Salesforce
+managed packages are Shape B for exactly these reasons; ADR-0005 overlay + Salesforce-style
+**muting** is how a subscriber *adjusts* a packaged set without forking it (the subtract layer),
+so Shape B loses no flexibility.
+
+Runtime composition semantics (unchanged, now stated as the contract): **grants UNION
+most-permissively across all of a user's assigned sets; keys never collide because object api
+names are package-namespaced; the flattened per-user effective grant is served at
+`GET /api/v1/auth/me/permissions` (`objectui MePermissionsProvider.tsx:56`).** Precedence with
+AND-gates (`private` posture, `requiredPermissions`) and RLS OR/AND is per ADR-0066's
+*Precedence / combination semantics* — this ADR does not change it.
+
+### D5 — Packages ship working default access (Q4): `bootstrapDeclaredPermissions` [bootstrap]
+
+Installing a package **should** grant working default access to **its own** objects.
+Implement `bootstrapDeclaredPermissions` — the exact sibling of `bootstrapDeclaredRoles`
+(`plugin-security/security-plugin.ts:719`) — that migrates `stack.permissions`
+(`stack.zod.ts:235`) into `sys_permission_set` as `managedBy:'package'` + `packageId`,
+**idempotently** and **upgrade-aware** (re-seed the package's own slice on version bump; never
+clobber env-authored `platform`/`user` sets). This:
+
+- **closes the ADR-0078 inert-metadata violation** (a declarable `permissions[]` that is never
+ seeded is precisely the smell 0078 prohibits);
+- seeds a **package-owned set** (Shape B / D4), not a slice into a shared set;
+- respects the **least-privilege floor** — the package chooses its default grants; the platform
+ `member_default` remains the floor (ADR-0056 D7 default-profile is the env-level counterpart:
+ packages ship *their* defaults, the environment declares *its* default profile).
+
+The manifest `permissions` install-consent field (ADR-0025) is **untouched and unrelated** —
+consent scopes are not RBAC grants; do not conflate them.
+
+### D6 — Governance (Q5): the package Access door is draft/published metadata
+
+Because a package's own access is **metadata** (D1/D3), editing it in the **package** Access
+pillar flows through **package draft/publish** (ADR-0016/0033), exactly like Data and
+Interfaces — not "saves the ACTIVE item directly". The #2196 banner ("permissions are
+platform-level … saves the active item directly") is **correct for the environment-admin door**
+and **wrong for the package door**; the fix is to *relocate* the live-save behaviour to the
+env-admin door, and put the package door under draft/publish.
+
+### D7 — Two doors, one metadata (Q6)
+
+| Door | Scope | Edits | Governance |
+|---|---|---|---|
+| **Package** Access pillar (`/studio/:packageId/access`) | **this package's own object slice** (`objects._*`, own fields, own sets) | package permission-set **definitions** (metadata) | **draft → publish** (D6) |
+| **Environment admin** (`metadata-admin`) | **cross-package** all-objects matrix **+** assignment (`sys_user_permission_set`, `sys_role_permission_set`) | env config: shared sets (Shape A), overlay/mute of packaged sets, subject bindings | **live** (config) |
+
+This is builder-ui §3.5 "two doors, one metadata": the package door is the scoped,
+version-controlled authoring view; the env-admin door is the cross-cutting, live operational
+view. The same underlying `sys_permission_set` / assignment records are reached from both, but
+each door only writes what it owns.
+
+---
+
+## Change list (phased — each phase independently shippable)
+
+### P0 — objectui, low-risk (closes the leak + the data-loss trap) — mirrors objectui#2197
+- Pass the route `packageId` into `` (`StudioDesignSurface.tsx:589`).
+- Scope the matrix rows to `client.list('object', { packageId })` (`PermissionMatrixEditor.tsx:171`)
+ and fields likewise (`:208`) — render **only this package's object rows**.
+- **Slice-merge on save** (`:289`): persist `{ ...original.objects, ...editedPackageKeys }` so
+ editing the package's slice **never deletes other packages' grants** from the shared record.
+ The namespaced keys make the package's slice unambiguous.
+- Keep the left rail global (permission sets are still env-level under the current model).
+- *Ships against today's shared-record model; no framework change required.*
+
+### P1 — framework (packages ship default access) — per D3/D4/D5
+- `PermissionSetSchema.packageId` + per-record provenance `managedBy` (D3); migration for
+ `sys_permission_set`.
+- `bootstrapDeclaredPermissions` seeding `stack.permissions` → `sys_permission_set`
+ (`managedBy:'package'`, idempotent, upgrade-aware) (D5). Closes ADR-0078 for this surface.
+- Add a **liveness / conformance** row (ADR-0056 D10 matrix) asserting `stack.permissions` is
+ now *seeded and enforced*, so it can never silently regress to inert again.
+
+### P2 — the two doors + subtract layer — per D6/D7
+- Environment-admin surface owns the cross-package all-objects matrix **and** the assignment UI
+ (config, live).
+- Package Access door edits only the package slice, under **draft/publish**.
+- Overlay/subtract: an environment adjusts a packaged set via ADR-0005 overlay
+ (Salesforce-muting-style subtract), never by forking — the precedence-step-4 explicit-deny
+ layer ADR-0066 deferred; gated on proven need.
+
+---
+
+## Consequences
+
+**Positive.**
+- One stated, mainstream-aligned line for the *whole* authz model (metadata vs config), encoded
+ on an **existing** axis (`managedBy` provenance) — no parallel vocabulary (PD #8).
+- Packages become **self-contained and upgrade/uninstall-safe**: a package ships, versions, and
+ removes its own access; no shared mutable record entangles two packages.
+- The objectui object-leak **and** the silent data-loss trap both close (P0), independent of the
+ deeper model work.
+- ADR-0078 inert-metadata violation on `stack.permissions` is closed (P1); ADR-0066 D5 is
+ operationalized rather than left aspirational.
+- Cross-package composition is **conflict-free by construction** (namespacing + union), and now
+ *specified* rather than incidental.
+
+**Negative / cost.**
+- Spec change (`packageId` + provenance) + a `sys_permission_set` migration; must backfill
+ existing rows (hardcoded platform defaults → `managedBy:'platform'`).
+- The package Access door gains a draft/publish lifecycle it did not have — more UI surface, but
+ consistent with every other pillar.
+- Shape B means "package default access" is a *new* set to assign, not automatic membership; the
+ environment still decides who gets it (correct, but one more admin step than a shared slice).
+
+**Neutral / open.**
+- Whether OWD/`sharingModel` env-tightening rides the same overlay path as view overlays
+ (ADR-0005 whitelist currently marks `security` types **not** org-overridable) — resolved in
+ the P2 overlay/subtract PR, not here.
+- Permission-set **groups** (ADR-0066 ⑦) as the bundling primitive over Shape-B sets — deferred,
+ evidence-gated.
+
+## Non-goals
+
+- **Not** re-opening ADR-0066's Capability/Assignment/Requirement split — this ADR sits on top
+ of it.
+- **Not** changing runtime enforcement, RLS compilation, or precedence (ADR-0056/0066 own those).
+- **Not** designing permission-set groups or delegated admin (deferred).
+- **Not** touching the manifest install-consent `permissions` field (ADR-0025) — orthogonal.
+- **Not** making `security` metadata org-overridable by default (ADR-0005 keeps it global; the
+ subtract layer is an explicit, gated P2 mechanism).
+
+## Alternatives considered
+
+- **(A) Shape A — packages write disjoint slices into one shared "Contributor" set.** Rejected as
+ the *packaging* primitive: it is the data-loss trap (issue Q3.A), couples package upgrade to a
+ shared mutable row, has no clean uninstall, and requires every editor forever to honour the
+ slice invariant. Kept only as an env-admin convenience where the admin owns the record.
+- **(B) Leave `stack.permissions` inert, seed nothing.** Rejected — a live ADR-0078 violation and
+ the reason a package cannot ship working access; ADR-0066 D5 already called it a gap.
+- **(C) A new third `managedBy`-style axis for "authz metadata vs config".** Rejected (PD #8) — the
+ `metadata-persistence.managedBy` provenance axis already expresses exactly this; reuse it.
+- **(D, chosen) Classify on the existing provenance axis + Shape-B packaging + two doors +
+ overlay subtract**, delivered as P0/P1/P2.
+
+## References
+
+- ADR-0066 (unified authz — three-way separation, D5), ADR-0056 (landing verification — D7, D10
+ conformance matrix), ADR-0057 (scope depth), ADR-0005 (overlay = subtract layer), ADR-0028 /
+ ADR-0048 (namespace isolation), ADR-0078 (no inert metadata), ADR-0016 / ADR-0033
+ (draft/publish), ADR-0084 + `docs/design/builder-ui.md` §7 & §3.5 (Access pillar; defers
+ composition), ADR-0025 (manifest consent scopes ≠ grants), ADR-0010 (metadata protection).
+- Framework: `spec/security/permission.zod.ts`, `spec/stack.zod.ts:235`,
+ `spec/system/metadata-persistence.zod.ts:72`, `spec/data/object.zod.ts:408`,
+ `spec/kernel/manifest.zod.ts:142-179,264`, `plugin-security/bootstrap-declared-roles.ts`,
+ `plugin-security/default-permission-sets.ts`, `plugin-security/permission-evaluator.ts`,
+ `core/security/resolve-authz-context.ts`, `dogfood/test/authz-conformance.matrix.ts`.
+- objectui: `StudioDesignSurface.tsx` (`AccessPillar` `:589`, Interfaces `:822`, Data `:1377`),
+ `metadata-admin/PermissionMatrixEditor.tsx` (`:171`, `:208`, `:289`),
+ `permissions/MePermissionsProvider.tsx:56`.
+- Related issues: objectui#2196 (trigger), objectui#2197 (Interfaces scope-fix template),
+ framework#1828 (ADR-0048), #1892/#1893 (aspirational-metadata hygiene), #2377 (ADR-0049
+ enforce-or-remove), #1883 (permission lifecycle ops ungated).
+- **Lifecycle context**: this ADR covers the package-authoring/composition slice of the authz
+ surface; the whole-lifecycle gap map (P0–P3, package dev → production) is tracked in
+ umbrella issue #2561.