T2: Minutes and Decisions Core Features#116
Closed
rubenvdlinde wants to merge 767 commits into
Closed
Conversation
…ment feat: meeting lifecycle state machine (p2-meeting-management)
…and security reviews (#18) - appinfo/info.xml: remove duplicate <background-jobs> blocks (three → one) - appinfo/routes.php: rename route voting#create → voting#open to match VotingController::open() - src/router/index.js: remove duplicate const declarations (MotionDetail, AmendmentDetail) and duplicate routes - tests/Unit/Service/VotingServiceTest.php: fix mock addMethods (getObject/findObjects instead of find/findAll), remove motionService from VotingService constructor call, add missing meetingId arg to openVotingRound, fix exception messages to Dutch, fix grantProxy exception class to InvalidArgumentException, rewrite closeVotingRound test to verify motion lifecycle transition - lib/Controller/MotionController.php: add requireChairOrSecretary() guard to coSignRequest and budgetImpact; always derive displayName from authenticated session in coSignConfirm (prevent identity spoofing) - lib/Service/MotionService.php: add relations.motion filter to detectConflicts findAll query (prevent full-table scan) - src/components/VotingRoundPanel.vue: fix isRoundOpen to compare closedAt as Date (allow future deadline rounds to remain open) - lib/Service/OriPublicationService.php: replace file_get_contents with IClientService for HTTP (SSRF defence, timeout control); add URL validation (HTTPS-only, no RFC-1918/loopback); track oriPublishedAt on publish success so getPublicationStatus distinguishes published from merely closed Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…motion-and-voting
…ew + Security Review (#17) Code Review CRITICALs: - DecisionDetail.vue: add motion relation card (task 6.2) - DecisionDetail.vue: add text property to propertyItems (task 6.2) - ActionItemDetail.vue: add decision + meeting relation cards (task 7.2) - ActionItemDetail.vue: add description to propertyItems (task 7.2) Code Review WARNINGs: - MinutesGenerationService: resolveMeeting() re-throws fetch exception as RuntimeException(503) - MinutesGenerationService: transition() returns saveObject() return value (not stale local array) - MinutesGenerationService: fetchRelatedObjects() uses paginated loop (matches OverdueActionItemsJob) - MinutesControllerTest: add testTransitionWhenOpenRegisterUnavailableReturns503() (task 9.3) Security Review WARNINGs: - MinutesGenerationService: enforce OpenRegister session-based ACL via setRegister/setSchema+find(id:) in both generateDraft() and transition() — matches MeetingService pattern (OWASP A01) - Add DecisionController::publish() with server-side admin check + outcome/isPublished validation (OWASP A01 / ADR-005 frontend-only bypass fix); update decisions.js + DecisionDetail.vue to call POST /api/decisions/{id}/publish instead of generic CRUD PUT
- appinfo/info.xml: remove duplicate <background-jobs> blocks (triple-register bug) - appinfo/routes.php: rename voting#create → voting#open to match controller method - src/router/index.js: remove duplicate const declarations and duplicate route entries - lib/Controller/MotionController.php: add requireChairOrSecretary() guard to coSignRequest and budgetImpact; always derive displayName from session in coSignConfirm (prevent spoofing) - lib/Service/MotionService.php: add relations.motion filter to detectConflicts() findAll call - lib/Service/OriPublicationService.php: fix getPublicationStatus() to check oriPublishedAt instead of closedAt; stamp oriPublishedAt on successful publish; add SSRF validation (HTTPS-only, block RFC-1918/loopback addresses) - src/components/VotingRoundPanel.vue: fix isRoundOpen to compare closedAt date vs now - tests/Stubs/ObjectService.php: add stub with correct named-parameter signatures - tests/bootstrap-unit.php: load ObjectService stub for unit tests - tests/Unit/Service/VotingServiceTest.php: fix constructor call (remove nonexistent motionService param); fix openVotingRound arg count; fix Dutch exception messages; update mock from stdClass/addMethods to ObjectService stub; fix findObjects return structure - tests/Unit/Service/MotionServiceTest.php: update mock to ObjectService stub; fix saveObject with() callback positions to match named-parameter call order - src/views/AmendmentDetail.vue: fix unnecessarily quoted Accept header key (ESLint) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…minutes-and-decisions
- AgendaController: return HTTP 401 (not 403) for unauthenticated requests - AgendaController: wrap entire advanceBobPhase body in try/catch(\Throwable) - AgendaService: normalize participant objects via toArray() before array access - phpstan.neon: scope broad wildcard ignore to specific \$calendarEventService - tests/integration/agenda.json: add 401 unauthenticated test per endpoint - appinfo/routes.php: remove dead metrics#index and health#index routes - SettingsController: add #[NoAdminRequired] PHP 8 attribute to index() Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Remove duplicate oriPublishedAt stamp block and redundant SSRF validation in getEndpoint() introduced by merge — isValidOriEndpoint() already covers the check and is called from publish(). Resolve trivial whitespace conflicts in routes.php, MotionController.php, and MotionService.php. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…and security reviews (#18) - [CRITICAL] Rename route voting#grantProxy → voting#proxy to match VotingController::proxy() method name; prevents 404 on grant-proxy endpoint - [CRITICAL] Fix integration test assertions for open (201) and cast (201) voting-round endpoints; was checking for 200 - [WARNING] Add requesttoken CSRF header to AmendmentDetail.vue transition() fetch; matches MotionDetail.vue pattern - [WARNING] Populate participantCount in VotingRoundPanel.fetchCurrentRound() by fetching participants for the meeting in parallel with the round fetch - [WARNING] Delete src/views/MotionIndex.vue — unreachable duplicate of Motions.vue (not imported in router) - [WARNING] Fix SSRF bypass in OriPublicationService.isValidOriEndpoint(): return false when gethostbyname() fails to resolve instead of allowing through - [WARNING] Omit participant relation from Vote objects when isSecret=true to preserve secret ballot anonymity (VotingService.castVote) - [WARNING] Validate participantId against OpenRegister before castVote() in MailReplyHandler to prevent unverified _mail metadata from casting votes (OWASP A07:2021) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…nts (#17) Extends the security fixes from iteration 3 (which already added the DecisionController publish endpoint and server-side validation): - MinutesController: generateDraft() now requires admin (prevents cross-tenant information disclosure — OWASP A01 / ADR-005) - MinutesController: RESTRICTED_TRANSITIONS now includes 'review', so ALL lifecycle transitions require admin (not just approved/signed/published) - MinutesControllerTest: add isAdmin=true mock to all generateDraft tests that expect the service to be reached (tests were silently passing before because the admin check did not exist) - MinutesControllerTest: add testGenerateDraftByNonAdminReturns403() Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…ons' into feature/17/p2-minutes-and-decisions
…reviews (#15) - AgendaController: return HTTP 401 (not 403) for unauthenticated requests in requireChairOrAdmin - AgendaController: wrap full advanceBobPhase body in try/catch with \Throwable fallback matching other methods - AgendaService: normalize participant objects via toArray() in publishAgenda loop, matching all other data loops - phpstan.neon: scope unused-property ignore to $calendarEventService only; remove wildcard that suppressed all AgendaService property warnings - SettingsController: add #[NoAdminRequired] PHP attribute to index() alongside existing @NoAdminRequired docblock - tests/integration/agenda.json: add 401 (unauthenticated) and 403 (unprivileged user) test cases for all four protected endpoints Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Resolved merge conflicts with existing fix iteration 5 commit on remote. Added 403 (unprivileged user) test cases alongside 401 (unauthenticated) tests for all four protected endpoints — both scenarios now covered per the reviewer's requirement to test requireChairOrAdmin at integration level. Retained meeting#lifecycle route from development branch merge. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* feat: Add OpenSpec change p2-minutes-and-decisions-core-t3 from Specter Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * feat: Add OpenSpec change p2-minutes-and-decisions-core-t3 from Specter * chore: add hydra.json for p2-minutes-and-decisions-core-t3 * chore(spec): mark p2-minutes-and-decisions-core-t3 as extension of p2-minutes-and-decisions * chore(spec): mark p2-minutes-and-decisions-core-t3 as extension of p2-minutes-and-decisions * chore(spec): mark p2-minutes-and-decisions-core-t3 as extension of p2-minutes-and-decisions --------- Co-authored-by: Specter Intelligence <specter@conduction.nl> Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
* feat: Add OpenSpec change p2-minutes-and-decisions-other-t1 from Specter Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * feat: Add OpenSpec change p2-minutes-and-decisions-other-t1 from Specter * chore: add hydra.json for p2-minutes-and-decisions-other-t1 * chore(spec): mark p2-minutes-and-decisions-other-t1 as extension of p2-minutes-and-decisions * chore(spec): mark p2-minutes-and-decisions-other-t1 as extension of p2-minutes-and-decisions * chore(spec): mark p2-minutes-and-decisions-other-t1 as extension of p2-minutes-and-decisions --------- Co-authored-by: Specter Intelligence <specter@conduction.nl> Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
* feat: Add OpenSpec change p2-minutes-and-decisions-other-t2 from Specter Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * feat: Add OpenSpec change p2-minutes-and-decisions-other-t2 from Specter * chore: add hydra.json for p2-minutes-and-decisions-other-t2 * chore(spec): mark p2-minutes-and-decisions-other-t2 as extension of p2-minutes-and-decisions * chore(spec): mark p2-minutes-and-decisions-other-t2 as extension of p2-minutes-and-decisions * chore(spec): mark p2-minutes-and-decisions-other-t2 as extension of p2-minutes-and-decisions --------- Co-authored-by: Specter Intelligence <specter@conduction.nl> Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
* feat: Add OpenSpec change p2-motion-and-voting from Specter * chore: add hydra.json for p2-motion-and-voting --------- Co-authored-by: Specter Intelligence <specter@conduction.nl>
* feat: Add OpenSpec change p2-motion-and-voting-core-t1 from Specter * feat: Add OpenSpec change p2-motion-and-voting-core-t1 from Specter * chore: add hydra.json for p2-motion-and-voting-core-t1 --------- Co-authored-by: Specter Intelligence <specter@conduction.nl>
* feat: Add OpenSpec change p2-motion-and-voting-core-t2 from Specter Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * feat: Add OpenSpec change p2-motion-and-voting-core-t2 from Specter * chore: add hydra.json for p2-motion-and-voting-core-t2 --------- Co-authored-by: Specter Intelligence <specter@conduction.nl> Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
* feat: Add OpenSpec change p2-motion-and-voting-core-t3 from Specter Adds proposal, design, specs, and tasks for Motion and Voting Core T3: motion execution tracking (lifecycle extension: execution-pending → executing → executed), vote anonymisation (GDPR/AVG compliance), quorum calculator service + API endpoint, and written/circular resolution approval (BW 2:238). No ADR-000 schema changes — all features use existing entity fields and built-in OpenRegister mechanisms. * feat: Add OpenSpec change p2-motion-and-voting-core-t3 from Specter * chore: add hydra.json for p2-motion-and-voting-core-t3 --------- Co-authored-by: Specter Intelligence <specter@conduction.nl>
* feat: Add OpenSpec change p2-motion-and-voting-other-t1 from Specter * chore: add hydra.json for p2-motion-and-voting-other-t1 --------- Co-authored-by: Specter Intelligence <specter@conduction.nl>
* feat: Add OpenSpec change p3-citizen-participation from Specter * chore: add hydra.json for p3-citizen-participation --------- Co-authored-by: Specter Intelligence <specter@conduction.nl>
* feat: Add OpenSpec change p4-integration from Specter * chore: add hydra.json for p4-integration --------- Co-authored-by: Specter Intelligence <specter@conduction.nl>
* feat: Add OpenSpec change p4-reporting-and-analytics from Specter * chore: add hydra.json for p4-reporting-and-analytics --------- Co-authored-by: Specter Intelligence <specter@conduction.nl>
* feat: Add OpenSpec change p4-standards-hardening from Specter * chore: add hydra.json for p4-standards-hardening --------- Co-authored-by: Specter Intelligence <specter@conduction.nl>
…issues Hydra's supervisor dep-check walks hydra.json's 'issue' field to verify a dependency is merged (closed on GitHub). Two P2 umbrella specs had `issue: null` even though matching issues exist and are closed: - p2-minutes-and-decisions → #17 (closed 2026-04-14 as COMPLETED) - p2-motion-and-voting → #72 (open; required for tiered impls to unblock, and we want it buildable) Every tiered spec under these umbrellas (p2-minutes-and-decisions-*, p2-motion-and-voting-*) was reporting 'deps not merged' and never dispatching — purely because the dep resolver couldn't find an issue to check. No spec content changed. Only the 'issue' field in two hydra.json files.
fix(openspec): link p2 umbrella specs to their GitHub issues
Follow-up to #112. The intelligence DB and development hydra.json files were missing issue linkage for three tier specs even though matching GitHub issues exist: - p2-meeting-management-core-t3 -> #61 - p2-meeting-management-other-t2 -> #42 - p2-minutes-and-decisions-core-t1 -> #21 When the umbrella specs (p2-meeting-management, p2-motion-and-voting) eventually merge, the supervisor's dep-check walks down to these tier specs. With issue:null they'd report 'deps not merged' forever, exactly the block pattern PR #112 fixed for the umbrellas. No spec content changed — only the 'issue' field in three hydra.json files.
fix(openspec): link 3 more p2 tier specs to their GitHub issues
…ydra (#115) App repos should carry ONLY repo-specific ADRs (in openspec/architecture/), not stale copies of hydra's org-wide ADRs. These copies had drifted — adr-004-frontend.md in this repo still said 'fetch() not axios' while hydra master says the opposite since commit e4cf8a2. That caused Hydra's code reviewer on decidesk#71 to flag a real ADR contradiction. Per user direction (2026-04-19): delete all per-repo copies of hydra's org-wide ADRs. Reviewer + builder containers already COPY the relevant ADRs from hydra into the image at build time — agents operating in the repo outside a container should read hydra master directly. openspec/architecture/ stays — that's where repo-specific ADRs (authored by Specter during research) should live.
Contributor
Quality Report — ConductionNL/decidesk @
|
| Check | PHP | Vue | Security | License | Tests |
|---|---|---|---|---|---|
| lint | ✅ | ||||
| phpcs | ❌ | ||||
| phpmd | ✅ | ||||
| psalm | ✅ | ||||
| phpstan | ✅ | ||||
| phpmetrics | ✅ | ||||
| eslint | ❌ | ||||
| stylelint | ❌ | ||||
| composer | ✅ | ✅ 100/100 | |||
| npm | ✅ | ✅ 215/215 | |||
| PHPUnit | ⏭️ | ||||
| Newman | ⏭️ | ||||
| Playwright | ⏭️ |
Quality workflow — 2026-04-19 14:55 UTC
Download the full PDF report from the workflow artifacts.
…subscriptions, version control, approval workflow, and portal publication (#73)
44289af to
eeaee86
Compare
Contributor
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 | ⏭️ |
Quality workflow — 2026-04-19 15:08 UTC
Download the full PDF report from the workflow artifacts.
…control, approval workflow, and portal publication (#73) - Add notification subscription toggle to Decision and Minutes detail views - Create MinutesVersionPanel component for version history and diff viewing - Implement Minutes approval workflow UI with dual sign-off (chair + secretary) - Add Decision portal publication UI with share link management - Add comprehensive Dutch and English translation keys for all new features - Fix DecisionControllerTest to include DecisionService constructor parameter Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
Contributor
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 | ⏭️ |
Quality workflow — 2026-04-19 15:15 UTC
Download the full PDF report from the workflow artifacts.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #73
Summary
Implements T2 collaboration layer: notification subscriptions, version control, approval workflow, portal publication, search integration, and reference provider.
Implementation Complete
✓ Backend services (DecisionNotificationService, MinutesVersionService, MinutesApprovalService, DecisionService)
✓ REST controllers and routes
✓ Frontend components (DecisionDetail, MinutesDetail, MinutesVersionPanel)
✓ Translations (EN/NL)
✓ Unit tests for all services
✓ Search provider and reference provider