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:
2026-09-06 12:52:47 +02:00
parent e14e31a840
commit 50c18405b6
14 changed files with 329 additions and 17 deletions
+3
View File
@@ -917,6 +917,9 @@ export const en: Catalog = {
jobsHint: "A job adds its permissions in one click; fine-tune below. Tap it again to remove them.",
lintMixedTills: "This role can open more than one till ({{tills}}) — one person, two drawers. Intended?",
lintPartialJob: "Partial \"{{job}}\": missing {{missing}} — this desk can look but not act.",
lintJobBehind: "Behind \"{{job}}\": this release added {{missing}} to the job. Tap the job chip off and on to take it, or tick it below.",
behind: "behind {{job}}",
reapply: "Update to job",
},
jobs: {
"booth-operator": "Booth operator",
+3
View File
@@ -931,6 +931,9 @@ export const sq = {
jobsHint: "Një punë shton lejet e saj me një klik; rregulloji poshtë. Kliko sërish për t'i hequr.",
lintMixedTills: "Ky rol mund të hapë më shumë se një arkë ({{tills}}) — një person, dy arka. E qëllimshme?",
lintPartialJob: "\"{{job}}\" e pjesshme: mungojnë {{missing}} — kjo tavolinë sheh, por nuk vepron.",
lintJobBehind: "Pas \"{{job}}\": ky version i shtoi punës {{missing}}. Hiqe dhe rivendose punën për t'i marrë, ose shënoji poshtë.",
behind: "pas {{job}}",
reapply: "Përditëso sipas punës",
},
jobs: {
"booth-operator": "Operator kabine",