feat: p2-agenda-management — governance agenda lifecycle#32
feat: p2-agenda-management — governance agenda lifecycle#32rubenvdlinde wants to merge 14 commits intodevelopmentfrom
Conversation
Add full governance agenda lifecycle: AgendaService (publishAgenda, advanceBobPhase, processHamerstukken, reorderItems), AgendaController with 4 REST endpoints, AgendaBuilder drag-drop frontend, LiveMeeting view with BOB phase tracking, COI declaration support, and motion linking on AgendaItemDetail.
…#15) Add Newman/Postman agenda.json integration tests for all 4 API endpoints. Mark all implemented tasks [x] in tasks.md. Set design.md status to pr-created.
Quality Report — ConductionNL/decidesk @
|
| Check | PHP | Vue | Security | License | Tests |
|---|---|---|---|---|---|
| lint | ✅ | ||||
| phpcs | ✅ | ||||
| phpmd | ✅ | ||||
| psalm | ✅ | ||||
| phpstan | ✅ | ||||
| phpmetrics | ✅ | ||||
| eslint | ❌ | ||||
| stylelint | ✅ | ||||
| composer | ✅ | ✅ 100/100 | |||
| npm | ✅ | ✅ 416/416 | |||
| PHPUnit | ❌ | ||||
| Newman | ✅ | ||||
| Playwright | ⏭️ |
Coverage: 0% (0/21 statements)
Quality workflow — 2026-04-14 08:45 UTC
Download the full PDF report from the workflow artifacts.
Code Review — Juan Claude van DammeResult: FAIL (3 critical, 6 warning, 2 suggestion) CRITICAL
All four agenda endpoints lack role-based authorisation WARNING
Unsafe mock reuse in
Duplicate translation key SUGGESTION
Note: task-9.3 (Playwright e2e tests) and task-10.6 (seed data verification) are unchecked in |
Security Review — Clyde BarcodeResult: FAIL (2 critical, 3 warning, 1 suggestion) SAST: Semgrep CRITICALMissing backend authorization on AgendaController — any authenticated user can perform chair-only operations Hardcoded WARNINGIDOR in
Internal exception messages forwarded directly to API responses SUGGESTIONLegacy |
…agenda-management
Quality Report — ConductionNL/decidesk @
|
| Check | PHP | Vue | Security | License | Tests |
|---|---|---|---|---|---|
| lint | ✅ | ||||
| phpcs | ✅ | ||||
| phpmd | ✅ | ||||
| psalm | ✅ | ||||
| phpstan | ✅ | ||||
| phpmetrics | ✅ | ||||
| eslint | ❌ | ||||
| stylelint | ✅ | ||||
| composer | ✅ | ✅ 100/100 | |||
| npm | ✅ | ✅ 416/416 | |||
| PHPUnit | ❌ | ||||
| Newman | ✅ | ||||
| Playwright | ⏭️ |
Coverage: 0% (0/21 statements)
Quality workflow — 2026-04-14 09:24 UTC
Download the full PDF report from the workflow artifacts.
|
Hydra Builder — Fix iteration 1 Fixed findings: CRITICAL
WARNING
Also fixed to keep quality gate green:
Remaining SUGGESTIONs (not addressed — informational only):
|
Quality Report — ConductionNL/decidesk @
|
| Check | PHP | Vue | Security | License | Tests |
|---|---|---|---|---|---|
| lint | ✅ | ||||
| phpcs | ✅ | ||||
| phpmd | ✅ | ||||
| psalm | ✅ | ||||
| phpstan | ✅ | ||||
| phpmetrics | ✅ | ||||
| eslint | ❌ | ||||
| stylelint | ✅ | ||||
| composer | ✅ | ✅ 100/100 | |||
| npm | ✅ | ✅ 416/416 | |||
| PHPUnit | ✅ | ||||
| Newman | ✅ | ||||
| Playwright | ⏭️ |
Coverage: 0% (0/78 statements)
Quality workflow — 2026-04-14 09:29 UTC
Download the full PDF report from the workflow artifacts.
Code Review — Juan Claude van DammeResult: FAIL (1 critical, 4 warnings, 4 suggestions) CRITICAL[CRITICAL] Authorization bypass on if ($meetingId === null) {
return new JSONResponse(['message' => 'Meeting relation required for authorization'], Http::STATUS_FORBIDDEN);
}WARNING[WARNING] Participants not converted to array in foreach ($participants as $participant) {
$participantData = $this->toArray(item: $participant);
$leftAt = $participantData['leftAt'] ?? null;
if ($leftAt !== null) { continue; }
$userId = $participantData['owner'] ?? null;
// ...
}[WARNING] No const response = await fetch(OC.generateUrl(`/apps/decidesk/api/agendas/${this.meetingId}/reorder`), { ... })
if (!response.ok) {
console.error('Failed to persist agenda reorder:', response.status)
// Optionally: revert localItems to pre-drag state
return
}
this.$emit('reordered', ids)[WARNING] const parts = await this.objectStore.fetchObjects('participant', {
'@self.relations.meeting': this.id,
})[WARNING] Overly broad PHPStan ignore silences all unread-property warnings on SUGGESTION[SUGGESTION] [SUGGESTION] import { getCurrentUser } from '@nextcloud/auth'
// ...
const currentUser = getCurrentUser()
const displayName = currentUser?.displayName ?? currentUser?.uid ?? 'Unknown'[SUGGESTION] [SUGGESTION] Playwright/browser tests missing (task 9.3 unchecked) Reviewed changed files only. No findings for unchanged context files. |
- AgendaBuilder.vue: remove unnecessary quotes from `requesttoken` property (quote-props) - LiveMeeting.vue, AgendaItemDetail.vue: remove alignment spaces in BOB_STAGES array (no-multi-spaces) - MeetingDetail.vue: remove alignment spaces in exportColumns array (no-multi-spaces) - MeetingDetail.vue: split COI list-item attributes to separate lines, `role` on own line (vue/max-attributes-per-line) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
Hydra Builder — Fix iteration 2 Merged latest `development` branch (clean merge, 1 file updated). Fixed findings from Code Review and Security Review: [CRITICAL] NcTextArea not imported in AgendaBuilder.vue — Fixed in iteration 1: added to import and `components` declaration. [CRITICAL] NcTextArea not imported in AgendaItemDetail.vue — Fixed in iteration 1: added to import and `components` declaration. [CRITICAL / SECURITY CRITICAL] All four agenda endpoints lack role-based authorisation — Fixed in iteration 1: `requireChairOrAdmin()` method added to `AgendaController`, called at the top of `publish`, `advanceBobPhase`, `processHamerstukken`, and `reorder`. Returns HTTP 403 when the calling user is not an admin or a chair/secretary for the meeting. [SECURITY CRITICAL] Hardcoded `isChair: true` in LiveMeeting.vue — Fixed in iteration 1: `isChair` is now a computed property resolving the current user against the loaded `participants` array. [WARNING] `isChairOrSecretary` hardcoded in MeetingDetail.vue — Fixed in iteration 1: now a computed property using `getCurrentUser()` matched against meeting participants (fetched in a new `created()` hook). [WARNING] `publishAgenda` notifies ALL participants system-wide — Already in the code with `'@self.relations.meeting' => $meetingId` filter on the participant query. [WARNING] `CalendarEventService` call missing from `publishAgenda` — Fixed in iteration 1: `$this->calendarEventService->updateMeetingEvent()` called after the notification loop. [WARNING] `advanceBobPhase` returns 422 for a not-found item — Fixed in iteration 1: introduced `NotFoundException` (HTTP 404) distinct from `InvalidArgumentException` (HTTP 422). [WARNING] Unsafe mock reuse in `testAdvanceBobPhaseCyclesThroughPhases` — Fixed in iteration 1: fresh `createMock(ObjectService::class)` instantiated per loop iteration. [WARNING / SECURITY WARNING] IDOR in `reorderItems` — Already in the code: `reorderItems()` builds a `$validIds` set from the meeting's actual items and skips any submitted UUID not belonging to the target meeting. [WARNING / SECURITY WARNING] Internal exception messages forwarded to API responses — Already in the code: `processHamerstukken` and `reorder` catch `\Throwable` and return a generic message; the full exception is logged server-side. [WARNING] Duplicate translation key `"Back to meeting detail"` — Fixed in iteration 1: duplicate entry removed from `l10n/en.json`. Quality gate — ESLint (fixed in iteration 2):
Quality gate — PHPUnit (fixed in iteration 1):
Remaining SUGGESTIONs (not addressed — informational only):
|
Security Review — Clyde BarcodeResult: PASS (0 critical, 2 warning, 0 suggestion) SAST scan (Semgrep WARNINGAuthorization bypass on Frontend
[FALSE POSITIVE] CSRF — All four agenda lifecycle endpoints carry [FALSE POSITIVE] Admin flag in settings response — |
|
Hydra Builder — Fix iteration 1 Fixed findings:
Remaining SUGGESTIONs (not addressed — informational only):
|
|
Pipeline complete — code review and security review both passed. Fix iterations: 0. |
29 similar comments
|
Pipeline complete — code review and security review both passed. Fix iterations: 0. |
|
Pipeline complete — code review and security review both passed. Fix iterations: 0. |
|
Pipeline complete — code review and security review both passed. Fix iterations: 0. |
|
Pipeline complete — code review and security review both passed. Fix iterations: 0. |
|
Pipeline complete — code review and security review both passed. Fix iterations: 0. |
|
Pipeline complete — code review and security review both passed. Fix iterations: 0. |
|
Pipeline complete — code review and security review both passed. Fix iterations: 0. |
|
Pipeline complete — code review and security review both passed. Fix iterations: 0. |
|
Pipeline complete — code review and security review both passed. Fix iterations: 0. |
|
Pipeline complete — code review and security review both passed. Fix iterations: 0. |
|
Pipeline complete — code review and security review both passed. Fix iterations: 0. |
|
Pipeline complete — code review and security review both passed. Fix iterations: 0. |
|
Pipeline complete — code review and security review both passed. Fix iterations: 0. |
|
Pipeline complete — code review and security review both passed. Fix iterations: 0. |
|
Pipeline complete — code review and security review both passed. Fix iterations: 0. |
|
Pipeline complete — code review and security review both passed. Fix iterations: 0. |
|
Pipeline complete — code review and security review both passed. Fix iterations: 0. |
|
Pipeline complete — code review and security review both passed. Fix iterations: 0. |
|
Pipeline complete — code review and security review both passed. Fix iterations: 0. |
|
Pipeline complete — code review and security review both passed. Fix iterations: 0. |
|
Pipeline complete — code review and security review both passed. Fix iterations: 0. |
|
Pipeline complete — code review and security review both passed. Fix iterations: 0. |
|
Pipeline complete — code review and security review both passed. Fix iterations: 0. |
|
Pipeline complete — code review and security review both passed. Fix iterations: 0. |
|
Pipeline complete — code review and security review both passed. Fix iterations: 0. |
|
Pipeline complete — code review and security review both passed. Fix iterations: 0. |
|
Pipeline complete — code review and security review both passed. Fix iterations: 0. |
|
Pipeline complete — code review and security review both passed. Fix iterations: 0. |
|
Pipeline complete — code review and security review both passed. Fix iterations: 0. |
Closes #15
Summary
Implements the full governance agenda lifecycle for Decidesk (p2-agenda-management). Adds a stateless
AgendaServicewith four core operations: publishing an agenda (with participant notifications), BOB phase tracking for discussion/decision items, batch-processing consent items (hamerstukken), and drag-and-drop reordering. The backend exposes four REST endpoints and the frontend gains a fully accessibleAgendaBuildercomponent, an extendedAgendaItemDetailview, a live-meetingLiveMeetingview, and conflict-of-interest declaration support — all built exclusively on platform services per ADR-012.Spec Reference
openspec/changes/p2-agenda-management/design.mdChanges
lib/Service/AgendaService.php— new stateless service:publishAgenda,advanceBobPhase,processHamerstukken,reorderItemswith full @SPEC PHPDoc traceabilitylib/Controller/AgendaController.php— thin REST controller exposing 4 agenda lifecycle endpointsappinfo/routes.php— 4 new routes registered before catch-all wildcardphpstan.neon— added ignore rules forOCA\OpenRegister\type stubssrc/components/AgendaBuilder.vue— drag-and-drop agenda builder with keyboard accessibility, ARIA labels, total duration, proposal inbox, recurring items, spokesperson assignment, and COI badgesrc/views/MeetingDetail.vue— extended with publish/revise buttons, AgendaBuilder, COI declarations section, export, and live meeting linksrc/views/AgendaItemDetail.vue— extended with BOB phase timeline, COI declaration, motion linking, spokesperson display, and itemType badgesrc/views/LiveMeeting.vue— new live-meeting view with auto-refresh (30s), hamerstukken section, BOB phase panel, chair activate controlssrc/router/index.js— addedLiveMeetingroute at/meetings/:id/livel10n/nl.json— 60+ new Dutch translation keysl10n/en.json— 60+ new English translation keysTest Coverage
tests/Unit/Service/AgendaServiceTest.php— 6 PHPUnit test methods covering all 4 service methods: validation, phase transitions, batch processing, sequential numberingtests/integration/agenda.json— 5 Newman/Postman test cases for all 4 API endpoints including error paths🤖 Generated with Claude Code