Back to Six — wave 2: facet composition (meeting/decision/organisation), register-detail widgets + wave-3 specs - #722
Back to Six — wave 2: facet composition (meeting/decision/organisation), register-detail widgets + wave-3 specs#722rubenvdlinde wants to merge 26 commits into
Conversation
…es, delegation chain, confidentiality status Three detail-page widgets (RegisterVersionTimelineWidget, DelegationChainWidget, ConfidentialityStatusTimelineWidget) registered via the shared dashboardWidgetRegistry; four register fragments wired; regeling date-format hint fixed; bekrachtigingDeadline naming drift fixed; 26 vitest tests; 34 l10n keys extracted + Dutch translations. Honestly withheld: GoverningDocuments current-in-force index column — the schema has no such property and faking the column would be the exact silent-mismatch anti-pattern; tracked as a wave-3 schema follow-up.
… facets
Three declarative object-list widgets (oral questions via targetMeeting,
interpellations browse-only via behandeldIn, proxy authorizations with
add-in-context) + two thin registry tabs (kascommissie assoc-mode gate;
routed-documents two-hop join), logic extracted to plain modules with
24 vitest tests (suite 346/346). Components read object context via the
cnObjectContext inject — the manifest slot mechanism passes only
{item,widget}, and the sibling tabs' objectId prop appears to never
receive a value (pre-existing defect, flagged for the debt pass).
…ion verdict, model-debt chain unified-decision-templates (schema head of a 3-change chain; VvE templates fold as builtin seeds); consultation-discriminator (measured verdict: 7-13% symmetric field overlap ⇒ NO fold, both schemas exempted under ADR-006's escape clause — the UI unification already lives on the decision hub); model-debt-cleanup-schema+code chain (undeclared Decision joins declared, Participant refs retargeted with a lossless nextcloudUserId crosswalk per judge amendment, BoardProxy folds, COI kept separate with evidence, slug hygiene as data migrations, Members-tab rewrite, GoverningDocument currentEffectiveDate; fractievoorzitter-fractie-koppeling draft formally superseded).
…debt specs + column-key fix GovernanceBodyDetail gains 8 declarative facets; GovernanceBody gains bodyType=faction + parentBody (ADR-006 discriminator) with faction seeds; 346/346 tests. Quick fix: Nevenfuncties index column key bezoldigd→remunerated (Dutch leftover silently resolving nothing — found during the facet apply). ux-debt-rendering change added: every item root-caused incl. the nc-vue liveUpdates unbounded-facets hang (two-repo defect, decidesk-side subscribe:false mitigation planned) and the fkResolve column widget that makes UUID resolution config-only.
…elint wrap it incompatibly
…— currentInwerkingtreding exists nowhere (verify-pass finding)
Quality Report — ConductionNL/decidesk @
|
| Check | PHP | Vue | Security | License | Tests |
|---|---|---|---|---|---|
| lint | ✅ | ||||
| phpcs | ✅ | ||||
| phpmd | ✅ | ||||
| psalm | ✅ | ||||
| phpstan | ✅ | ||||
| phpmetrics | ✅ | ||||
| eslint | ✅ | ||||
| stylelint | ❌ | ||||
| build | ❌ | ||||
| check-manifest | ✅ | ||||
| check-nav-ceiling | ✅ | ||||
| test-l10n | ✅ | ||||
| format | ✅ | ||||
| composer | ✅ | ✅ 104/104 | |||
| npm | ✅ | ✅ 532/532 | |||
| app:check-code | ⏭️ | ||||
| info.xml | ❌ | ||||
| REUSE | ❌ | ||||
| PHPUnit | ❌ | ||||
| Newman | ❌ | ||||
| Playwright | ❌ | ||||
| Hydra gates | ❌ |
Quality workflow — 2026-08-19 17:12 UTC
Download the full PDF report from the workflow artifacts.
…osition); verify-pass notes
Quality Report — ConductionNL/decidesk @
|
| Check | PHP | Vue | Security | License | Tests |
|---|---|---|---|---|---|
| lint | ✅ | ||||
| phpcs | ✅ | ||||
| phpmd | ✅ | ||||
| psalm | ✅ | ||||
| phpstan | ✅ | ||||
| phpmetrics | ✅ | ||||
| eslint | ✅ | ||||
| stylelint | ✅ | ||||
| build | ✅ | ||||
| check-manifest | ✅ | ||||
| check-nav-ceiling | ✅ | ||||
| test-l10n | ✅ | ||||
| format | ✅ | ||||
| composer | ✅ | ✅ 104/104 | |||
| npm | ✅ | ✅ 532/532 | |||
| app:check-code | ⏭️ | ||||
| info.xml | ✅ | ||||
| REUSE | ❌ | ||||
| PHPUnit | ❌ | ||||
| Newman | ❌ | ||||
| Playwright | ❌ | ||||
| Hydra gates | ❌ |
Quality workflow — 2026-08-19 17:17 UTC
Download the full PDF report from the workflow artifacts.
…retargeted, slugs kebab-cased Decision declares meeting/agendaItem; Person gains nextcloudUserId (lossless crosswalk key); ConflictOfInterest→Membership, ProxyAuthorization grantor/holder→Person + proxyStatus; BoardProxy retired inactive; GoverningDocument.currentEffectiveDate; slugs adviceRequest→advice-request, proxyAuthorization→proxy-authorization across register+manifest with grep-proven completeness (two documented property-name/enum false positives untouched). 346/346, Ajv PASS, gate 0.
…ate concept DecisionTemplate schema (state machine, voting rule, quorum, urgency, checklist, templateCategory) with 13 builtin seeds; ProcessTemplate/ VveDecisionTemplate/ModelreglementPreset superseded (active:false, the BoardProxy precedent); additive VveConfiguration retarget (ADR-037 merges cannot delete); idempotent IRepairStep migration + 6 tests (migration suite 24/24; statics clean).
…old verdict recorded ADR-006 addendum with the pairwise overlap table (7-13%, symmetric) and four qualitative signals; the three consultation schemas cross-reference their exemption status (description+version only). UI unification lives on the decision hub; storage stays honest.
Quality Report — ConductionNL/decidesk @
|
| Check | PHP | Vue | Security | License | Tests |
|---|---|---|---|---|---|
| lint | ✅ | ||||
| phpcs | ✅ | ||||
| phpmd | ✅ | ||||
| psalm | ✅ | ||||
| phpstan | ✅ | ||||
| phpmetrics | ✅ | ||||
| eslint | ✅ | ||||
| stylelint | ✅ | ||||
| build | ✅ | ||||
| check-manifest | ✅ | ||||
| check-nav-ceiling | ✅ | ||||
| test-l10n | ✅ | ||||
| format | ✅ | ||||
| composer | ✅ | ✅ 104/104 | |||
| npm | ✅ | ✅ 532/532 | |||
| app:check-code | ⏭️ | ||||
| info.xml | ✅ | ||||
| REUSE | ❌ | ||||
| PHPUnit | ✅ | ||||
| Newman | ✅ | ||||
| Playwright | ❌ | ||||
| Hydra gates | ❌ |
Quality workflow — 2026-08-19 17:55 UTC
Download the full PDF report from the workflow artifacts.
…, plainYear, spec tags fkResolve reference columns across 21 fragments (plus five more silent column-key mismatches found and fixed); plainYear formatter via CnAppRoot formatters; subscribe:false on the two stuck-loading pages; quick-filter regression guard; walkthrough copy for the six clusters; citeertitel→citationTitle key fix; ACTIVE e2e fixture leak closed (five schemas missing from teardown order); @SPEC tags on all 49 flagged widget methods (gate-16 PASS); ShieldLockOutline→ ShieldCheckOutline (gate-55); DecisionTemplate.migratedFrom.sourceUuid de-relation-shaped as a provenance marker (gate-54); agenda sub-items e2e budget 20s→35s with rationale (twin-run flake on the legitimately heavier MeetingDetail). vitest 351/351; diff-scoped hydra-gates: zero failures.
Verified: consultation-discriminator PASS, model-debt-cleanup-schema PASS, unified-decision-templates + ux-debt-rendering PASS-WITH-NOTES. Both notes closed: the VveConfiguration spec scenario now names the NEW seed object (OR seed import is create-only, so patching an already-imported object is inert — demonstrating on a fresh object is the only honest proof), and Task 5 records the filed upstream issue nextcloud-vue#703. Fragment filename drift 67→68 corrected in all four unified-decision-templates docs.
…tired end to end PHP: ParticipantToPersonMembershipResolver (nextcloudUserId→email→ create match order), two idempotent repair steps (ConflictOfInterest boardMember repoint, BoardProxy→proxy-authorization row migration), ProxyVoteService on the kebab-case slug, ConflictOfInterest params renamed to membershipId. Vue: GovernanceBodyMembersTab + four member dialogs rewritten from the deprecated participant schema to membership+Person; removal now ends a membership (endDate) instead of nulling a pointer; role writes use an explicit field allowlist so display-only fields cannot leak onto the object. Helper logic extracted to useRelationStore for testability. vitest 367/367; all 49 applicable hydra gates pass; openspec valid.
… errors cleared decidesk calls FileService::createFolder/getFiles (VotingRoundCloser, TranscriptionSourceResolver, TranscriptRetentionJob) but tests/Stubs had no stand-in, so 87 unit tests ERRORED locally while CI stayed green — app CI clones the real OpenRegister at ref: development, so the gap was invisible there. Signatures copied verbatim from openregister@a8cc77387 per the tests/Stubs parity contract; only the two methods decidesk actually calls are declared, and both throw if a test forgets to mock them rather than returning a shape production cannot emit. Full suite now: 1019 tests, 4214 assertions, 0 failures, 0 errors (was 87 errors + 1 failure).
Archived 2026-08-19: meeting/decision/organisation-facet-composition, register-detail-optimisation, unified-decision-templates, consultation-discriminator, model-debt-cleanup-schema + -code, ux-debt-rendering. Spec deltas synced into openspec/specs/ by the archive step. vitest 367/367, Ajv PASS, nav-ceiling exit 0 after the sync.
Five specs asserting real rendered content (widget heading plus a row or the declared empty-state, never a bare 200): meeting facets incl. the assoc-only kascommissie gate asserted ABSENT in gov mode, decision facets incl. a created toezegging appearing under Commitments, organisation factions, the three register catalog widgets against existing seeds, and the Goals index/detail. Fixtures are deleted by id in finally blocks rather than routed through governance-fixture's TEARDOWN_ORDER, which lists neither mondelinge-vraag nor toezegging and would leak them.
Live verification found the version-timeline widget rendering an empty shell on RegelingDetail: OpenRegister's seed importer stores $ref values as raw SLUG strings, so regeling-versie rows carry regulation: "afvalstoffenverordening-amsterdam" while the parent's id is a UUID — filtering on the id alone matched nothing. Both the version-timeline and delegation-chain widgets now retry once on the parent's @self.slug. No static check could see this; only a rendered page could. The five new facet specs get 120s budgets and 45s content waits (measured: app mounts only after an initializeStores round trip, then 16-18 widget queries at ~1s each). No assertion was weakened — the kascommissie absence check now explicitly depends on a sibling assertion proving the grid finished rendering first.
Archiving moved openspec/changes/model-debt-cleanup-code/ under archive/2026-08-19-, dangling 21 @SPEC tags across lib/ and tests/. Repointed to the archived path, and corrected the migration.md anchors to the slug the gate's own checker computes from the class-FQN headings (ocadecideskrepair...) rather than the short name that never matched. gate-46 spec-anchor-existence: PASS.
…s post-archive 196 synced scenarios resolved: ~22 cite a real Playwright/PHPUnit test, ~32 are schema-shape assertions with no UI surface, ~57 are labelled honestly as genuine e2e debt rather than papered over, the rest are ADR/process or library-boundary cases. The five new facet specs' @e2e anchors are repointed from the pre-archival change paths to the canonical openspec/specs/ paths the gate actually resolves, so their coverage counts as coverage instead of an explained exclusion. ALL 60 APPLICABLE GATES PASSED — and all 60 ran.
Quality Report — ConductionNL/decidesk @
|
| Check | PHP | Vue | Security | License | Tests |
|---|---|---|---|---|---|
| lint | ✅ | ||||
| phpcs | ✅ | ||||
| phpmd | ✅ | ||||
| psalm | ✅ | ||||
| phpstan | ✅ | ||||
| phpmetrics | ✅ | ||||
| eslint | ✅ | ||||
| stylelint | ✅ | ||||
| build | ✅ | ||||
| check-manifest | ✅ | ||||
| check-nav-ceiling | ✅ | ||||
| test-l10n | ✅ | ||||
| format | ✅ | ||||
| composer | ✅ | ✅ 104/104 | |||
| npm | ✅ | ✅ 532/532 | |||
| app:check-code | ⏭️ | ||||
| info.xml | ✅ | ||||
| REUSE | ❌ | ||||
| PHPUnit | ❌ | ||||
| Newman | ❌ | ||||
| Playwright | ❌ | ||||
| Hydra gates | ❌ |
Quality workflow — 2026-08-19 21:59 UTC
Download the full PDF report from the workflow artifacts.
CI's lint-check failed with exit 2 on 'suppressions left that do not occur anymore' — not on the 238 warnings, which are pre-existing and allowed. The stale entry was src/modals/MemberAddDialog.vue: rewriting it from the deprecated participant schema to membership+Person removed the violation the suppression covered. Pruned via --prune-suppressions (touches only the suppressions file, never source, so no suppressed violation is silently 'fixed' along with it). eslint src now exits 0.
Quality Report — ConductionNL/decidesk @
|
| Check | PHP | Vue | Security | License | Tests |
|---|---|---|---|---|---|
| lint | ⏭️ | ||||
| phpcs | ⏭️ | ||||
| phpmd | ⏭️ | ||||
| psalm | ⏭️ | ||||
| phpstan | ⏭️ | ||||
| phpmetrics | ⏭️ | ||||
| eslint | ⏭️ | ||||
| stylelint | ⏭️ | ||||
| build | ⏭️ | ||||
| composer | ⏭️ | ⏭️ | |||
| npm | ⏭️ | ⏭️ | |||
| app:check-code | ⏭️ | ||||
| info.xml | ⏭️ | ||||
| REUSE | ⏭️ | ||||
| PHPUnit | ❌ | ||||
| Newman | ❌ | ||||
| Playwright | ❌ | ||||
| Hydra gates | ❌ |
Quality workflow — 2026-08-19 22:28 UTC
Download the full PDF report from the workflow artifacts.
…s errors CI caught three phpmd violations on one method — cyclomatic 13 (max 10), NPath 514 (max 200), 102 lines (max 100). Three thresholds breached by one method means decompose, not raise limits: run() now delegates to resolveRow() (parse + reference resolution, returns null to skip, logs every unresolvable row) and saveMigratedRow() (persist + outcome logging), leaving the loop as counters and the idempotency key. Also fixes the two phpcs ERRORS CI reported (inline ternaries in the extracted method, and a lowercase-leading inline comment in ProxyVoteService). Note the CI phpcs step's message is misleading — it says warnings are not counted, but phpcs exits 1 on warnings too; the real errors were only visible under --warning-severity=0. Local: phpcs 0 errors, phpmd 0, phpstan OK, PHPUnit 1019/0 failures.
Quality Report — ConductionNL/decidesk @
|
| Check | PHP | Vue | Security | License | Tests |
|---|---|---|---|---|---|
| lint | ✅ | ||||
| phpcs | ❌ | ||||
| phpmd | ❌ | ||||
| psalm | ✅ | ||||
| phpstan | ✅ | ||||
| phpmetrics | ✅ | ||||
| eslint | ✅ | ||||
| stylelint | ✅ | ||||
| build | ✅ | ||||
| check-manifest | ✅ | ||||
| check-nav-ceiling | ✅ | ||||
| test-l10n | ✅ | ||||
| format | ✅ | ||||
| composer | ✅ | ✅ 104/104 | |||
| npm | ✅ | ✅ 538/538 | |||
| app:check-code | ⏭️ | ||||
| info.xml | ✅ | ||||
| REUSE | ❌ | ||||
| PHPUnit | ❌ | ||||
| Newman | ❌ | ||||
| Playwright | ❌ | ||||
| Hydra gates | ❌ |
Quality workflow — 2026-08-19 23:00 UTC
Download the full PDF report from the workflow artifacts.
CI's coverage guard failed because the denominator nearly doubled (399→784 statements): +1591 lib lines added, only 48 deleted, so this is the added-code case, not the deleting-dead-code false positive the guard warns about. Answer is real branch coverage, not filler. 54 new tests (1019→1073, 0 failures) across the crosswalk resolver, both repair steps, ProxyVoteService, ConflictOfInterestService and its controller: exception paths, unresolvable-reference skips, idempotency short-circuits, entity-normalisation fallbacks, and cap/authorisation branches. Every test is falsifiable; branches that are unreachable under the typed ObjectServiceInterface contract were left uncovered and documented rather than faked. Noted: lib/Migration/ is excluded from coverage in phpunit.xml, so the MigrateLegacyTemplates tests (also added) cannot move the ratchet — they are kept for their own value.
…rated' Found while a test-writing agent walked the branches: isParticipant() swallowed any Throwable and returned false, which the caller counted as 'already migrated'. So a transient OpenRegister failure during the repair retired the row permanently — on a step whose entire value is that re-running it finishes the job. isParticipant() now returns null for UNKNOWN (and logs it); the caller counts unknown as skipped, leaving the row eligible next run. run() regained a branch, so the row-normalisation guard is extracted to rowKeys() rather than raising the cyclomatic threshold. The test that asserted the old semantics now asserts the corrected ones, with the reasoning inline so it is not 'tidied' back. phpcs lib 0 errors, phpmd 0, phpstan OK, PHPUnit 1073/0 failures.
Quality Report — ConductionNL/decidesk @
|
| Check | PHP | Vue | Security | License | Tests |
|---|---|---|---|---|---|
| lint | ✅ | ||||
| phpcs | ✅ | ||||
| phpmd | ✅ | ||||
| psalm | ✅ | ||||
| phpstan | ✅ | ||||
| phpmetrics | ✅ | ||||
| eslint | ✅ | ||||
| stylelint | ✅ | ||||
| build | ✅ | ||||
| check-manifest | ✅ | ||||
| check-nav-ceiling | ✅ | ||||
| test-l10n | ✅ | ||||
| format | ✅ | ||||
| composer | ✅ | ✅ 104/104 | |||
| npm | ✅ | ✅ 538/538 | |||
| app:check-code | ⏭️ | ||||
| info.xml | ✅ | ||||
| REUSE | ❌ | ||||
| PHPUnit | ❌ | ||||
| Newman | ❌ | ||||
| Playwright | ❌ | ||||
| Hydra gates | ❌ |
Quality workflow — 2026-08-19 23:38 UTC
Download the full PDF report from the workflow artifacts.
… false) Newman caught a real regression from model-debt-cleanup-schema, not a stale assertion: fragment 63's proxy-authorization authorization block names only `read`, and a block that names some actions closes every action it does not name — so OpenRegister's RBAC began refusing `create` for every non-admin and `update` for every non-owner, breaking proxy registration and revoke-by-grantor. register()/transition() now pass `_rbac: false` (a real parameter on ObjectServiceInterface::saveObject, verified against the contract) to declare that authorization already ran: this service enforces the finer-grained domain rule RBAC cannot express — self-delegation OR chair/clerk — via isAuthorizedToRegister()/isAuthorizedForTransition() before either write. Widening the schema block to blanket `create` would have been the wrong fix: it would let any user register a proxy for anyone. Collections updated for the Person-based grantor/holder model. Local: the four proxy assertions CI flagged now pass; phpcs 0, phpmd 0, phpstan OK, 27 proxy unit tests green.
Quality Report — ConductionNL/decidesk @
|
| Check | PHP | Vue | Security | License | Tests |
|---|---|---|---|---|---|
| lint | ✅ | ||||
| phpcs | ✅ | ||||
| phpmd | ✅ | ||||
| psalm | ✅ | ||||
| phpstan | ✅ | ||||
| phpmetrics | ✅ | ||||
| eslint | ✅ | ||||
| stylelint | ✅ | ||||
| build | ✅ | ||||
| check-manifest | ✅ | ||||
| check-nav-ceiling | ✅ | ||||
| test-l10n | ✅ | ||||
| format | ✅ | ||||
| composer | ✅ | ✅ 104/104 | |||
| npm | ✅ | ✅ 538/538 | |||
| app:check-code | ⏭️ | ||||
| info.xml | ✅ | ||||
| REUSE | ❌ | ||||
| PHPUnit | ✅ | ||||
| Newman | ❌ | ||||
| Playwright | ❌ | ||||
| Hydra gates | ❌ |
Quality workflow — 2026-08-19 23:56 UTC
Download the full PDF report from the workflow artifacts.
What
Wave 2 of the Back to Six refactor (follows #711), plus the wave-3 OpenSpec artifacts.
Applied (wave 2)
bodyType=faction+parentBody(ADR-006 discriminator, no parallel Fractie schema) with faction seed data.bekrachtigingDeadline→ratificationDeadline) fixes; 26 vitest tests; 34 l10n keys with Dutch translations.bezoldigd→remunerated(Dutch leftover key silently resolving nothing).Spec'd, not yet applied (wave 3 artifacts, apply-ready)
unified-decision-templates(schema head of a 3-change chain) ·consultation-discriminator(measured verdict: no fold — 7–13% symmetric field overlap, ADR-006 escape clause) ·model-debt-cleanup-schema/-codechain (undeclared Decision joins, Participant-shim retirement with a lossless nextcloudUserId crosswalk, BoardProxy fold, slug hygiene, Members-tab rewrite) ·ux-debt-rendering(every rendering-debt item root-caused, incl. a two-repo nc-vue/OpenRegister unbounded-facets hang).Verification
openspec validate --strict🤖 Generated with Claude Code