feat(security)!: ADR-0090 P1 breaking wave — position rename, profile removal, secure OWD default, principal/external spec shapes#2697
Conversation
… removal, secure OWD default, principal/external spec shapes One coordinated pre-launch pass implementing ADR-0090 D1-D4 and the P1 spec shapes of D10/D11 (docs/adr/0090). No compatibility aliases. Renames (D3): sys_role→sys_position (+assignment/binding tables and fields), RoleSchema→PositionSchema (flat, parent removed), ctx.roles→ctx.positions, current_user.roles→current_user.positions (formula validators, RLS docs, CEL examples), stack roles:→positions:, metadata kinds role/profile→position. better-auth boundary untouched (sys_member.role, additionalOrgRoles, org_membership_level). Profile removal (D2): isProfile deleted from PermissionSetSchema and all fixtures; isDefault narrowed to an install-time suggestion; appDefaultProfileName→appDefaultPermissionSetName (isDefault-only). OWD (D1/D4/D11): enum reduced to the canonical four; custom objects with an owner field and no sharingModel now resolve PRIVATE (fail-closed for unrecognised stored values; system objects keep the public fall-through); new optional externalSharingModel field (external ≤ internal, P1 shape). Regression tests pin the objectui#2348 incident shape (unset OWD + C/R/U grant must not read others' records). Principal taxonomy (D10, shape only): principalKind/audience/onBehalfOf on ExecutionContext. Sharing recipients: 'role'→'position' expanded via sys_user_position ∪ the membership transition source; 'role_and_subordinates' removed — 'unit_and_subordinates' expands the BU subtree (ADR-0057 D5 re-homing, replacing the walk over a parent column that never existed). Translations patched in place per generated-file contract; conformance matrix row updated. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012oLzaP8n7A3YKFmgaHWC8H
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
📓 Docs Drift CheckThis PR changes 20 package(s): 117 hand-written doc(s) reference the affected code and may need an implementation-accuracy re-verification:
|
… classification + downstream fixture Liveness: GOVERNED kind role→position; liveness/role.json→position.json (flat, parent dropped); permission ledger rowLevelSecurity.positions; object ledger gains externalSharingModel as 'authorable' with the P1 shape-only rationale and the #2696 pointer. Downstream-contract sharing fixture moves to the position recipient. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012oLzaP8n7A3YKFmgaHWC8H
…tion identifiers Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012oLzaP8n7A3YKFmgaHWC8H
…recipient enum, D1 grandfather stamps — dogfood 39/39 green - app-plugin: stack section registration positions→'position' kind (the seeding gap: declared positions never reached the registry); standalone artifact surface roles→positions; CLI diff/stats keys. - sys_sharing_rule.recipient_type runtime enum role→position (seeding inserted 'position' and was rejected by the stale enum), description updated to the BU-subtree semantics of unit_and_subordinates. - ADR-0090 D1 grandfather stamps: explicit `sharingModel: 'public_read_write'` on showcase/dogfood fixture objects whose isolation is RLS-owned or intentionally public (they carry an auto-injected owner_id, so the new unset⇒private default would owner-filter them). - dogfood expectations updated to v2 semantics (recipient 'position'; appDefaultPermissionSetName is isDefault-only). - service-datasource test: explicit casts for Response.json() unknowns (latent, surfaced by the affected-package typecheck). Full dogfood suite 39 files / 191 tests green; repo-wide typecheck clean. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012oLzaP8n7A3YKFmgaHWC8H
…sition rename; runas/authz test fixtures to v2 field names The spec export surface intentionally changed (RoleSchema→PositionSchema, definePosition, ShareRecipientType 'position', ExecutionContext.positions, principal-taxonomy fields, externalSharingModel) — snapshot regenerated per the gate's documented flow. Test fixtures updated: sys_user_position rows use the 'position' field, sys_position_permission_set uses position_id, automation trigger identity uses positions[]. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012oLzaP8n7A3YKFmgaHWC8H
…e gate Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012oLzaP8n7A3YKFmgaHWC8H
… (v2 vocabulary) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012oLzaP8n7A3YKFmgaHWC8H
…ntics, multi-org benchmark recalibration Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012oLzaP8n7A3YKFmgaHWC8H
…— overlay whitelist guards, option-visibility positions, standalone surface role/profile kinds asserted as retired (reintroduction guards per ADR-0090 D2/D3); position asserted allowOrgOverride:true. customSession projection tests read user.positions. Standalone artifact surface tests use positions[]. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012oLzaP8n7A3YKFmgaHWC8H
…on Model v2 vocabulary (ADR-0090) (#2717) - roles.mdx → positions.mdx: flat positions (岗位), assignment BU anchor, built-in identity positions, everyone/guest audience anchors; the hierarchy narrative moves to business units. - profiles.mdx → migration tombstone: the Profile concept was removed (D2); maps each former use to everyone-anchor bindings, isDefault suggestions, and position-bound sets. - permission-sets.mdx: rewritten as the single capability container — union semantics, access depth (moved here from profiles), capabilities, built-in sets (additive member_default baseline), governed assignment tables, isDefault suggestion, adminScope delegated administration, package provenance. - sharing-rules.mdx: OWD default corrected to fail-closed private (D1 — the page previously documented the pre-v2 fail-open default), canonical four values only, externalSharingModel dial (D11), recipient types position / unit_and_subordinates / team. - authorization.mdx: position vocabulary, D1/D11 in the enforcement chain, delegated-admin gate in anti-escalation, new explain-engine section (D6), D7 linter + access-matrix snapshot added to governance, ADR-0090 in the index. - permissions-matrix.mdx: role-hierarchy section replaced with business-unit hierarchy & positions; isProfile removed from samples; position recipients. - permission-metadata.mdx: isProfile → isDefault/adminScope in the field table; union-semantics section replaces Profile-vs-Set; current_user.positions. - index.mdx: five-concepts overview, v2 implementation-status callout, best practices and example updated. - access-recipes.mdx / field-level-security.mdx: link + heading fixes. Authoritative reference: docs/design/permission-model.md; decision record: ADR-0090 (P1 #2697, P2 #2708, P3 #2711, P4 #2716). Claude-Session: https://claude.ai/code/session_012oLzaP8n7A3YKFmgaHWC8H Co-authored-by: Claude <noreply@anthropic.com>
…90 v2 launch window (#2719) The Release workflow's "Downstream backward-compat smoke (live hotcrm)" gate has been red on every main commit since ADR-0090 P1 (#2697), which removed the sharing recipients `role` / `role_and_subordinates` from @objectstack/spec with no compatibility aliases (docs/adr/0090 D3). The latest published hotcrm tag (v1.2.2) still authors those recipients, so the gate necessarily fails on a break the spec took deliberately, and no adopting hotcrm release exists yet to bump HOTCRM_REF to. Mark the step continue-on-error so it keeps running for signal but no longer blocks the v2 publish. Restore hard-blocking (drop the flag and bump HOTCRM_REF) once hotcrm ships a release on the v2 recipient vocabulary. Claude-Session: https://claude.ai/code/session_016ZGeuMQ4SeuCR4uaiWTUX5 Co-authored-by: Claude <noreply@anthropic.com>
Implements ADR-0090 P1 (docs/adr/0090, accepted in #2695) as one coordinated pre-launch breaking pass — no compatibility aliases (supersedes ADR-0057 D5/D7 alias discipline per the ADR). 158 files, mechanical where possible, semantic where it matters. Tracks #2696.
Renames (D3) — the word "role" leaves the platform
sys_role→sys_position·sys_user_role→sys_user_position(fieldrole→position) ·sys_role_permission_set→sys_position_permission_set(fieldrole_id→position_id)RoleSchema/defineRole→PositionSchema/definePosition— flat: theparentfield is gone (hierarchy lives on the BU tree; the old walk queried a column that never existed, per ADR-0057 D5)ExecutionContext.roles[]→positions[]; EvalUser/CEL contractcurrent_user.roles→current_user.positions(formula ROLE_* validators, RLS docs, all CEL doc examples); RLS policy fieldroles→positions; stack propertyroles:→positions:; metadata kindsrole/profile→position(profile kind retired);SystemObjectName.ROLE→POSITION; ADR-0068 builtin constantsBUILTIN_ROLE_*→BUILTIN_IDENTITY_*(values unchanged:org_adminetc.)sys_member.role,additionalOrgRoles,org_membership_level. The approvals plugin'sroleapprover type also stays — it resolves throughsys_member.roleand is flagged in the ADR as boundary follow-up.Profile removal (D2)
isProfiledeleted fromPermissionSetSchemaand every fixture;isDefaultnarrows to the install-time suggestion semantics;appDefaultProfileName→appDefaultPermissionSetName(isDefault-only, interim wiring until theeveryoneposition lands in P2).Secure OWD default (D1) + vocabulary (D4) + external dial (D11 shape)
read/read_write/fullno longer parse (fixtures updated).effectiveSharingModel: a custom object with an owner field and nosharingModelnow resolvesprivate— the objectui#2348 incident shape ("unset OWD + C/R/U grant = silent org-wide read/write") is pinned by new regression tests, including fail-closed handling of unrecognised stored values. System objects keep their explicit ADR-0066 posture.externalSharingModel(external ≤ internal; default private) lands as spec shape.Principal taxonomy shape (D10)
principalKind/audience/onBehalfOfadded to ExecutionContext — shape only, evaluation semantics phase in with P2+.Sharing recipients
role→position, expanded viasys_user_position∪ the better-auth membership transition source (new flatPositionGraphService);role_and_subordinatesremoved —unit_and_subordinatesnow expands the business-unit subtree (finishes ADR-0057 D5's re-homing honestly).Verification
metadata-coreReDoS timing budget under parallel load).isProfileform-translation blocks removed.Out of scope (per ADR phasing)
P2
everyone/guestanchors · P3 linter/tiered gates/delegated admin · P4 explain/matrix gate · runtime consumption ofexternalSharingModel/onBehalfOf· objectui UI follow-ups.🤖 Generated with Claude Code
https://claude.ai/code/session_012oLzaP8n7A3YKFmgaHWC8H
Generated by Claude Code