[T01] Tenant Admin — Tenant-Wide Administration and Operational Records

Summary

The tenant-admin surface — everything a tenant administrator does that spans the whole organisation rather than one event or one membership period. Two kinds of work live here, and conflating them was the original mistake:

  • Configuration — who may do what, which modules are on, branding, billing, the audit trail.

  • Tenant-wide operational records — master data, and the cross-cutting record screens (people, orders, financial recon, reports) that belong to no single event or period.

T01 owns the administration screens. It does not own the cross-cutting record screens themselves: those are ordinary tenant-scope screens that a tenant admin reaches from the sidebar’s EVENTS / MEMBERSHIPS / INSIGHTS groups, gated by capability like any other row. T01’s boundary is "the screens that exist because you administer the organisation", not "every screen an admin can see".

Re-scoped 2026-08-12. Both the Claude Design canvas and C01 rev 1–5 treated Tenant admin as configuration only (users, roles, modules, branding, billing, audit) — a "Tenant admin workspace" whose nav carried nothing operational. That left master data with no home and pushed cross-cutting operational records into the Events workspace, which is a large part of why the navigation needed rev 6. Master data moves in; the cross-cutting record screens are named here but owned elsewhere.

Actor & Context

Actors:

  • Tenant admin — primary. Holds the tenant-admin capabilities at this organisation (tenant.users, tenant.masterdata, tenant.settings, tenant.audit), sourced from OrgPermission.role via Feature #534.

  • Finance — reaches billing and financial recon; does not hold tenant.users.

  • Super admin — sees T01 for whichever organisation is active, in addition to the platform-scope equivalents in S01. The two are different screens over different data; see T01 versus S01.

Frequency: episodic. Onboarding a new staff member, opening a season, correcting master data, or answering an audit question — not daily.

Precondition: authenticated; an organisation is active (/o/:orgId/…); the user holds at least one tenant.* capability, otherwise the TENANT ADMIN sidebar group does not render.

Entry point: sidebar ▸ TENANT ADMIN group. Rows: Users & roles, Master data, Modules · Branding · Billing, Audit log.

Permissions do not cascade from a parent organisation. Administering a child organisation requires an explicit grant on that child; holding tenant-admin on the parent grants nothing below it. The tenant admin of a parent sees T01 for the parent only. See C01 § Organisation hierarchy.

Scope

Area Owner Note

Users & roles

T01

Staff list for this organisation, role assignment, invitations, deactivation.

Permissions / capability view

T01

Read-only projection of what each role can do at this organisation. The role → capability mapping itself is Feature #534.

Master data

T01

Tenant-scope reference data — the lists that events and memberships draw on. Scope settled 2026-08-13: fourteen collections in In scope — organisation-owned today; ten blocked on schema remediation in Blocked — schema remediation required first.

Modules

T01

Which of EVENT / MEMBERSHIP are enabled (Organisation.enabledModules). Load-bearing for navigation: it decides whether the EVENTS / MEMBERSHIPS sidebar groups render at all.

Branding

T01

Organisation display name, logo, accent — as consumed by portal chrome and outbound communication.

Billing

T01

Plan, invoices, usage. Placeholder until a billing model exists.

Audit log

T01

Tenant-scope audit trail — who changed what, in this organisation.

People (master person list)

tenant-scope screen

Reached from sidebar ▸ EVENTS ▸ People. Lists Person; not EventParticipant. Gated on people.view, not on a tenant-admin capability.

Orders (cross-entity)

C06 + tenant-scope list

Dual-use screen (C01 § Dual-use screens).

Financial recon

tenant-scope screen

Dual-use. Named here because it is tenant-wide operational work; owned by its own use case when authored.

Reports

tenant-scope screen

Dual-use.

Number & tag inventory

T02 / T03 / T09 / T08

Sidebar OPERATIONS group. Tenant-scope, but operationally distinct — its own use-case family.

Master Data

The screen that had no home before C01 rev 6. Tenant-scope reference data that events and membership periods draw on, edited in one place rather than per-event.

