Management role and permission adjustments¶
1. Problem distillation¶
- Add an admin-assignable, non-parametric management preset with key
administrative_assistant, stored English labelAdministrative Assistant, and Italian localized labelAssistente Amministrativo. - Give the new role the exact functional union of the current
secretary(Front Office) andhr(HR Manager) presets: the stronger access wins for a scope (WRITE > READ > NONE) and actions are the set union. - Make Director (
principal) read-only on attendance. The recommended complete interpretation downgradesattendance.registerfrom WRITE to READ and removes all three attendance mutation actions:take,manage_communications, andjustify. - Give Curriculum Coordinator READ on student health only
(
students.health). Teacher, Staff, and Referent health remain excluded. - Apply the changed baselines to the global presets and every existing tenant clone, so deployments do not leave stale environments.
Success criteria (observable behavior that proves this works):
- Global and tenant role catalogues contain administrative_assistant; the role
is assignable to Teacher or Staff people without parameters and is localized
as Administrative Assistant / Assistente Amministrativo.
- For every scope, the new role equals max(secretary, hr); for every action,
it equals secretary ∪ hr. It also receives the same school-wide record-policy
reach and service-level behavior as whichever source role supplies a function.
- Director sees attendance data tenant-wide but cannot create/correct register
cells, manage attendance communications, or submit attendance justifications.
- Curriculum Coordinator can read the health block of students already
visible through its existing curriculum/related-student policy, without
gaining health visibility for teachers, staff, or referents.
- The authoritative data migration updates global presets and all existing
tenant clones while leaving custom roles and unrelated preset grants intact.
- The manual db:reconcile-rbac tool recognizes the new preset and converges
every environment to the same code matrix after the deploy seed.
Non-goals (in-scope-shaped things this iteration is explicitly not doing):
- No new permission entity, scope, action, route, DTO, or database model.
- No parameter dimension for Administrative Assistant; it is school-wide like
Front Office and HR.
- No change to Front Office or HR themselves.
- No health access for Curriculum Coordinator outside students.health.
- No change to tenant-authored custom roles.
2. Patterns survey¶
| Analogous module/spec | What we'd borrow | What doesn't fit |
|---|---|---|
prisma/seed/roles.ts + prisma/seed/helpers/expected-preset-grants.ts |
Existing *_WRITE_SCOPE_KEYS, *_EXCLUDED_SCOPE_KEYS, and *_ACTION_KEYS preset definitions plus the independent DB-vs-code drift oracle |
The new role combines two presets; derive its write set and action set by union and its exclusion set by intersection so future HR/Front Office changes cannot silently leave it stale |
prisma/migrations/20260820120000_realign_management_preset_v3_grants_and_labels/migration.sql |
Data-only, key-scoped reconciliation of editable management presets across the global baseline and every tenant clone | This change also adds a new preset; role creation remains in the Tier-1 seed plus backfillMissingPresetRoles, while the migration owns the two existing-role deltas |
src/permissions/role-clone.ts |
Tier-1 backfillMissingPresetRoles already creates any newly authored global preset in every existing tenant, including production |
It is intentionally additive-only for existing editable presets, so it cannot roll out the Director downgrade or Coordinator grant; those need the explicit migration |
docs/superpowers/specs/2026-08-20-role-matrix-v3-preset-realignment-design.md |
Precedent for pairing grant changes with record-policy/service behavior, localized labels, an authoritative migration, tests, and docs | The new role must follow both HR and Front Office hard-coded functional branches rather than changing one existing role's matrix |
src/common/i18n/role-labels.catalog.ts + src/common/i18n/role-descriptions.catalog.ts |
Bilingual product copy for every management preset, guarded against the seed's exact management-role key set | Stored role metadata is English while the requested Italian name belongs in it_IT; both surfaces must be updated together |
src/attendance/attendance-access.service.ts + src/attendance/attendance.policy.ts |
Existing distinction between school-wide READ roles, school-wide WRITE roles, qualified writers, and policy admission | Removing the scope alone is incomplete if mutation actions remain; the baseline should not advertise inert or still-effective mutation grants |
On-axis/off-axis assessment (chapter 16 §§4–5): adding a role, changing existing grants, adding policy branches, and applying a data-only preset reconciliation are all on-axis. The only maintainability wrinkle is that the new role composes two existing functional identities. This design keeps that composition explicit through shared functional-role key constants and derived seed sets rather than scattering an untracked third copy of the two matrices.
3. Architecture mapping¶
| Primitive | Apply? | How | Justify |
|---|---|---|---|
| Tenant scope | yes | Author one tenantId = null global preset, backfill one clone per tenant, and key-scope the data migration to isPreset = true global/tenant roles |
Preset definitions are platform metadata, while effective assignments reference tenant clones |
| Academic-year scope | no data change | Role/grant rows are not year-scoped; existing student and attendance policies continue to apply their current dated/year-aware narrowing | No new record reach is introduced beyond existing policy branches |
| RBAC entity key | existing only | attendance and students; no entity-keys.ts change |
Requested changes map to existing entities |
| Scopes | existing only | Director: attendance.register WRITE→READ. Coordinator: add students.health: READ. Administrative Assistant: derived HR/Front Office union |
No new scope vocabulary is needed |
| Actions | existing only | Administrative Assistant gets HR∪Front Office actions. Recommended Director baseline removes attendance.take, attendance.manage_communications, and attendance.justify |
Read/update remain implicit, but these explicit actions authorize attendance mutations |
| Service base | existing services | No CRUD base changes. Add the new functional role key to existing policy branches, role gates, and service-level semantic checks wherever HR or Front Office currently supplies the corresponding function | Grants alone do not establish record-level or service-level access |
queries.ts shape |
existing only | No new queries. Existing tenant/policy filters remain authoritative | New role is school-wide and non-parametric |
| Error codes | existing only | Existing insufficient-scope/action and policy errors apply | No new failure condition or wire contract |
| DTO conventions | existing only | Role catalogue and permissions DTO shapes already accept arbitrary role keys | No response-shape change |
| File-backed sub-resources | existing | Administrative Assistant follows HR for teacher/staff document functions and Front Office for student/referent document functions | No new file usage or owner type |
| Custom fields | no | All presets continue with no others grants and no custom-field management grant |
Existing platform-wide hidden decision remains intact |
| Profile completeness | no | n/a — role grants do not change completeness fields |
4. Data model plan¶
Schema deltas¶
- None.
Role,RolePermission,RoleActionPermission, assignments, enums, and indexes already represent the requested state.
Migration shape¶
- Additive/destructive/renaming: one data-only migration,
20260828xxxxxx_realign_management_roles. - New-role rollout:
seedGlobalRolePresetsauthorsadministrative_assistant; the existing Tier-1backfillMissingPresetRolescreates it with grants in every existing tenant after migrations in every environment. No fixture tenant creation is needed. - Existing-role backfill: the migration updates
principal'sattendance.registerto READ, deletes the approved attendance actions, and upsertscurriculum_coordinatorstudents.health: READfor every global and tenant preset clone. - The manual reconciliation CLI is updated with the new non-parametric key. It remains an exact-code fallback and post-deploy verifier for all environments.
- Hazards from chapter 12: data-only writes on small RBAC tables; no DDL, table
rewrite, enum change, or domain-table backfill. The migration must use
SET LOCAL row_security = off,is_preset = true, exact role/entity/scope/ action keys, idempotent UPSERT/DELETE operations, and comments documenting the hazard audit. A clean worktree currently contains no uncommitted migration; re-check immediately before authoring or runningprisma migrate dev.
Indexes and uniqueness¶
- None added. Existing uniqueness on
(tenantId, key),(roleId, scopeId), and(roleId, actionId)supports the seed/backfill/upserts.
5. API surface¶
No new routes or DTOs.
| Verb | Path | Decorators | Request DTO | Response DTO |
|---|---|---|---|---|
| GET | /roles, /roles/presets |
existing tenant-admin/platform-admin gates | n/a | existing role catalogue DTO; includes administrative_assistant |
| POST/PATCH/DELETE | /role-assignments |
existing admin-only assignment gates | existing assignment DTOs; role key administrative_assistant, no parameter ids |
existing assignment DTO |
| GET | /permissions |
existing authentication/scope compilation | n/a | existing entity-grouped map with the new role's unioned effective grants |
| existing domain routes | unchanged paths | existing scope/action/policy decorators | unchanged | unchanged; new role is admitted where its HR/Front Office functions require it |
Swagger considerations¶
- No schema change. Update role/attendance descriptions only where they enumerate the management behavior; the role catalogue supplies localized name and description at runtime.
6. RBAC seed plan¶
| Seed file | Delta |
|---|---|
PermissionScope (rbac-catalogue.ts) |
none |
PermissionAction (rbac-catalogue.ts) |
none |
ScopeFieldMapping (rbac-catalogue.ts) |
none |
Role grants (roles.ts) |
Add administrative_assistant as the exact HR/Front Office union; remove attendance.register from PRINCIPAL_WRITE_SCOPE_KEYS; remove the approved attendance actions from PRINCIPAL_ACTION_KEYS; remove students.health from CURRICULUM_COORDINATOR_EXCLUDED_SCOPE_KEYS so it resolves to READ |
expected-preset-grants.ts |
Add administrative_assistant from the same derived constants so DB drift coverage remains bidirectional |
*_SCOPES runtime constant |
none — no scope-field mapping changes |
Runtime authorization deltas:
- Add administrative_assistant to every school-wide policy/read branch that
currently admits HR or Front Office.
- Add it to Front Office functional write branches/checks (students,
enrolment/curriculum selection, departments/grades/rooms, homerooms/subject
groups, attendance, disciplinary notes, school-side assignments, referent
contact editing).
- Add it to HR functional write branches/checks (teacher/staff management and
deletion, personnel documents/health).
- Prefer shared role-key constants for HR_FUNCTION_ROLE_KEYS,
FRONT_OFFICE_FUNCTION_ROLE_KEYS, and their school-wide union so future
changes cannot omit the combined role from one of the two functions.
7. Divergence ledger¶
| Pattern | We diverge by | Reason | Tradeoff accepted |
|---|---|---|---|
| Each management preset has a hand-authored independent matrix | Administrative Assistant derives its scope/action matrix from HR and Front Office | “Functions of Front Office and HR” is a composition invariant; hand-copying creates immediate and future drift risk | A future product request to diverge the assistant must replace the derived sets with explicit rules and update this decision |
| Editable tenant preset clones normally preserve historical customization | The migration authoritatively changes the named Director/Coordinator pairs in all tenant clones | The user explicitly requires no stale tenants across environments | Only the listed scope/action pairs are overwritten; unrelated preset edits and every custom role remain untouched |
| Policies list role branches explicitly | Closely related checks use shared functional-role key constants that include the assistant | The same new key must follow two established functional identities across many modules | Small indirection in exchange for making the union mechanically reviewable |
8. Pushback log¶
| US says | Conflicts with | Proposed instead | Status |
|---|---|---|---|
| “Assistente Amministrativo con funzioni di Front Office e HR” | Scope/action grants alone do not confer record-policy or service-level behavior | Implement the exact grant union and add the role to both functional authorization paths; key administrative_assistant, stored English label plus requested Italian localization |
Proposed — approve with design |
| “Rimuovere write su attendance” for Director | Keeping attendance actions would either advertise inert grants (take/manage_communications require WRITE) or retain a real mutation (justify requires only READ) |
Downgrade the scope to READ and remove all three attendance mutation actions | Open — user confirmation required |
| “Read permission on health for cv coord” | health is four distinct scopes: students, teachers, staff, referents |
Grant only students.health: READ, matching the student-centric coordinator matrix; keep the other three excluded |
Open — user confirmation required |
9. Deferrals¶
- No generalized role-composition engine in the database or UI — one derived code preset is sufficient; revisit only if product introduces additional composite roles.
- No per-tenant customization provenance/baseline version — this rollout is an explicit authoritative exception for named grant pairs, following role-matrix v3; broader three-way merge remains a separate concern.
10. Open questions¶
- Confirm Director becomes fully read-only for attendance: downgrade
attendance.registerand revoketake,manage_communications, andjustify. Resolved by user approval in chat on 2026-08-28. - Confirm “health for Curriculum Coordinator” means only
students.health: READ(not teacher, staff, or referent health). Resolved by user approval in chat on 2026-08-28.
11. Verification plan¶
- Unit specs:
- Extend policy/service specs for students, teachers, staff, referents,
departments/grades, curricula/selection windows, homerooms/subject groups,
attendance, disciplinary notes, and assignments to assert
administrative_assistantfollows the appropriate HR/Front Office branch. - Extend attendance access tests to assert Director qualifies for READ but not WRITE/action slices under the baseline.
- Extend role label/description drift specs with the new bilingual copy.
- Update the reconciliation CLI's role metadata guard with the new key.
- E2E specs:
- Extend
rbac-presets.e2e-spec.tswith the complete assistant union, Director attendance read-only state, and Coordinator student-health READ. - Extend
role-presets.e2e-spec.tsexpected management keys and role-assignment coverage for the new non-parametric preset. - Add/extend a migration E2E that starts with stale global and tenant clones, applies the data migration, and proves exact changed pairs plus preservation of custom roles/unrelated grants.
- Keep
rbac-grants.db-sync.e2e-spec.tsas the bidirectional full-matrix oracle for every global/tenant preset. - Manual verification:
- After deploy/seed, dry-run then apply
npm run db:reconcile-rbac -- ...in dev, stage, and prod; expect 11 global preset keys and one assistant clone per tenant. - Assign Administrative Assistant to a Staff person, switch to that role view, and smoke-test one Front Office write, one HR write, and both health/ attendance restrictions.
- Documentation sync: update
docs/04-rbac.md,docs/15-seeding.md,docs/19-attendance.md, and the RBAC pointers indocs/REFERENCE.md. - Per repository rules, builds, lint, tests, and migrations run only when the user explicitly requests those commands.
12. Sign-off¶
- Approved by: user
- Date: 2026-08-28
- Chat reference: “approved” in chat after confirming the §10 Director attendance and Curriculum Coordinator health interpretations.
Until this section is filled, no implementation code is written. When it is
approved, set frontmatter status: Approved, fill this section, commit the
spec, and add the ordered implementation plan separately under
docs/superpowers/plans/ before implementation.