Repository navigation
ADR-0090: Permission Model v2 — concept convergence, final naming, AI-authoring safety (tracking) #2696
Description
Activity
Status 2026-07-10 — platform work complete; downstream adoption in flight
Done (all merged): ADR #2695 · P1 #2697 · P2 #2708 · P3 #2711 · P4 #2716 · docs #2717 · objectui D2 badges objectstack-ai/objectui#2354. Checkboxes updated accordingly.
In flight (opened today):
- feat(app-shell,console,types)!: adopt @objectstack/spec 13 — Permission Model v2 (ADR-0090) UI fallout objectui#2369 — objectui adopts
@objectstack/spec^13: fixes theRoleSchemabuild break (objectui#2365),role→positionacross loader/previews/fallback schemas/i18n, profile concept fully removed from the Studio surface, dead OWD alias normalization + fully-public unset warning removed (D1/D4 semantics) - feat!: adopt ObjectStack Permission Model v2 (ADR-0090 / spec 13) hotcrm#438 — hotcrm v2 adoption: recipients
role→position/role_and_subordinates→position(flat),RoleHierarchy→flatpositions:,isProfileremoved, explicitsharingModelauthored on all 15 objects (the D7 posture linter caught them all — working as designed). Verified locally against published 13.0.0: typecheck/validate/build/tests all green. After merge: tagv2.0.0 - ci(release): restore blocking live-hotcrm smoke — v2 adoption shipped #2730 (draft) — restores the blocking live-hotcrm release gate per ci(release): make live-hotcrm downstream smoke advisory during ADR-0090 v2 launch window #2719's restore condition. Merge order: hotcrm#438 → tag v2.0.0 → ci(release): restore blocking live-hotcrm smoke — v2 adoption shipped #2730
Remaining under this issue:
- ObjectUI: OWD external dial (D11
externalSharingModel) in the permission matrix / object settings — the only unshipped objectui item - Follow-up ADRs stay unopened until their work actually starts (owner preference)
Found along the way (spun off): spec's
BookAudienceSchemastill carries a{ profile }arm + "role-gated" comments (D2/D3 leftover the forbidden-word lint missed);ApproverTypehas nopositionoption, so "position X approves" is inexpressible post-D3 (rolethere resolves against better-authsys_member.roletiers) — both flagged as separate tasks.- feat(app-shell,console,types)!: adopt @objectstack/spec 13 — Permission Model v2 (ADR-0090) UI fallout objectui#2369 — objectui adopts
Update: the OWD external dial (D11) and permission-matrix OWD badges shipped as a second commit on objectstack-ai/objectui#2369 (external
externalSharingModelselect with inline wider-than-internal warning; per-rowOWD …/Ext …badges incl. the D1Private (default)unset case). With that, every pure-UI ObjectUI follow-up on this issue is in that one PR.Two adjacent items from the broader v2 UX list are blocked on missing framework surfaces, flagged as separate tasks rather than bolted onto #2369:
- D5
isDefaultinstall-suggestion confirm UI — no pending-suggestion list / confirm-bind API exists (isDefaultis only read at bootstrap as the fallback set) - D6 explain panel — the explain engine is an in-process service only; no REST route for a UI to call
- D5
v2 downstream adoption chain is closed ✅
- feat(app-shell,console,types)!: adopt @objectstack/spec 13 — Permission Model v2 (ADR-0090) UI fallout objectui#2369 merged (
619097ed) — spec ^13.0.0 adoption + all pure-UI follow-ups (position relabel, dead alias/profile removal, external OWD dial, matrix OWD badges); closed objectui#2365 - feat!: adopt ObjectStack Permission Model v2 (ADR-0090 / spec 13) hotcrm#438 merged (
d29c214e) → released v2.0.0 — the adopting release the smoke gate pins to - ci(release): restore blocking live-hotcrm smoke — v2 adoption shipped #2730 merged (
b8ce2da3) —HOTCRM_REF→ v2.0.0,continue-on-errorremoved, honoring ci(release): make live-hotcrm downstream smoke advisory during ADR-0090 v2 launch window #2719's restore condition
First Release run with the gate blocking again is green (29070034679).
Remaining scope on this issue is the two framework-surface gaps flagged above (D5 install-suggestion confirm flow, D6 explain REST + panel) — both in flight as separate sessions.
- feat(app-shell,console,types)!: adopt @objectstack/spec 13 — Permission Model v2 (ADR-0090) UI fallout objectui#2369 merged (
- added a commit that references this issue
on Jul 10, 2026 Closing as delivered. Final state:
- ADR + all four phases merged: docs(adr): ADR-0090 — Permission Model v2: concept convergence, final naming, AI-authoring safety #2695 (ADR), feat(security)!: ADR-0090 P1 breaking wave — position rename, profile removal, secure OWD default, principal/external spec shapes #2697 (P1 breaking wave), feat(security): ADR-0090 P2 — everyone/guest audience anchors, additive baseline, anchor binding gate #2708 (P2 audience anchors), feat(security)!: ADR-0090 P3 — security publish linter (D7), delegated administration (D12), BU assignment anchor #2711 (P3 linter + delegated admin), feat(security): ADR-0090 P4 — explain engine (D6), access-matrix snapshot gate, recalibrated benchmark #2716 (P4 explain engine + matrix gate), docs(permissions): rewrite content/docs/permissions/* to the Permission Model v2 vocabulary (ADR-0090) #2717 (docs alignment)
- D5/D6 completed end-to-end: feat(security): ADR-0090 D5/D9 suggested-audience-binding surface + confirm/dismiss API #2746 (D5 suggested-binding surface + confirm/dismiss API) + objectui#2376 (install prompt + Access banner); feat(rest,plugin-security): REST face for the explain engine — GET/POST /api/v1/security/explain (ADR-0090 D6) #2743 (D6
POST /api/v1/security/explain) + objectui#2375 (explain panel); feat(approvals): add 'position' approver type resolved via sys_user_position (ADR-0090 D3) #2738 ('position' approver type) - ObjectUI follow-ups all shipped in objectui#2369 (spec 13 adoption, position relabel, profile/alias removal, D11 external OWD dial, matrix OWD badges) — published as
@object-ui/*@13.1.0 - Downstream chain closed: hotcrm v2.0.0 adoption release →
HOTCRM_REFbumped + blocking live-hotcrm smoke gate restored (ci(release): restore blocking live-hotcrm smoke — v2 adoption shipped #2730, honoring ci(release): make live-hotcrm downstream smoke advisory during ADR-0090 v2 launch window #2719) → first blocking Release run green; cloud pins bumped (cloud#793); framework console pin at objectui@16e2615f - Showcase/docs: permission zoo feat(showcase,docs): ADR-0090 permission-model zoo + full docs alignment #2739 + fixes fix(security)!: ADR-0090 follow-ups — driver tenant wall, batch-insert marshaling, scoped counts, vocabulary leftovers #2745
The six named follow-up ADRs (grant lifecycle, SoD, scale/reorg hardening, ERP dimensions, ALM promotion, portal identity) move to their own parking-lot issue: #2776 — ADRs to be opened when each work stream actually starts.
- added a commit that references this issue
on Jul 10, 2026 - added a commit that references this issue
on Jul 19, 2026
Tracking issue for ADR-0090 (proposed in #2695; companion reference:
docs/design/permission-model.md).Summary
Converge the permission model ahead of launch: five admin-facing concepts (permission set · position · business unit · sharing/OWD · team), secure defaults, one-step renames with no compatibility aliases (launch window), a typed principal model (humans / AI agents / services / guests / externals), scoped delegated administration, and a machine-enforced authoring-safety story for AI-drafted metadata.
Origin incident: an object created without
sharingModel+ an ordinary C/R/U permission set silently granted org-wide read/write of other users' records (objectstack-ai/objectui#2348 — the Studio OWD control shipped there is the UI half; this ADR fixes the platform half).Decisions (see ADR for full rationale)
private; unset state removed (existing metadata grandfathered by explicit stamping)isProfiledeleted;isDefaultnarrows to an install-time suggestion)sys_role→sys_position,sys_user_role→sys_user_position,sys_role_permission_set→sys_position_permission_set,ctx.roles[]→ctx.positions[],current_user.role→current_user.position; "role" becomes a lint-enforced forbidden word (sole exception: better-authsys_member.role/org_membership_level)read/read_write/fullaliases; authoring rejectseveryoneposition carries default grants (per-request resolution, install-time suggestion prompt, no fallback cliff, high-privilege bits blocked)guestposition joinseveryone; packages suggest audience bindings, never ship shared builtin sets; no admin anchor (superuser wildcard covers new packages)kind: human|agent|service|guest|system,audience: internal|external,onBehalfOf); agents act on the intersection of their grants and their delegator's, under a lint ceiling, with human co-sign for destructive ops; services = seatless least-privilege machine identitiesexternalSharingModel, defaultprivate, validatedexternal ≤ internal; BU depth inapplicable to externalseveryone/guestanchors and security publishes stay tenant-levelPhased delivery (each independently shippable, proofs per ADR-0054)
isProfileremoval, D1 default flip + grandfather stamping, plus spec shapes: ctx principal taxonomy (D10) +externalSharingModel(D11). Proof: full suite + dogfood re-run of the objectui#2348 scenario showing owner isolation with no explicit OWD authoredeveryone+guestbuiltin seeding, install-time suggestion prompts, cliff removal. Proof: package install/uninstall grant-liveness e2e + anonymous-principal e2econtent/docs/permissions/*to matchdocs/design/permission-model.mdrole→positionloader/preview/i18n + spec ^13 adoption, OWD external dial (D11) + permission-matrix OWD badges (objectui#2369); D5 suggested-binding confirm UI (framework#2746 + objectui side) and D6 explain panel (objectui#2375) landed via follow-up sessionsNamed follow-up ADRs (scoped out of 0090, to be opened separately)
Supersedes / amends
ADR-0056 D7 (default-profile fallback →
everyoneposition) · ADR-0057 D5/D7 alias clauses (pre-launch one-step renames). ADR-0057's core (BU tree, scope depth,sys_user_roledecoupling) is untouched and load-bearing.🤖 Generated with Claude Code
https://claude.ai/code/session_012oLzaP8n7A3YKFmgaHWC8H