Scope settled 2026-08-13 by an audit of all 88 entities in the database module, tracing each reference-style entity’s path to Organisation. The governing rule:

A collection belongs to the scope that owns its rows in the schema, not the scope where editing it is convenient. A collection with no path to Organisation — and which is not a physical or ISO constant — is a schema gap, not a platform-scoped collection. Fix the schema before building a screen for it.

In scope — organisation-owned today

These reach Organisation already, directly or transitively, and need no schema work. This is what the master-data screen ships on.

Collection Path to Organisation

Category

direct FK

Discipline

direct FK

CustomList / CustomListValue

CustomList direct FK; values inherit

EmailTemplate

direct FK

NumberType

direct FK

MembershipType

direct FK

Series

direct FK

RegistrationSystem

direct FK

RaceType

Discipline → org

CourseType

Discipline → org

RoundType

RaceTypeDiscipline → org

RaceTypeConfig

RaceTypeDiscipline → org

Leaderboard / LeaderboardCategory

Series → org

ColumnMappingTemplate

RegistrationSystem → org

Excluded — instance-scoped, not master data

These look like reference data but are owned by an event, so they must not appear on this screen:

  • CourseEvent. Its tenant-level counterpart is CourseType.

  • EventCategoryEvent + Category — the per-event override of a tenant Category (the cascading-inheritance pattern; see design-journal/2026-04/event-category-cascading-inheritance.adoc).

  • Race, Round, StartGroup, ProgramEntry — event structure.

The Category/EventCategory and CourseType/Course pairs are the model for anything new: tenant-level template, instance-level instantiation.

Blocked — schema remediation required first

Conceptually organisation-owned, but carrying no path to Organisation today. Classified 2026-08-13; each joins In scope — organisation-owned today once its scoping lands.

Until then these appear on neither T01 nor S01. Exposing them at platform scope would be a cross-tenant leak in the UI even though the rows are technically shared today. No screens are to be built for this table’s contents yet.

Collection Target scoping Note

Product

FK → RegistrationSystem (org-scoped) ⇒ transitively org-scoped

Not a direct organisation_id. Precedent exists: ColumnMappingTemplate already scopes this way. external_id is a WooCommerce product and each organisation runs its own WooCommerce, so the rows are already de-facto per-organisation.

ProcessDefinition (+ ProcessStep, ProcessStepOption)

Organisation-scoped

Note the direction of existing linkage: Series references ProcessDefinition, not the reverse. Live registration flows depend on these definitions, so the change is additive in schema but not low-risk in behaviour — sequence it with care.

TagType

Organisation-scoped, mirroring NumberType

The asymmetry is the evidence: NumberType carries a direct FK and TagType does not, while T02 / T03 / T08 / T09 treat number and tag stock as a pair.

GlAccount

Organisation-scoped — a chart of accounts per organisation

Same asymmetry: GlTransaction is org-scoped, GlAccount is not. Requires a complete financial review; the scoping decision is settled but the model is not.

BankingDetails

Organisation-scoped

Unambiguously per-organisation. Part of the same financial review.

AssociationType

Relevance review pending

Carries number-allocation policy (number_Format, number_Min/Max, pre_Assign, self-parentId), which reads as organisation policy — but whether the entity is still required by the design is unconfirmed. Review before scoping.

ResultMetaType

Relevance review pending

name, capture, width. Possibly platform-scoped, possibly obsolete.

Track

Relevance review pending

31-line value class holding distance. Track-cycling reference; possibly obsolete.

Package

Undefined

A 5-line stub. Decide when it becomes real.

Two entities resolve for free once the above lands: MembershipCriteriaProduct and PaymentProcessorGlAccount both become transitively organisation-owned with no change of their own.

Once Product and ProcessDefinition are organisation-scoped, a referential-consistency rule follows: Series (org-scoped) references both, so a Series in one organisation must not reference a Product or ProcessDefinition in another. That validation does not exist today and cannot be added before the FKs are.

Relationship to S01

S01's global master data turns out to be nearly empty. After the classification above, Country is the only confirmed platform-wide collection; Track and ResultMetaType are under relevance review and may not survive.

This puts the S01 Global master data nav row in question. C01 rev 6 forbids shipping permanently-dead nav, so if Country alone is the platform-wide reference set, the row should be folded into another S01 screen rather than given its own. Decide when S01 is authored.

Main Flow

  1. Tenant admin opens the sidebar TENANT ADMIN group and picks a row.

  2. The screen renders at tenant scope — no entity header, no tab strip; the sidebar row highlights.

  3. All reads and writes carry the active organisationId from the URL segment, validated server-side against the principal’s linked organisations.

  4. The admin completes the task. Changes that affect navigation (module toggles, role changes touching the current user) re-render the sidebar without a full reload.

Alternative Flows

  • AF-1 — Invite a user. Admin invites by email, assigns a role at this organisation. The invitee’s OrgPermission is created on acceptance. Note that this grants access to this organisation only.

  • AF-2 — Change a role. Role change takes effect on the target user’s next token mint. Changing your own role is either blocked or requires confirmation — an admin must not be able to silently remove their own tenant.users capability and lock the organisation out. Last-admin protection: the final holder of tenant.users cannot be demoted or deactivated.

  • AF-3 — Disable a module. Turning off MEMBERSHIP hides the MEMBERSHIPS sidebar group. Existing membership data is retained, not deleted; the toggle is a visibility and licensing control, and the confirmation dialog must say so.

  • AF-4 — Edit master data in use. Editing a reference value already referenced by events or participants must warn and show the reference count rather than silently repointing history.

  • AF-5 — Insufficient capability. A user without any tenant.* capability never sees the group. Deep-linking to /o/:orgId/admin/…​ renders a "not available" state and admin-service returns 403.

Acceptance Criteria

  • The TENANT ADMIN group renders only when the user holds at least one tenant.* capability at the active organisation; individual rows render per their own capability.

  • Every T01 screen operates on the active organisation from the URL segment, and an organisationId outside the principal’s linked set is rejected 403/404 server-side.

  • A tenant admin at a parent organisation sees no T01 access to a child organisation they have not been explicitly granted.

  • Toggling a module updates the sidebar groups without a page reload, and does not delete data.

  • The last holder of tenant.users at an organisation cannot be demoted, deactivated, or self-demoted.

  • Master data edits show the in-use reference count before saving a change to a referenced value.

  • The audit log records actor, action, target, before/after and timestamp for every T01 mutation, and is filterable by actor and date range.

  • Role changes are reflected for the affected user on their next token mint, and the UI states that latency rather than implying immediacy.

API Surface

Call Purpose

GET /api/org-permissions/current-user (admin-service)

