Plain-English summary
You manage your team inside the admin backoffice now — Settings → Organization → Users (/settings/organization/users), which has a Members tab and a Role groups tab. You invite people, pick which apps they can use, assign role groups (the permission bundles), and (on the edit screen) decide which outlets they can access. Behind the scenes this is still the shared Hubits identity service (identity-api / sso.hubits.io) that all Hubits apps authenticate against — but the day-to-day user admin no longer requires bouncing out to the standalone SSO portal. The role groups a person has determine every permission they get inside Foodops.
When you'd use this
- Hiring a new cashier — invite, assign Counter role, tie to one outlet
- Giving the accountant read-only access to reports — Accounting Officer role
- Making someone a full admin — Administrator (all 250 permissions) or Owner (121)
- Investigating an incident — check the Audit Logs to see who did what
- Plan change — re-scoping roles after upgrading to a tier with more features
How to access
- In-app (canonical, new stack): admin.foodops.io → Settings → Organization → Users (
/settings/organization/users) — Members tab + Role groups tab.[code: foodops-admin-app/apps/admin/src/pages/settings/organization/UsersPage.tsx; components/settings/users/** @ main] - Audit logs: Settings → Audit Log (
/settings/audit-log) in the admin backoffice. - Identity service (backend / IdP):
identity-api/sso.hubits.io(haafai.app.identity, prod branchstaging) — still the auth provider and the source of role/permission definitions.
The standalone
sso.hubits.io/usersBlazor portal still exists (it backs the identity service and is where Hubits-wide identity lives), but FoodOps user admin is now done in-app; the old "the/settings/userspage is just a redirect to SSO" behaviour is retired — it is a real Users page.


Prerequisites
- The organisation must be created (via signup flow) — SSO auto-creates Administrator role for the founding user
- You must be logged in with a role that has "Manage Users" permission (Administrator or Owner by default)
- The user you're inviting needs a valid email address — all Foodops invitations go via email
Walkthrough A — invite a new user
1. Open the Users page → Members
admin.foodops.io → Settings → Organization → Users → Members tab → Invite member.
2. Fill the invitation dialog

The new in-app Invite dialog collects:
| Field | Purpose |
|---|---|
| Full name | Display name shown across Hubits apps (required) |
| Required — invite email sent here; becomes the login | |
| Phone | Optional |
| Role groups | The permission bundle(s) to assign — multi-select checkboxes; leaving none checked invites the user with no permissions |
| Applications | Tick each app they can reach (loaded from the org's available applications) |
| Outlets | Optional multi-select — scope the invite to specific outlets; leave empty to grant access to all outlets |
[code: foodops-admin-app/apps/admin/src/components/settings/users/InviteUserDialog.tsx — payload = {applicationOrganisationId, name, email, phone?, roleGroupIds[], applicationIds[], outletIds[]} @ main]
Note: User Bots and Mobile apps are NOT on the invite dialog (those were legacy SSO-portal fields). Outlet scope can now be set at invite time too — an optional multi-select (leave empty to grant all outlets) — in addition to the Edit user screen (see below).
3. Save
The user receives an email with a temporary password to sign in ("They'll receive an email with a temporary password to sign in."). Once they sign in they can access whichever apps they were granted.
Editing a user (outlet scope, role groups, apps)
The Edit user dialog manages Role groups (multi-select), Applications (multi-select) and Outlets (multi-select — the per-user outlet scope). [code: foodops-admin-app/apps/admin/src/components/settings/users/EditUserDialog.tsx — payload includes roleGroupIds[], applicationIds[], outletIds[] @ main]
Walkthrough B — create or customise a role
1. Open Role groups
admin.foodops.io → Settings → Organization → Users → Role groups tab (RoleGroupsTab.tsx). This is where the permission bundles are managed in-app. The underlying permission constants are defined in the identity service (ModelConstants.Roles.*, e.g. Roles.Users.View, Roles.Settings.View) [code: haafai.app.identity/Haafai.Identity/Controllers/{Users,Roles}Controller.cs uses [Authorize(Roles = ModelConstants.Roles.Users.View)] @ staging]. The standalone sso.hubits.io/blazor/roles Blazor page still exists as the identity-service surface.

2. Click "New role group" or edit an existing one
- New role group creates a custom role group from scratch — useful for unusual permission combinations
- Edit a built-in to tweak it — e.g., strip Counter's ability to delete orders if you want a read-only cashier role
3. Assign role groups
Each role is composed of role groups — bundles of related permissions (e.g., "View Sales", "Create Orders", "Close Registers"). Tick the groups the role should include. Changes apply to every user currently assigned to that role, immediately.
4. Save
The role is now available on the Invite User form and on the Edit User screen.
Built-in roles — what each one does
| Role | Groups | Typical user |
|---|---|---|
| Administrator | 250 (all) | Company owner, IT admin — unrestricted |
| Owner | 121 | Restaurant owner who doesn't need deep-config access but wants to see everything operational |
| Manager | 108 | Shift manager — operations + reports, no org-level config |
| Counter | 33 | Cashier — open register, take orders, process payments |
| Accounting Admin | 28 | Full accounting + receivables + payments |
| Purchasing | 19 | Create suppliers, POs, receive stock |
| Waiter | 17 | Dine-in order management, table service |
| Kitchen | 14 | KDS access — mark items ready, view queue |
| Accounting Officer | 11 | Read-only financial reports |
| Delivery Driver | 2 | Minimum — see assigned deliveries, mark delivered |
The group counts are indicators of scope, not exact permissions — each "group" bundles several individual permissions. A Waiter with 17 groups can touch many more than 17 actions in total.
Note: These role names/counts are seed/tenant data, not a code constant The 10 roles + their counts above came from a live (playwright) read of the test org and can vary per tenant/plan. In the new in-app surface they appear under the Role groups tab. The canonical permission identifiers live in the identity service (
ModelConstants.Roles.*). Several legacy role-gates were removed in the new stack — do not assume a specific role still gates a specific action without checking current code.
Key concepts
- SSO (Single Sign-On) — one login for all Hubits apps. Users authenticate once at
sso.hubits.ioand flow into foodops, People, Finance, etc. without re-logging. - Application grant — being in the Users list doesn't automatically give app access. Apps must be ticked during invite or edit. Someone with
Administratorrole but nofoodopsapplication grant still can't log into the restaurant app. - Role — a named bundle of permissions (e.g., "Counter", "Kitchen"). Assigned to users; editable at the role level so policy changes propagate.
- Role group — a sub-bundle inside a role (e.g., "Order Management", "Register Operations"). You can't edit individual permissions from the UI — you compose roles out of groups.
- Outlet scope — restricts a user to specific outlets. A Waiter assigned to Outlet A sees only Outlet A's tables and orders; Outlet B is invisible to them.
- User Bot — non-human actor that does things on behalf of a team (e.g., Telegram Notification Bot posting order alerts). Invited like a user but doesn't log in interactively.
- Audit Log — immutable record of every user action (login, user-created, role-changed, etc.). Stored at SSO level, visible to Administrators for compliance / incident investigation.
- Mobile app grant — separate from web-app grants. A user may have web-foodops access but no mobile Waiter app access, or vice versa.
Common questions
Q: Where do I manage users — in Foodops or in SSO?
A: In Foodops now — admin.foodops.io → Settings → Organization → Users (Members + Role groups tabs). It still talks to the shared Hubits identity service under the hood (so one person can work across Foodops/People/Finance with one login), but you no longer have to leave the app for routine user admin. The standalone sso.hubits.io portal remains as the identity-service surface.
Q: I invited someone but they never got the email. A: Check the email address for typos. Ask them to check spam. If still missing, from the Users list open the user's Edit screen and click "Resend Invitation" — generates a fresh link.
Q: Can I have a user with access to only some outlets? A: Yes — select specific outlets during invite or edit. The user will only see those outlets' data in reports, POS, kitchen, etc.
Q: Can two users share one login? A: Technically yes but strongly discouraged. Shared logins break the audit trail (you can't tell who did what). Create a separate user per person, even part-timers.
Q: What if I need a permission that's not in any role? A: Custom roles — create a new role, tick the role groups that cover the permissions you need. If you need finer-grained than groups allow, that's a support/feature-request conversation.
Q: How do I change someone's role? A: From the Users list, click Manage on their row, un-tick the old role, tick the new one, save. Takes effect immediately on their next page load.
Q: Does a deleted user's data disappear? A: No — orders, adjustments, POs they created stay intact (audit integrity). The user becomes "Deactivated" — can't log in, but their name still appears on historical records.
Q: How do I enforce 2FA? A: From the user's Security settings in SSO, 2FA can be enabled per-user. Org-level "require 2FA for admin roles" is a roadmap item; for now it's user-opt-in.
Q: Can I see a user's login history? A: Yes — Audit Logs in SSO show every login event with IP and timestamp. Filter by user to see their session history.
Q: How many users can I invite?
A: User count varies by plan — check sso.hubits.io/subscription. Starter typically covers 3-5 users; upgrade for more.
Q: How do I get "supervisor" or manager permission? I need to approve something / void / give a discount and the app won't let me. A: "Supervisor permission" isn't a switch you turn on yourself — it's a role group your Owner or Administrator assigns to your account. In Foodops the shift-supervisor tier is the built-in Manager role (operations + reports; Owner and Administrator sit above it — see the roles table above). To get it, ask whoever owns the account to open Settings → Organization → Users → Members, click Manage on your user, and add the Manager role group (or a custom role built to include just the permissions you need). It applies immediately on save. You can't grant it to yourself — the app blocks editing your own role groups as a self-lockout guard, so the change always goes through an owner/admin.
Q: The counter says I need permission to void / refund / discount — what should I ask for? A: Those are permission-gated actions tied to your role group, not a separate "supervisor mode." Ask your Owner/Administrator to move you onto a role group that includes them — the built-in Manager role covers most operational approvals, or they can create a custom role group (Users → Role groups → New role group) with exactly the permissions you need. Only someone with Manage Users rights can change roles, so the request goes to an owner/admin, not you.
Edge cases and known issues
User invited with role but can't access the app
- Symptom: User logs in but gets "Access denied" for Foodops
- Cause: Roles were ticked but the foodops Application checkbox wasn't
- Workaround: Edit the user, tick foodops under Applications, save. Roles and applications are independent.
User sees no data after login despite roles
- Symptom: Logged in, has roles, but every screen shows empty
- Cause: No outlets ticked during invite — they have app access but see no outlets' data
- Workaround: Edit the user, tick at least one outlet, save
Editing a built-in role affects everyone on that role
- Symptom: Customised "Counter" by removing a permission, now other cashiers can't do their jobs
- Cause: Built-in roles are shared org-wide — every user on that role inherits the changes
- Workaround: Don't edit built-ins. Create a custom role ("Counter – Restricted") and reassign specific users to it.
Role group counts differ from permission counts in audit logs
- Symptom: Role says "60 groups" but audit log shows hundreds of permission checks per user
- Cause: Each group is itself a bundle of permissions; counts measure groups, not individual permissions
- Workaround: Expected — treat group count as relative scope, not a permission ceiling
SSO portal feels separate from Foodops
- Symptom: Admins get confused bouncing between
app.foodops.ioandsso.hubits.io - Cause: Intentional separation — one portal manages identity across apps
- Workaround: Bookmark the SSO Users page directly for quick access; the Foodops Settings → Users redirect is one click
Plan limits
Plan tiers and their limits are not listed here — they are DB-backed and change with the lineup. See Pricing for the live catalogue.
Confirm exact numbers against sso.hubits.io/subscription — limits change with plan pricing.
API and integration notes
- Identity provider:
haafai.app.identity(prod branchstaging) —identity-api/sso.hubits.io, OAuth2 / OpenID Connect - Login flow:
/connect/authorize→ login → redirect back to the FoodOps app (new stack: admin.foodops.io, not the retiredapp.foodops.io) - Permission/role constants:
ModelConstants.Roles.*in the identity service; role groups + invite/edit are administered in-app (foodops-admin-appcomponents/settings/users/**) and via the identity-service Blazor UI atsso.hubits.io/blazor/roles - User admin endpoints are served by the identity service (admin-only)
- Audit log retention is plan-dependent — higher plans retain longer history
- Passwords, 2FA secrets, and MFA state live entirely at SSO — Foodops never handles credentials directly
Related
Settings Overview — where Foodops deep-links to SSO
Kitchen Display — Kitchen role users land here
POS Takeaway Order — Counter role users drive this flow
User Stories: Users & Roles — invitation + permission scenarios
2026-08-20 · codebase (foodops-admin-app 41b89f1..5b6816c, main): InviteUserDialog's role-group picker changed from a single Select (roleGroupId?: number, optional) to a checkbox multi-select (selectedGroupIds: Set), and the invite payload field is now roleGroupIds: number[] — always present, sent as [] when nothing is checked. Per the dialog's own test suite, an empty roleGroupIds array means the invited user is created with no permissions (distinct from the outlet-empty-array semantics, which means 'all outlets'). This contradicts the entry's Walkthrough A field table ('Role group ... select') and the cited invite payload shape (roleGroupId?); both have been corrected to reflect multi-select role groups on invite, bringing Invite in line with the Edit User dialog's existing multi-select Role groups behavior. The outletIds[] outlet-selection behavior documented in the 2026-08-19 note is unaffected by this diff. (auto-applied by the truth pipeline; adversarially gated)
2026-08-20 · codebase (haafai.app.identity 8d8e660..388eec9, staging): The identity service's user-registration/invite endpoint (UsersController.Register) was changed to accept MULTIPLE role groups per assignment — viewModel.ResolveRoleGroupIds() replaces the old single-value RoleGroupId, and the 'already has this role group' guard now checks membership against the whole requested set rather than one id. This is backend-only in this diff (no accompanying foodops-admin-app change was supplied), so it doesn't yet contradict the entry's documented invite-dialog contract, which still shows a singular 'Role group' select and a singular roleGroupId? field in InviteUserDialog.tsx's payload. Flagging for curators: if/when the admin-app InviteUserDialog is updated to a multi-select for Role group (mirroring the Edit User dialog's existing roleGroupIds[] multi-select), the Walkthrough A field table and the InviteUserDialog payload shape in this entry will need updating to match. Not yet verified live or against admin-app source this pass. (Note: the other hunk in this diff range — RemoveUserFromOrganisationAsync also clearing outlets/mobile apps — is the same change already captured in the entry's 2026-08-19 verification note; no new action needed there.) (auto-applied by the truth pipeline; adversarially gated)
2026-08-19 · codebase (foodops-admin-app 41b89f1..7f1a050, main): InviteUserDialog.tsx gained an Outlets multi-select (via a new useOutlets hook), and the invite payload now carries outletIds[] alongside applicationIds[]. Per the dialog's own helper text and the added test suite, an empty outlet selection is sent as [] and is treated by the backend as 'no restriction rows' — i.e. access to all outlets. This directly contradicts the entry's prior claim that Outlets are absent from the invite dialog and settable only on Edit User; that claim, the field table, and the cited payload shape have been corrected. Outlet scoping on the Edit User dialog (outletIds[] there) is unaffected and remains accurate. (auto-applied by the truth pipeline; adversarially gated)
2026-08-19 · codebase (haafai.app.identity 7bc5b96..8d8e660, staging): UserAssignmentService.RemoveUserFromOrganisationAsync now additionally clears a user's outlet assignments (ApplicationUserOutlets) and mobile-app assignments (ApplicationUserMobileApplications) for the organisation being removed, alongside the org membership/applications/role-groups/roles it already cleared — closing a gap where a removed user's outlet scope and mobile-app grant silently lingered in the DB and would come back unchanged if that user was ever re-invited to the same org. This doesn't contradict anything currently documented: the entry has no 'remove user from organisation' action distinct from the 'Deactivated' state described in the 'Does a deleted user's data disappear?' FAQ, and the outlet/mobile-app scoping documented under Walkthrough A's Edit User dialog and the 'Outlet scope' / 'Mobile app grant' Key concepts is about active users, which this change doesn't touch. Worth a short mention if curators ever add a documented 'remove/offboard user' flow, so it's clear stale outlet/mobile-app access no longer survives a re-invite. (auto-applied by the truth pipeline; adversarially gated)
2026-08-01 · codebase (haafai.app.identity 85abc6a..1aae8b0, staging): RolesController.RemoveRoleFromGroup (Role groups tab: removing a single permission-role from a role group) was fixed to revoke ONLY the specific role that was removed, instead of the prior logic which iterated the roles still remaining in the group — a latent bug that could strip members of permissions they were supposed to keep. The fix also adds a 'granted elsewhere' guard: if a member's permission is still granted via a different role group they belong to, it is no longer revoked. This doesn't contradict the entry — it continues to support Walkthrough B step 3's 'Changes apply to every user currently assigned to that role, immediately' and the 'Editing a built-in role affects everyone on that role' edge case, now implemented more precisely (exactly the removed permission is revoked, and only from members who don't retain it through another group). No entry text needs to change; this is another fix in the same role-group-propagation-correctness code path as the 2026-07-11 verification note. (auto-applied by the truth pipeline; adversarially gated) (auto-applied by the truth pipeline; adversarially gated)
2026-07-17 · curation (Track B, kb:gap) — added two customer-phrased Common-questions pairs answering a real client asking "how do I get supervisor permission". Grounded entirely in this entry's already-verified role-management content (the built-in Manager role as the shift-supervisor tier per the roles table; role-group assignment via Settings → Organization → Users → Members → Manage; the self-edit-role-group lockout guard noted in the 2026-07-05
foodops-admin-app@81653c9verification below; theuseUserAdminAccess/canManageUsersgate on who can change roles). No new code claim was made and no SHA was re-pinned. Left as UI-TODO: whether the counter surfaces an on-screen supervisor-authorization / manager-PIN prompt for individual overrides (void/refund/discount) versus pure role-group gating — not verified this pass, so the answer is framed around role assignment only.2026-07-11 · codebase (haafai.app.identity a787ecf..2fa051b, staging): Two bug fixes tighten role-group → member permission propagation, both in the code path behind Walkthrough B's role-group editing and the Edit User role-group reassignment described in this entry. (1) RoleService.AddRoleToGroupAsync/DeleteRoleGroupRoleAsync (Role groups tab: adding/removing a permission-role from a role group) now immediately grants/revokes that role on every current member of the group, instead of only affecting future assignments — this is the concrete mechanism that makes the entry's existing claim 'Changes apply to every user currently assigned to that role, immediately' (Walkthrough B step 3) and the 'Editing a built-in role affects everyone on that role' edge case actually hold; previously a removed permission could linger on already-assigned users until some other resync. (2) UserAssignmentService.SyncUserAssignmentsAsync (Edit User dialog role-group multi-select) now recomputes a user's desired roles as the union across ALL of their currently-assigned role groups and revokes anything not in that set, rather than only revoking roles tied to an explicitly-unassigned group — closing a gap where a permission dropped from a still-assigned group wasn't being revoked. Neither fix changes any UI surface or documented field; no edit to the entry text is needed, but curators should note the underlying propagation guarantee is now more reliable than it was, in case a future support ticket references stale-permission behavior predating this fix. Unrelated in this same diff: a new Costing.* permission-role seeding path (already captured in the 2026-07-10 verification note above) and internal-only DI plumbing (ForwardingApplicationDbContextFactory / Startup.cs) with no user-facing effect. (auto-applied by the truth pipeline; adversarially gated)
2026-07-10 · codebase (haafai.app.identity fa91390..a787ecf, staging): A new 'Costing / Food Cost' permission category (Costing.View, Costing.Manage, Costing.RecordWastage, Costing.ManageRecipes, Costing.ManageReasons) was added to RoleGroupHelper.CreateDefaultRoleGroups and seeded via a new EnsureCostingRolesExist() helper. It's folded into at least three of the default role-group permission sets — the two higher-tier groups get the full five Costing.* permissions, a lower-tier (view-only) group gets just Costing.View — increasing the permission-group counts new organisations get on signup. This only affects role groups created going forward (CreateDefaultRoleGroups runs per-organisation at creation time) or orgs that hit the reseed endpoint; it does not retroactively change already-provisioned orgs like the KB test org, so the entry's existing role-count table (already flagged as seed/tenant-data, not a code constant) isn't contradicted. Still, this is a new user-facing permission surface tied to a Costing/food-cost module that isn't mentioned in Key concepts or the role table — worth a short addition once the corresponding UI (recipe costing, wastage recording) is confirmed live, so curators know to check for a 'Costing' role-group entry when next reading role groups from a live org. (auto-applied by the truth pipeline; adversarially gated)
2026-07-05 · codebase (foodops-admin-app@81653c9): Members admin actions are now permission-gated via a new useUserAdminAccess() hook, beyond what the entry's Prerequisites section implies. Users without canManageUsers get a read-only Members view (no 'Invite member' button; in Edit User, the name/phone fields, role-group/application/outlet checkboxes, and the 'Unlock' action are all disabled) — the entry currently assumes anyone reaching the Users page already has full Manage Users rights. Separately, EditUserDialog now blocks editing your OWN role groups ('You can't change your own role groups.') as a self-lockout/self-demotion guard, which qualifies the entry's 'How do I change someone's role?' answer. The Members table also dropped its Joined-date and Locked/Active Status columns (now just Name/Email/Role groups/Actions), and InviteUserDialog added a client-side email-format check before submit. Worth adding a short 'view-only vs manage access' note and the self-edit role-group restriction to Walkthrough A / Key concepts / the relevant Common Question once confirmed live against a non-manager test account. (auto-applied by the truth pipeline; adversarially gated)
2026-07-05 · codebase (foodops-admin-app@6642e57): Two new read-only surfaces were added around user permissions, not yet reflected in this entry. (1) MembersTab now has a 'Roles' button (ShieldCheck icon) next to 'Manage' on each row, opening a new UserRolesDialog that shows the member's assigned role groups plus the specific roles each group grants (composed from GET /Users/{id} + GET /RoleGroups/{id}) — purely informational, assignment changes still happen via the existing Edit User ('Manage') dialog. (2) My Account > My Access (MyAccessTab.tsx) now splits the signed-in user's access into two chip sections — 'Role groups' and 'Roles' — and adds a 'View my permissions (N)' link opening a new MyPermissionsDialog listing the user's dotted fine-grained permission strings (e.g. sales.invoice.view) grouped by domain and filterable by search; previously this tab only showed a flat list of named roles. Worth adding to Walkthrough A (Members list actions) and Key concepts once the new dialogs are screenshotted. (auto-applied by the truth pipeline; adversarially gated)
Success: Verified 2026-04-15 · playwright Status:
stable· Review due: 2026-07-14 This entry was verified against the live app on the date above. The weekly sync (see_meta/failure-log.md) re-runs the Playwright scenarios tied to this module and will surface any drift as a[!failure]callout here.
Note: Code-verified 2026-05-04 · codebase Verified_by:
codebase· Last verified: 2026-05-04 ·haafai.app.identity:c9f554b+haafai.app.foodops:774bfe4Curatedsource_repos[]to two paths: the SSO repohaafai.app.identity:Haafai.Identity/**(where users/roles actually live — invite, role-groups, audit logs) and the Foodops-side placeholderhaafai.app.foodops:Haafai.Restaurant.App/Areas/Settings/{Controllers/{Users,Roles}Controller.cs,Views/{Users,Roles}/**}(which is just a deep-link redirect to SSO). No commits to either curated path since 2026-04-15: identity last touched on 2026-04-11 (c9f554b, registration + email verification — affects signup flow, not the in-Foodops Users page); the Foodops placeholder hasn't changed since 2025-04-10 (774bfe4). HIGH-drift flag was a false positive from broken globs. UI labels and role counts unchanged from 2026-04-15 playwright pass.
Note: Re-pointed after backend microservice split 2026-06-03 · codebase Verified_by:
codebase· Last verified: 2026-06-03 ·haafai.app.identity:ebaa30a+haafai.app.foodops:bf7c9d2+foodops-admin-app:f3ae3e9Identity (auth/SSO) was NOT part of the 2026-06-03 backend split — it remains the canonical user/role/audit-log source. Re-pinnedhaafai.app.identity:Haafai.Identity/**to prod HEADebaa30a(prod branchmaster) and the Foodops monolith placeholder to prod HEADbf7c9d2(both paths still resolve). Addedhaafai/foodops-admin-app(backoffice settings UI, prodmain) with globapps/admin/src/{pages/settings/organization/UsersPage.tsx,components/settings/users/**}— the React backoffice now surfaces a Users page plus the in-app members/invite/edit/role-groups tabs and dialogs (InviteUserDialog, EditUserDialog, MembersTab, RoleGroupsTab, MyAccessTab, MyProfileTab). Paths verified viagh api ...?ref=master(identity) /?ref=main(admin-app + foodops). Did NOT addfoodops.apiorhubits.microservices.
Important: Re-verified 2026-06-29 · codebase — NEW-STACK re-point (legacy app.foodops retired) Verified_by:
codebase·haafai.app.identity:31e7739(staging) +foodops-admin-app:f6ffd0b(main) CORRECTIONS: (1) Branch fix — the prior note said identity prod branch wasmaster; it isstaging(per the salesmade code-map + the task brief). Re-pinned to staging HEAD31e7739. (2) Removed the retiredhaafai.app.foodopsplaceholder fromsource_repos. (3) Biggest reframe — user management is now an in-app admin page (/settings/organization/users, Members + Role groups tabs), NOT an SSO redirect. Reworked the Plain-English summary, How-to-access, Walkthrough A (invite dialog: Full name/Email/Phone/Role group/Applications + temporary-password email), Walkthrough B (in-app Role groups tab; identityModelConstants.Roles.*), and the API notes (login redirects to admin.foodops.io; identity on staging). Outlet scope confirmed on the Edit user dialog (outletIds[]). RESIDUAL: role names/counts are seed data (not code-verified); several legacy role-gates were removed — any role-permission claim needs current-code checking.
Additional verification events are appended here by the weekly KB sync and by manual re-verification passes. See _meta/failure-log.md for the global failure index.