feat(roles): roles remember the jobs they follow (re-appliable), every role edit is signed
Closes the permissions-matrix loose ends (venue-modules.md §Permissions matrix): - `role_jobs` (migration 0029): a role stores the manifest jobs it was composed from (chips on at save + any bundle fully present). `jobById` / `jobsBehind` in @parking/shared surface a followed job whose bundle grew past the role in a later release; the roles list shows a "behind <job>" badge with a one-click "Update to job" (the union, nothing removed) and the editor lints it. Never a runtime union: the grid stays the explicit enforcement layer and an update never widens a role without a click. - Every role create/update/delete appends a `config_change` (`role.<id>`, prev/value = name + sorted permissions + jobs, operator); a no-op resave signs nothing. roleRoutes now takes the ledger. - booth-supervisor already carries subscription:*; the stale open note is closed. Tests: routes/roles.test.ts. Wiki: venue-modules status, local-jwt-auth, log. Claude-Session: https://claude.ai/code/session_01FWncR69HgGPuei1dLrW3cU
This commit is contained in:
@@ -439,9 +439,23 @@ resources — the till already IS that copy. Role *templates stored in the DB*
|
||||
(they change with the module), roles are data; keep that line.
|
||||
|
||||
**Status.** Moves 1, 2 and 3 built 2026-09-05 (see the Tills as-built below and [[shift]]
|
||||
§Tills). Open: whether `booth-supervisor` should carry `subscription:*` by default; whether a
|
||||
job should be *re-applicable* after a module update (today a chip only adds/removes the bundle
|
||||
as it is now); an audit `config_change` on role edits.
|
||||
§Tills). The three loose ends closed 2026-09-06:
|
||||
- `booth-supervisor` DOES carry `subscription:read/create/update` (plus `tariff:read`,
|
||||
`validation:read`) — it already did; the note was stale. Decided: a supervisor sells and
|
||||
edits subscriptions by default.
|
||||
- **Jobs are remembered and re-appliable.** A role stores the jobs it follows (`role_jobs`:
|
||||
the chips on at save, plus any bundle fully present). `jobsBehind(jobs, has)` in
|
||||
`@parking/shared` lists a followed job whose bundle has GROWN past the role (a newer
|
||||
release added a permission); the roles list shows a "behind <job>" badge with a one-click
|
||||
"Update to job" (the union; nothing removed), the editor lints it. Deliberately NOT a
|
||||
runtime union: what a role may do is always the explicit grid, and a software update never
|
||||
changes it without an admin's click — see the threat model. The first failure of this kind
|
||||
was the wash operator's empty price list (the settings read needed `site:read`; now
|
||||
`carwash:read` OR `site:read`, `requireAnyPermission`).
|
||||
- **Role edits are signed.** Create/update/delete each append one `config_change`
|
||||
(`setting: role.<id>`, `value`/`prev` = name + sorted permissions + jobs, `operator`); a
|
||||
no-op resave signs nothing. A role edit is a privilege change and was the one setting an
|
||||
admin could alter without a trace.
|
||||
|
||||
## Tills: shifts per money-taking module — BUILT (raised + built 2026-09-05)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user