The caller’s organisations, roles and enabledModules (US #538). Also the source for the C01 switcher.

`GET

POST

PUT

DELETE /api/org-permissions` (admin-service)

Staff list and role assignment for the active organisation. Existing resource; needs organisation-scoped list + capability enforcement.

`GET

PUT /api/organisations/{id}` (admin-service)

Branding, enabledModules, organisation settings. OrganisationResource exists.

Master-data endpoints

Per collection, over the fourteen in In scope — organisation-owned today. Most already have JHipster-generated resources (CategoryResource, DisciplineResource, CustomListResource, …); the work is organisation-scoped list filtering plus capability enforcement, not new endpoints. Nothing is needed for Blocked — schema remediation required first until those collections are scoped.

Audit endpoints

Not yet designed. Candidate: reuse the Async Signal & Sweep event stream (Feature #403) rather than a bespoke audit table.

Billing endpoints

None. Placeholder screen.

All calls carry the active organisationId; see C01 § Organisation claims for the selector contract and the opaque-token boundary.

T01 versus S01

Same verbs, different scope, different data — and they must not be built as one parameterised screen, because the blast radius differs by an order of magnitude.

Concern T01 (tenant) S01 (platform)

Users

Staff at this organisation

All users across all organisations

Roles

Assign existing roles

Define role templates

Modules

Toggle for this organisation

Global module availability

Master data

Organisation-owned collections

Platform-wide collections

Audit

This organisation’s trail

Cross-organisation trail

Reached via

Sidebar TENANT ADMIN group

Sidebar PLATFORM group, ALL TENANTS pill

Out of Scope

  • The role → capability mapping itself (Feature #534).

  • Cross-cutting operational record screens — People, Orders, financial recon, reports. Named in Scope for completeness; each is an ordinary tenant-scope screen with its own use case.

  • Number/tag inventory (T02 / T03 / T08 / T09).

  • Anything cross-organisation (S01).

  • Notification preferences — C01 § Notifications open follow-up.

Open Questions

  1. Relevance review — AssociationType, ResultMetaType, Track. Are these still required by the design and functional requirements? Each is unscoped and possibly obsolete; scoping them is pointless if they are being retired. Blocks the last three rows of Blocked — schema remediation required first.

  2. Financial model review. GlAccount and BankingDetails are agreed organisation-scoped, but the wider financial model — chart of accounts per organisation, its relationship to GlTransaction / Recon / financial recon — needs a dedicated review before either is surfaced.

  3. Product scoping route. Agreed: FK → RegistrationSystem. Confirm whether existing product rows can be attributed to a registration system without ambiguity, or whether the backfill needs a manual pass.

  4. S01 Global master data row. With Country possibly the only platform-wide collection, does the row survive as its own screen? See Master Data § Relationship to S01.

  5. Audit-log source. Bespoke audit table, or a projection over the Async Signal & Sweep event stream (Feature #403)? Affects whether T01’s audit screen can ship before that framework.

  6. Role granularity in the UI. Feature #534 defines five roles. Does T01 assign roles only, or also per-capability overrides? Roles-only is simpler and is the assumption here until contradicted.

  7. Billing. No billing model exists. Confirm the row stays a placeholder rather than being dropped from the group until it is real — a permanently-dead row contradicts the C01 rule against shipping dead nav.

  8. Branding reach. Does branding affect only portal chrome, or also outbound email and the public registration portal? The latter crosses into registration-portal territory.

Notes for Implementation

  • T01 screens live under features/admin/ and route under /o/:orgId/admin/…​.

  • Nothing in T01 is instance-scoped — no entity header, no tab strip.

  • Module toggles must invalidate the cached session/permission payload; a stale enabledModules silently shows or hides a whole sidebar group.

  • Treat every mutation as auditable from day one, even before the audit screen exists — retrofitting an audit trail over untracked writes is not possible.

Change History

Date Change

2026-08-13

Master-data scope settled, from an audit of all 88 database-module entities tracing each reference-style entity’s path to Organisation. Fourteen collections are organisation-owned today and form the screen’s scope (In scope — organisation-owned today); Course / EventCategory / Race / Round / StartGroup / ProgramEntry are excluded as instance-scoped; ten collections carry no path to Organisation and are blocked pending schema remediation (Blocked — schema remediation required first) — Product (FK → RegistrationSystem), ProcessDefinition, TagType (mirroring NumberType), GlAccount + BankingDetails (org-scoped, pending a full financial review), with AssociationType / ResultMetaType / Track held for a relevance review and Package undefined. Recorded the governing rule (schema ownership decides scope; an unscoped non-constant collection is a schema gap, not a platform collection), the two entities that resolve transitively for free (MembershipCriteria, PaymentProcessor), and the cross-organisation referential-consistency rule that Series will need once Product and ProcessDefinition are scoped. Surfaced that S01’s Global master data row may not justify a screen — Country may be the only platform-wide collection. Status in-designhandoff-ready: the split that blocked promotion is resolved; no screens are to be built for the remediation table’s contents yet.

2026-08-12

Authored. Re-scoped from the design’s configuration-only "Tenant admin workspace" to cover tenant-wide operational records as well as configuration — master data moves in; cross-cutting record screens (People, Orders, financial recon, reports) are named but owned elsewhere. Absorbed the C01 rev 6 model: sidebar TENANT ADMIN group rather than a workspace, capability gating rather than ROLE_ADMIN, organisation from the URL segment, no permission cascade from parent organisations. Added the T01/S01 boundary table. Status design-todoin-design — the split of master data against S01 is the blocker for handoff-ready.