Skip to content

Back to Six — wave 2: facet composition (meeting/decision/organisation), register-detail widgets + wave-3 specs - #722

Open
rubenvdlinde wants to merge 26 commits into
developmentfrom
refactor/back-to-six
Open

Back to Six — wave 2: facet composition (meeting/decision/organisation), register-detail widgets + wave-3 specs#722
rubenvdlinde wants to merge 26 commits into
developmentfrom
refactor/back-to-six

Conversation

@rubenvdlinde

Copy link
Copy Markdown
Contributor

What

Wave 2 of the Back to Six refactor (follows #711), plus the wave-3 OpenSpec artifacts.

Applied (wave 2)

  • meeting-facet-composition — MeetingDetail gains five facets: oral questions (targetMeeting), interpellations (browse-only per domain), proxy authorizations (add-in-context), kascommissie (assoc-mode-gated registry tab), routed documents (two-hop agenda join). Logic extracted to plain modules; 24 new vitest tests.
  • organisation-facet-composition — GovernanceBodyDetail gains eight declarative facets; GovernanceBody gains bodyType=faction + parentBody (ADR-006 discriminator, no parallel Fractie schema) with faction seed data.
  • register-detail-optimisation — version-timeline, delegation-chain and confidentiality-status widgets on the four register detail pages via the shared widget registry; date-format and column-key (bekrachtigingDeadlineratificationDeadline) fixes; 26 vitest tests; 34 l10n keys with Dutch translations.
  • (decision-facet-composition's 8 DecisionDetail widgets already merged in Back to Six — wave 1: nav collapse to ADR-004's six clusters, ceiling gate, organisation goals, appointment decisions #711.)
  • Drive-by fix: Nevenfuncties index column bezoldigdremunerated (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/-code chain (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

  • Full vitest suite 346/346 · Ajv manifest PASS · nav-ceiling gate exit 0 (6 primary, every fragment placed) · l10n check OK · all changes openspec validate --strict
  • Live verification of the composed pages is deferred to this PR's Playwright e2e legs (local dev box was under heavy load from parallel programmes)

🤖 Generated with Claude Code

…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.
@github-actions

Copy link
Copy Markdown
Contributor

Quality Report — ConductionNL/decidesk @ e60ecd6

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.

@github-actions

Copy link
Copy Markdown
Contributor

Quality Report — ConductionNL/decidesk @ 4c4ef29

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.
@github-actions

Copy link
Copy Markdown
Contributor

Quality Report — ConductionNL/decidesk @ 947870b

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.
@github-actions

Copy link
Copy Markdown
Contributor

Quality Report — ConductionNL/decidesk @ 4c2b412

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.
@github-actions

Copy link
Copy Markdown
Contributor

Quality Report — ConductionNL/decidesk @ 0f00d70

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.
@github-actions

Copy link
Copy Markdown
Contributor

Quality Report — ConductionNL/decidesk @ 3b7b89c

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.
@github-actions

Copy link
Copy Markdown
Contributor

Quality Report — ConductionNL/decidesk @ 11b4461

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.
@github-actions

Copy link
Copy Markdown
Contributor

Quality Report — ConductionNL/decidesk @ 0761b4f

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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants