Skip to content

Commit 2da3842

Browse files
authored
[change request] decidesk notifications (#267)
* docs(notifications): change request for decidesk schema notifications * feat(notifications): add x-openregister-notifications to decidesk schemas
1 parent e198f8d commit 2da3842

4 files changed

Lines changed: 277 additions & 0 deletions

File tree

lib/Settings/decidesk_register.json

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -146,6 +146,27 @@
146146
]
147147
},
148148
"Meeting": {
149+
"x-openregister-notifications": {
150+
"meetingScheduled": {
151+
"trigger": { "type": "created" },
152+
"enabled": true,
153+
"channels": ["nc-notification"],
154+
"recipients": [
155+
{ "kind": "object-acl", "permission": "read" },
156+
{ "kind": "groups", "groups": ["decidesk-members"] }
157+
],
158+
"subject": { "nl": "Nieuwe vergadering ingepland: {{title}}", "en": "New meeting scheduled: {{title}}" }
159+
},
160+
"meetingReminder": {
161+
"trigger": { "type": "scheduled", "intervalSec": 86400, "filter": { "lifecycle": "scheduled" } },
162+
"enabled": true,
163+
"channels": ["nc-notification"],
164+
"recipients": [
165+
{ "kind": "object-acl", "permission": "read" }
166+
],
167+
"subject": { "nl": "Herinnering: vergadering '{{title}}' komt eraan", "en": "Reminder: meeting '{{title}}' is coming up" }
168+
}
169+
},
149170
"slug": "meeting",
150171
"icon": "CalendarClock",
151172
"version": "0.3.0",
@@ -633,6 +654,18 @@
633654
]
634655
},
635656
"Motion": {
657+
"x-openregister-notifications": {
658+
"motionSubmitted": {
659+
"trigger": { "type": "created" },
660+
"enabled": true,
661+
"channels": ["nc-notification"],
662+
"recipients": [
663+
{ "kind": "object-acl", "permission": "read" },
664+
{ "kind": "groups", "groups": ["decidesk-members"] }
665+
],
666+
"subject": { "nl": "Nieuwe motie ingediend: {{title}}", "en": "New motion submitted: {{title}}" }
667+
}
668+
},
636669
"slug": "motion",
637670
"icon": "TextBoxOutline",
638671
"version": "0.2.0",
@@ -1118,6 +1151,18 @@
11181151
]
11191152
},
11201153
"Decision": {
1154+
"x-openregister-notifications": {
1155+
"decisionRecorded": {
1156+
"trigger": { "type": "created" },
1157+
"enabled": true,
1158+
"channels": ["nc-notification"],
1159+
"recipients": [
1160+
{ "kind": "object-acl", "permission": "read" },
1161+
{ "kind": "groups", "groups": ["decidesk-members"] }
1162+
],
1163+
"subject": { "nl": "Besluit vastgelegd: {{title}}", "en": "Decision recorded: {{title}}" }
1164+
}
1165+
},
11211166
"slug": "decision",
11221167
"icon": "GavelOutline",
11231168
"version": "0.2.0",
@@ -1301,6 +1346,28 @@
13011346
]
13021347
},
13031348
"ActionItem": {
1349+
"x-openregister-notifications": {
1350+
"actionAssigned": {
1351+
"trigger": { "type": "created" },
1352+
"enabled": true,
1353+
"channels": ["nc-notification"],
1354+
"recipients": [
1355+
{ "kind": "object-acl", "permission": "manage" },
1356+
{ "kind": "groups", "groups": ["decidesk-members"] }
1357+
],
1358+
"subject": { "nl": "Nieuwe actie toegewezen: {{title}}", "en": "New action item assigned: {{title}}" }
1359+
},
1360+
"actionOverdue": {
1361+
"trigger": { "type": "scheduled", "intervalSec": 86400, "filter": { "taskStatus": "overdue" } },
1362+
"enabled": true,
1363+
"channels": ["nc-notification"],
1364+
"recipients": [
1365+
{ "kind": "object-acl", "permission": "manage" },
1366+
{ "kind": "groups", "groups": ["decidesk-members"] }
1367+
],
1368+
"subject": { "nl": "Actie over tijd: {{title}}", "en": "Action item overdue: {{title}}" }
1369+
}
1370+
},
13041371
"slug": "action-item",
13051372
"icon": "CheckboxMarkedOutline",
13061373
"version": "0.1.0",
@@ -2144,6 +2211,18 @@
21442211
}
21452212
},
21462213
"BudgetProposal": {
2214+
"x-openregister-notifications": {
2215+
"budgetProposalVotingDeadline": {
2216+
"trigger": { "type": "scheduled", "intervalSec": 86400, "filter": { "status": "voting" } },
2217+
"enabled": true,
2218+
"channels": ["nc-notification"],
2219+
"recipients": [
2220+
{ "kind": "object-acl", "permission": "read" },
2221+
{ "kind": "groups", "groups": ["decidesk-members"] }
2222+
],
2223+
"subject": { "nl": "Stemtermijn begrotingsvoorstel '{{title}}' verloopt binnenkort", "en": "Budget proposal '{{title}}' voting deadline is approaching" }
2224+
}
2225+
},
21472226
"slug": "budget-proposal",
21482227
"icon": "LightbulbOnOutline",
21492228
"version": "0.1.0",
@@ -2221,6 +2300,18 @@
22212300
}
22222301
},
22232302
"PublicConsultation": {
2303+
"x-openregister-notifications": {
2304+
"consultationDeadline": {
2305+
"trigger": { "type": "scheduled", "intervalSec": 86400, "filter": { "status": "open" } },
2306+
"enabled": true,
2307+
"channels": ["nc-notification"],
2308+
"recipients": [
2309+
{ "kind": "object-acl", "permission": "read" },
2310+
{ "kind": "groups", "groups": ["decidesk-members"] }
2311+
],
2312+
"subject": { "nl": "Inspraaktermijn '{{title}}' verloopt binnenkort", "en": "Consultation '{{title}}' deadline is approaching" }
2313+
}
2314+
},
22242315
"slug": "public-consultation",
22252316
"icon": "ForumOutline",
22262317
"version": "0.1.0",
Lines changed: 123 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,123 @@
1+
---
2+
kind: config
3+
depends_on: [notification-updated-field-change-condition]
4+
---
5+
6+
## Why
7+
8+
Decidesk (meeting/decision governance for councils, boards, NGOs, citizen participation) currently declares no `x-openregister-notifications` on its schemas, so the OpenRegister notification engine (change `notification-schema-rules-and-userconfig-prefs`, archived 2026-05-26) emits nothing for decidesk objects. Per the fleet notification plan, decidesk should notify about: meeting scheduled + reminder; action item assigned / overdue; motion submitted; decision recorded; participation deadlines.
9+
10+
This change adds schema-declared notification rules in the verified dialect to `lib/Settings/decidesk_register.json`. The design is constrained by two realities discovered while verifying recipient fields:
11+
12+
1. **`Participant`, `Motion.proposer`, and `ActionItem.assignee` do not hold Nextcloud user IDs.** `Participant` holds `displayName` + `email` (an email string, not a uid); `Motion.proposer` is a *name* ("Name of proposer"); `ActionItem.assignee` is "Assigned participant" (a participant reference / name, not a resolvable uid). The verified `kind:field` recipient resolves **uids only**, so assignee/proposer-based delivery is unreliable. Per the plan caveat, decidesk routes these to `kind:groups` (governance/secretariat staff) and `kind:object-acl` (whoever has read/manage on the object) instead of `kind:field`. The assignee-direct rule is declared but flagged: it only fires correctly once `assignee` carries a uid (a separate data-model change).
13+
14+
2. **No named lifecycle transition actions are defined** on Meeting/Motion/Decision (the schemas carry a `lifecycle` enum but no transition-action map). The verified `transition` trigger needs a named `action`. So status-change rules (meeting → scheduled, motion → submitted, decision recorded) are expressed today as `created` (notify when the row first appears in the target state) or `scheduled` reminders, and the precise "lifecycle entered X" form is deferred — see Caveats. `depends_on: notification-updated-field-change-condition` is declared because the most-wanted form ("notify when `lifecycle`/`taskStatus` changed to Y") needs the field-change condition on the `updated` trigger from that engine change.
15+
16+
## What Changes
17+
18+
Add `x-openregister-notifications` to these schemas in `lib/Settings/decidesk_register.json`.
19+
20+
### Meeting — scheduled + reminder
21+
22+
```jsonc
23+
"x-openregister-notifications": {
24+
"meetingScheduled": {
25+
"trigger": {"type": "created"}, "enabled": true,
26+
"channels": ["nc-notification"],
27+
"recipients": [{"kind": "object-acl", "permission": "read"}, {"kind": "groups", "groups": ["decidesk-members"]}],
28+
"subject": {"nl": "Nieuwe vergadering ingepland: {{title}}", "en": "New meeting scheduled: {{title}}"}
29+
},
30+
"meetingReminder": {
31+
"trigger": {"type": "scheduled", "intervalSec": 86400, "filter": {"lifecycle": "scheduled"}}, "enabled": true,
32+
"channels": ["nc-notification"],
33+
"recipients": [{"kind": "object-acl", "permission": "read"}],
34+
"subject": {"nl": "Herinnering: vergadering '{{title}}' komt eraan", "en": "Reminder: meeting '{{title}}' is coming up"}
35+
}
36+
}
37+
```
38+
39+
Recipients use `object-acl:read` (everyone who can see the meeting) plus a `decidesk-members` group, because `Meeting` has no attendee-uid field.
40+
41+
### ActionItem — assigned + overdue
42+
43+
```jsonc
44+
"x-openregister-notifications": {
45+
"actionAssigned": {
46+
"trigger": {"type": "created"}, "enabled": true,
47+
"channels": ["nc-notification"],
48+
"recipients": [{"kind": "object-acl", "permission": "manage"}, {"kind": "groups", "groups": ["decidesk-members"]}],
49+
"subject": {"nl": "Nieuwe actie toegewezen: {{title}}", "en": "New action item assigned: {{title}}"}
50+
},
51+
"actionOverdue": {
52+
"trigger": {"type": "scheduled", "intervalSec": 86400, "filter": {"taskStatus": "overdue"}}, "enabled": true,
53+
"channels": ["nc-notification"],
54+
"recipients": [{"kind": "object-acl", "permission": "manage"}, {"kind": "groups", "groups": ["decidesk-members"]}],
55+
"subject": {"nl": "Actie over tijd: {{title}}", "en": "Action item overdue: {{title}}"}
56+
}
57+
}
58+
```
59+
60+
`assignee` is **not** used as a `kind:field` recipient (it is a participant name, not a uid). Once `assignee` carries a uid, add `{"kind": "field", "field": "assignee"}` — see Caveats.
61+
62+
### Motion — submitted
63+
64+
```jsonc
65+
"x-openregister-notifications": {
66+
"motionSubmitted": {
67+
"trigger": {"type": "created"}, "enabled": true,
68+
"channels": ["nc-notification"],
69+
"recipients": [{"kind": "object-acl", "permission": "read"}, {"kind": "groups", "groups": ["decidesk-members"]}],
70+
"subject": {"nl": "Nieuwe motie ingediend: {{title}}", "en": "New motion submitted: {{title}}"}
71+
}
72+
}
73+
```
74+
75+
`proposer` is a name, not a uid — not used as a recipient. Motions enter at lifecycle `submitted`, so `created` ≈ "submitted".
76+
77+
### Decision — recorded
78+
79+
```jsonc
80+
"x-openregister-notifications": {
81+
"decisionRecorded": {
82+
"trigger": {"type": "created"}, "enabled": true,
83+
"channels": ["nc-notification"],
84+
"recipients": [{"kind": "object-acl", "permission": "read"}, {"kind": "groups", "groups": ["decidesk-members"]}],
85+
"subject": {"nl": "Besluit vastgelegd: {{title}}", "en": "Decision recorded: {{title}}"}
86+
}
87+
}
88+
```
89+
90+
### PublicConsultation / BudgetProposal — participation deadlines
91+
92+
```jsonc
93+
// PublicConsultation
94+
"x-openregister-notifications": {
95+
"consultationDeadline": {
96+
"trigger": {"type": "scheduled", "intervalSec": 86400, "filter": {"status": "open"}}, "enabled": true,
97+
"channels": ["nc-notification"],
98+
"recipients": [{"kind": "object-acl", "permission": "read"}, {"kind": "groups", "groups": ["decidesk-members"]}],
99+
"subject": {"nl": "Inspraaktermijn '{{title}}' verloopt binnenkort", "en": "Consultation '{{title}}' deadline is approaching"}
100+
}
101+
}
102+
```
103+
104+
`BudgetProposal` follows the same `scheduled`+`filter:{status:"voting"}` shape for its voting-window deadline. The deadline date (`submissionDeadline` / proposal voting window) is the field the engine filters/evaluates against per scheduled run.
105+
106+
## Capabilities
107+
108+
No new product capability. This adds schema-declared notification configuration consumed by the existing OpenRegister notification engine.
109+
110+
## Impact
111+
112+
- **Affected file:** `lib/Settings/decidesk_register.json` only (additive `x-openregister-notifications` blocks).
113+
- No data migration, no API change, no Vue change.
114+
- Rules go live only when `notification-schema-rules-and-userconfig-prefs` engine is present.
115+
- Recipient delivery for assignee/proposer is intentionally routed to groups/object-acl, not the (non-uid) person fields — see Caveats.
116+
117+
## Caveats
118+
119+
- **Participant / proposer / assignee hold emails and display names, not Nextcloud uids.** `kind:field` resolves uids only, so per-person delivery to the assignee/proposer/participant is **not used**. Rules route to `kind:object-acl` (read/manage on the object) and `kind:groups` (`decidesk-members`). To deliver to the actual assignee, a data-model change is required to store a uid on `ActionItem.assignee` (and similarly for proposer); only then add `{"kind":"field","field":"assignee"}`. Flagged from the fleet plan's "participant uid caveat".
120+
- **No named lifecycle transition actions exist** on Meeting/Motion/Decision schemas. Status-change rules are approximated by `created` (object first appears in target state) and `scheduled` filtered on `lifecycle`/`status`/`taskStatus`. The precise "lifecycle entered X" / "status changed to Y" form is deferred to `notification-updated-field-change-condition` (declared in depends_on) or to adding named transition actions to the schemas.
121+
- **External-recipient email** (participant emails, citizen submitters) is not deliverable — the engine `field`/`groups`/`object-acl` recipients resolve to internal Nextcloud users only. Citizen-facing participation notifications are out of scope until an external-email channel exists.
122+
- **`decidesk-members` group** is assumed to exist in the deployment; confirm group provisioning, or swap to the deployment's actual governance/secretariat group name.
123+
- **`scheduled` deadline rules** assume the engine evaluates the deadline field (`submissionDeadline`, meeting `scheduledDate`) per run; the per-day reminder fires for matching rows each interval rather than once at an exact horizon.
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
---
2+
status: draft
3+
---
4+
5+
# Decidesk Schema Notifications
6+
7+
## Purpose
8+
9+
Adds `x-openregister-notifications` annotations to `lib/Settings/decidesk_register.json` in the verified OpenRegister notification-engine dialect, covering meeting scheduled + reminder, action item assigned + overdue, motion submitted, decision recorded, and participation deadlines. Configuration-only; no data-model or API change.
10+
11+
## ADDED Requirements
12+
13+
### Requirement: Governance schemas MUST declare notifications in the verified engine dialect
14+
15+
`Meeting`, `ActionItem`, `Motion`, `Decision`, `PublicConsultation`, and `BudgetProposal` MUST declare `x-openregister-notifications` using only verified keys: `trigger.type`, `channels[]`, `recipients[]`, and inline `subject{nl,en}`.
16+
17+
#### Scenario: Meeting scheduled and reminder rules
18+
19+
- **GIVEN** the `Meeting` schema
20+
- **WHEN** notifications are declared
21+
- **THEN** a `created`-trigger rule notifies on a newly scheduled meeting
22+
- **AND** a `scheduled`-trigger rule (intervalSec >= 60, filter on lifecycle) sends a reminder
23+
24+
### Requirement: Recipients for non-uid person fields MUST use object-acl/groups, not field
25+
26+
Because `ActionItem.assignee`, `Motion.proposer`, and `Participant` hold participant names / email strings rather than Nextcloud user IDs, rules for these MUST NOT use `kind:field` on those properties; they MUST route to `kind:object-acl` and `kind:groups`.
27+
28+
#### Scenario: Action item assigned routes to object-acl and a group
29+
30+
- **GIVEN** `ActionItem.assignee` is a participant name, not a uid
31+
- **WHEN** the actionAssigned rule is declared
32+
- **THEN** its `recipients` use `kind:object-acl` (permission manage) and `kind:groups`
33+
- **AND** no `kind:field` recipient references `assignee`
34+
35+
### Requirement: Status-change rules MUST be approximated and deferrals documented
36+
37+
With no named lifecycle transition actions on the schemas, status-change rules MUST be expressed via `created` (object first appears in target state) or `scheduled` (filtered on lifecycle/status), and the precise lifecycle-entered form MUST be documented as deferred to `notification-updated-field-change-condition`.
38+
39+
#### Scenario: Motion submitted approximated by created
40+
41+
- **GIVEN** motions enter at lifecycle `submitted` and no `submit` transition action is defined
42+
- **WHEN** the motionSubmitted rule is declared
43+
- **THEN** it uses `trigger.type: "created"`
44+
- **AND** the proposal's Caveats note the lifecycle-entered form is deferred
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
# Tasks
2+
3+
- [x] Add `x-openregister-notifications` to `Meeting` (meetingScheduled created + meetingReminder scheduled)
4+
- [x] Add `x-openregister-notifications` to `ActionItem` (actionAssigned created + actionOverdue scheduled)
5+
- [x] Add `x-openregister-notifications` to `Motion` (motionSubmitted created)
6+
- [x] Add `x-openregister-notifications` to `Decision` (decisionRecorded created)
7+
- [x] Add `x-openregister-notifications` to `PublicConsultation` (consultationDeadline scheduled, filter status=open)
8+
- [x] Add `x-openregister-notifications` to `BudgetProposal` (deadline scheduled, filter status=voting)
9+
- [x] Route all recipients to `object-acl` + `groups` (NOT `field`) for Meeting/ActionItem/Motion/Decision since assignee/proposer/participant are not uids
10+
- [x] Provide inline `subject{nl,en}` for every rule
11+
- [x] Validate `lib/Settings/decidesk_register.json` parses as JSON and every block uses verified keys only
12+
13+
## Acceptance criteria
14+
15+
- Every rule uses `trigger.type` from the verified set (`created`/`scheduled`), `channels[]`, `recipients[]` with `kind` of `object-acl|groups`, and inline `subject{nl,en}`.
16+
- No `kind:field` recipient references a non-uid field (assignee, proposer, participant email/displayName).
17+
- `scheduled` rules carry `intervalSec >= 60` and a `filter` on the relevant lifecycle/status field.
18+
- The register JSON validates against OpenRegister's register schema after the additions.
19+
- Assignee-direct and lifecycle-entered-X deferrals are documented in the proposal's Caveats.

0 commit comments

Comments
 (0)