Role permission templates: 4 roles + capability toggles; remove guest & apprentice subsystems - #144
Merged
important-new merged 21 commits intoJun 14, 2026
Conversation
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
… become inspectors) Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…roles.ts Add no-restricted-syntax lint rule flagging bare RBAC role string literals (owner/admin/manager/inspector/agent) outside the source-of-truth. The :not() selector excludes requireRole() args (already typed as Role[]). Comprehensive exemption block covers existing code where matches are either type-safe (Drizzle column enum, TypeScript Role type) or non-RBAC (OpenAPI tags/scopes, contact types, signer roles, presence roles). Fix 4 files with genuinely unguarded RBAC comparisons to use new ROLE.* constants (ROLE.OWNER, ROLE.ADMIN, ROLE.INSPECTOR, ROLE.AGENT) exported from roles.ts alongside ROLES + Role: - server/lib/rbac/can-edit.ts — role === 'owner'|'admin'|'agent'|'inspector' - server/lib/report-section-numbering.ts — new Set(['owner','admin','inspector']) - server/api/availability.ts — ['admin','owner'].includes(userRole) x4 - server/api/agent.ts — userRole === 'admin' Add tests/unit/role-enum-drift.spec.ts: verifies that users.role and tenant_invites.role drizzle column enums match ROLES exactly (both pass). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…e/contacts gates Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
… the capability override is effective
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…o member Add an "Advanced permissions" disclosure to the invite modal with the four toggleable capabilities (Publish reports, Schedule for others, Financial data, Manage contacts), defaulting to the selected role's template. The chosen diffs flow invite -> accept -> users.permission_overrides: - tenant_invites gains a nullable permission_overrides JSON column (mirrors users); migration 0003_invite_permission_overrides (single ADD COLUMN). - InviteMemberSchema accepts an optional sparse capability map. - TeamService.createInvite stores only toggles that differ from the role template (null when nothing differs); AuthService.joinTeam replays the stored overrides onto the new users row. - Modal exports computeOverrideDiff/CAP_LABELS for direct unit testing. - Remove the dead `role === "lead"` account-role check in TeamBanner. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…renumber migrations 0002/0003 after living_the_fury - Integrate InspectorHub#143 (DB-16 report cover photo + concierge retirement, deployed to prod) - Renumber permission_overrides (0002) + role-value remap (0003) after main's 0001_living_the_fury - Resolve inspection-patch-settings test: DB-16 POOL_KEY behavior + admin->manager rename - 0002 hand-written as additive ADD COLUMN only (no D1-unsafe users table-recreate)
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 free
to 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.
Summary
Collapses the role taxonomy to 4 canonical roles —
owner / manager / inspector / agent— each a fixed permission template, and adds a small capability layer so the long tail is handled by a few per-user toggles instead of a sprawling role set.server/lib/auth/roles.ts):ROLES+Roletype;requireRole(...roles: Role[])is typed, so role changes are compiler-guided. Legacyinspector→leadalias removed.adminrenamed tomanager; the deadlead/specialist/apprentice/officevalues are gone from the type, the invite enum, and the drizzle column enums.getCapabilities(role, overrides)+requireCapability(...)on 4 gates — publish reports, schedule-for-others, financial data, manage contacts — backed by a nullableusers.permission_overridesJSON column. Owner is pinned all-on, agent all-off; inspector defaults are overridable per user.expires_at/assigned_section_ids/mentor_idare DEAD-marked, not dropped (D1 cannot drop FK-referenced columns).publishcapability (former apprentices getpublish=falsevia the data migration).docs/migrating-roles.md).ROLES.Large/commercial jobs continue to use the existing multi-inspector assignment axis (the per-inspection
['lead','helper']enum) — untouched by this change, as is the commercialofficeproperty subtype.Migrations
0001— addusers.permission_overrides0002— data remap of legacy role values (admin→manager,office→manager,lead/specialist/apprentice→inspector; former apprentices keep review-on-publish). Idempotent/defensive (no-op when absent).0003— addtenant_invites.permission_overridesApply to remote D1 at deploy time per the migration SOP (export backup + time-travel bookmark first).
Test plan
type-check0 ·test:unit1651 pass ·test:web370 pass ·test:workers41 pass ·lint0guest_invites/apprentice_reviewstables are noted (D1 cannot drop them).🤖 Generated with Claude Code