Release: merge beta into main - #1576
Open
rubenvdlinde wants to merge 27 commits into
Open
Conversation
…190920 chore(sync): carry main back into beta
…0260830190930 chore(sync): carry beta back into development
vue-router 5 peers `vite: ^7.3.0 || ^8.0.0` and expects a Vite toolchain. These apps build with webpack, which cannot resolve it at all: the build dies on `Can't resolve 'vue-router'` from src and from @nextcloud/vue's own chunks. Adopting it is a Vite migration, not a version bump. Dependabot proposed it across 8 repositories in a single run, and merging any one of them takes that app's build from green to red with no code change that can fix it. versioniq already builds with Vite and is the natural pilot if the fleet does move. Lift this when an app's toolchain can actually take it.
#1536) The comment claimed these apps "cannot resolve it at all". That is not what the evidence shows, and a comment that overstates its case is worse than none: the next person reads it, tries vue-router 5 somewhere it works, and stops trusting the file. Measured across four apps: integriq and zaakafhandelapp fail their build on `Can't resolve 'vue-router'`, while openregister and learniq build clean on 5.3.0. The holdback still stands, for the honest reason rather than the overstated one: a major that breaks some apps and not others cannot be merged unattended, and the difference between them is not yet understood. No behaviour change. The ignore rule is unchanged; only the reasoning is.
…ride it (#1539) 🔴 SECURITY. An inspector could escalate an enforcement measure past the manager gate. `LhsRecommendationService::override()` took the whole recommendation row from the request and compared the requested intervention against the `recommendedIntervention` IN THAT ROW. So posting a body claiming the matrix had already recommended `bestuursdwang` made every lesser measure an "override-down", and the manager check never fired. The same `array_merge` also let the request restate `severity`, `behaviour`, `matrixVersion` and `recommendedBy` on the persisted row — so the audit record of what the matrix said would agree with whoever overrode it. The controller's `groupManager->isAdmin()` check was real and is untouched. It was guarding the wrong side of the comparison. Now the service takes an ID and reads the row back from OpenRegister, and merges only the five override fields onto it. `recommendationId` is the documented shape; a whole `recommendation` object is still accepted from callers that have not been updated, but ONLY its id is read. Eight new tests pin it, and they fail against the old behaviour: restoring the caller-supplied baseline turns two red, and rewriting one stored audit field turns another red. `testOverrideRejectsANonObjectRecommendationWith400` asserted the OPPOSITE of the new contract — "the engine expects the original row, not an id" — which was the vulnerability stated as a requirement; it is renamed and inverted rather than deleted, so the change of contract is visible. Storage moved to a new LhsRecommendationStore. That was forced by a measurement, not chosen for tidiness: the stored-row read took the service past the class-complexity threshold, and moving the reads and writes out is the answer that makes the service smaller rather than the one that silences the rule. The service now needs neither SettingsService nor a logger, so both leave its constructor. Also removes the `LHS recommendations` settings-menu entry. A recommendation is a per-enforcement audit record, not configuration — it belongs on the case, the way the audit trail does. The PAGES stay routable so existing deep links still resolve; only the menu entry goes. Verified: 2559 unit tests pass (1 pre-existing warning in ZgwMappingServiceTest, present on the base tree too), PHPCS/PHPMD/PHPStan/Psalm clean on every changed file, and the manifest still validates against the v2 schema.
npm ci failed with ERESOLVE, so every frontend job failed before running a single check. Majors had landed WITHOUT the peers that must move with them. These packages are a set: bumping one alone leaves another declaring a range the new version cannot satisfy, and npm rejects the whole tree rather than the single package. Fixing them individually walks the list, because each correction exposes the next conflict underneath. Realigned: @babel/core=^7.22.9 sass-loader=^17.0.0 Each range is the one npm itself demanded in the ERESOLVE it reported -- discovered by applying it and re-checking until resolution succeeded -- rather than a version chosen by hand. Measured across the fleet: nine apps were unresolvable on this pattern. Verified: the lockfile resolves from a clean tree, where it previously exited on ERESOLVE. Co-authored-by: Conduction Release Bot <release-bot@conduction.nl>
…the referral as a flow (#1531) * feat(bezwaar): hold advisory committees in the decision app, and run the referral as a flow migrate-committees-to-decidiq has been BLOCKED on two prerequisites. Both are now in place: decidiq#874 gave GovernanceBody the four fields a bezwaaradviescommissie needs, and decidiq's governance-body-events change gave it the typed command seam an in-process migration must use. This is dossiq's half. - CommitteeDelegationService dispatches GovernanceBodyRequestedEvent, guarded by class_exists across BOTH namespace spellings, and fails closed when the decision app is absent or did not handle the command. Shaped after ContractDecisionDelegationService, which is this app's working example of commanding the decision app by event. - MigrateCommitteesToDecidiq raises a body per existing committee and records the id back. It REFUSES to run when OpenRegister exposes no runAsSystem(), rather than falling back to a bare call like the shared trait does: a repair step has no session, OpenRegister then resolves the actor as Anonymous and refuses every write, and $output->warning() does not fail an upgrade — so the fallback is exactly the silent no-op the spec forbids. - dossiq.ensureCommittee is a flow node that does the same thing mid-run, and the seeded `Bezwaar advies` flow on bacAdviceRequest uses it as its first step: ensure the committee exists, the secretary opens deliberation, the chair issues the advice, each human step moving the request's status. - governanceBodyId on bezwaaradviescommissie is the mapping record. The roster is where the sharp edge was. `members[]` may also contain the chair, and the seam keys a seat on (person, body) — so sending a uid twice overwrites its first seat, and the committee silently loses its chair while every row still looks well-formed. Officers are seated first and seen uids skipped; testChairIsNotDemotedByAppearingInMembers pins it and goes red when the de-duplication is removed. Also fixed while here: DossiqFlowNodeListenerTest asserted a hard-coded count of 17 nodes. Its sibling test's own comment warns against exactly that, and it had already gone stale. It now derives from the fixture. Verified: 2561 unit tests pass (1 pre-existing warning in ZgwMappingServiceTest, present on the base tree too), PHPCS/PHPMD/PHPStan/Psalm clean on every changed file, all 71 applicable hydra gates green, and the register fragment merge was run through the real RegisterFragmentMerger rather than assumed. * test(bezwaar): cover the committee flow node and the migration, and fix the mapping write they exposed CI's coverage ratchet was right: this branch added a flow node and a repair step with no tests of their own. 21 tests now cover both. 🔴 The first one written found a real bug. `recordMapping()` and the repair step both wrote the mapping with `$committee + ['governanceBodyId' => $bodyId]`, and PHP's `+` on arrays KEEPS the left operand's key. A committee row that already carried an empty `governanceBodyId` — which is every row, once the schema declares the property — kept the empty string. The mapping was never recorded, so every run would re-dispatch, and the local side would never show a committee as migrated. Both sites now use array_merge. That is exactly the shape of defect the coverage gate exists to catch: the code looked right, the migration reported success, and nothing recorded anything. The node's tests pin the two behaviours it exists for — it FAILS the step when the committee cannot be raised (so a run never continues past a committee in no shared register), and it short-circuits on an already-mapped one (so a heartbeat does not re-dispatch on every pass). The step's tests pin that it REFUSES without runAsSystem() rather than warning, because $output->warning() does not fail an upgrade and the silent no-op is the failure nobody notices. One test-harness note worth keeping: the fake object service must implement searchObjectsBySlug(), not findAll(). The SearchesObjects trait calls the former, and a fake with the wrong method makes the step report "could not list committees" while the assertions blame the migration. Verified: 2582 unit tests pass (1 pre-existing warning), PHPCS/PHPMD/PHPStan clean on both files. * test(bezwaar): cover the branches the coverage guard named The guard reported the exact shortfall: 86.69% (254/293 statements) on the changed files against a 92.86% base, so 39 statements were uncovered. The coverage artifact named them line by line rather than leaving it to guesswork. They were all real branches, not filler: the dispatcher throwing, a committee row arriving as an ENTITY rather than an array, a read that fails, a best-effort mapping write that fails, members given as objects with their own external flag, a members list that is not a list, entries with no usable uid, an unusable relation value, the node's scope predicate, and the migration's two catch arms. Two of them are worth their own line. The entity-shaped committee: OpenRegister hands a row back unflattened when the read did not expand it, and reading that as an array finds nothing and reports the committee as missing. The failed mapping write: it must NOT fail the step, because the decision app has already raised the body by then and failing would discard work that succeeded. Verified: 2593 unit tests pass, PHPCS and PHPMD clean.
…1542) ADR-065 is explicit: OpenRegister is the only home for a flow engine, and a leaf app that grows a second one is an ADR-022 violation. dossiq's `workflowTemplate` is a state machine — statuses joined by guarded transitions — which is one of the two shapes that ADR names, and the one symfony/workflow's StateMachine models with `case.status` as the marking. OpenRegister already requires it. The visible cost is in this app's own navigation: the settings foldout carries BOTH `Flows` and `Workflow definitions`, side by side. Two authoring surfaces for one concept. - WorkflowTemplateFlowMigrator projects each template: one `dossiq.setStatus` node per status, one edge per transition. - `occ dossiq:workflows:migrate-to-flows` with --user and --dry-run, mirroring the automatic-actions migration that already exists. A command and not a repair step, because FlowService refuses to create a flow without a signed-in owner and a repair step under `occ upgrade` has none. 🔴 THE FLOW ARRIVES DISABLED. The template still drives cases through StatusTransitionService, so an enabled projection is a second thing driving the same case: every status change would fire twice from the moment the migration runs, and it would look like it worked. Adoption stays a deliberate act, which is how the shipped x-openregister-flows arrive too. 🔴 STATUSES TRAVEL BY NAME. A statusType uuid is minted per installation, so a flow carrying one is portable nowhere — which is exactly why `dossiq.setStatus` exists as a node distinct from `dossiq.setField`. Two smaller decisions: A wildcard `fromStatus: '*'` contributes no edge. The seeder accepts it and no shipped template uses it; an edge with no source node is not drawable, so it is skipped rather than guessed at. A re-run resolves the existing flow by a provenance marker in its notes, not by name. A name is editable in the flow editor, and matching on one would mint a second flow the moment somebody renamed the first. Out of scope: retiring workflowTemplate or collapsing the two menu entries. The projection has to be adopted and proven first, and the definition still carries per-step SLAs, checklists and roles the projection does not. Removing the definitions page while it is the only authoring surface would take away the way to edit a live workflow. Verified: 2572 unit tests pass (1 pre-existing warning), PHPCS/PHPMD/PHPStan clean. Mutation-checked: creating the flow enabled, accepting the wildcard source, and dropping the provenance marker each turn the suite red.
…1544) 2.25.x makes the canonical KPI card flat and horizontal, so dashboards stop drawing a grey box inside the white card CnWidgetWrapper already draws, and 2.25.1 fixes the narrow-tile step-down that 2.25.0 shipped inert (a source-order bug left KPI values clipped on a three-column tile). The caret range already allowed both; only the lockfile pinned this app back. Lockfile only.
Bumps [stylelint-config-recommended-vue](https://github.com/ota-meshi/stylelint-config-recommended-vue) from 1.6.1 to 2.0.0. - [Release notes](https://github.com/ota-meshi/stylelint-config-recommended-vue/releases) - [Changelog](https://github.com/ota-meshi/stylelint-config-recommended-vue/blob/main/CHANGELOG.md) - [Commits](ota-meshi/stylelint-config-recommended-vue@v1.6.1...v2.0.0) --- updated-dependencies: - dependency-name: stylelint-config-recommended-vue dependency-version: 2.0.0 dependency-type: direct:development update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Bumps [postcss-html](https://github.com/ota-meshi/postcss-html) from 1.8.1 to 2.0.0. - [Release notes](https://github.com/ota-meshi/postcss-html/releases) - [Commits](ota-meshi/postcss-html@v1.8.1...v2.0.0) --- updated-dependencies: - dependency-name: postcss-html dependency-version: 2.0.0 dependency-type: direct:development update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Bumps [marked](https://github.com/markedjs/marked) from 12.0.2 to 18.0.11. - [Release notes](https://github.com/markedjs/marked/releases) - [Commits](markedjs/marked@v12.0.2...v18.0.11) --- updated-dependencies: - dependency-name: marked dependency-version: 18.0.11 dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
… to route a voorstel that has no route (#1532) * feat(bezwaar): hold advisory committees in the decision app, and run the referral as a flow migrate-committees-to-decidiq has been BLOCKED on two prerequisites. Both are now in place: decidiq#874 gave GovernanceBody the four fields a bezwaaradviescommissie needs, and decidiq's governance-body-events change gave it the typed command seam an in-process migration must use. This is dossiq's half. - CommitteeDelegationService dispatches GovernanceBodyRequestedEvent, guarded by class_exists across BOTH namespace spellings, and fails closed when the decision app is absent or did not handle the command. Shaped after ContractDecisionDelegationService, which is this app's working example of commanding the decision app by event. - MigrateCommitteesToDecidiq raises a body per existing committee and records the id back. It REFUSES to run when OpenRegister exposes no runAsSystem(), rather than falling back to a bare call like the shared trait does: a repair step has no session, OpenRegister then resolves the actor as Anonymous and refuses every write, and $output->warning() does not fail an upgrade — so the fallback is exactly the silent no-op the spec forbids. - dossiq.ensureCommittee is a flow node that does the same thing mid-run, and the seeded `Bezwaar advies` flow on bacAdviceRequest uses it as its first step: ensure the committee exists, the secretary opens deliberation, the chair issues the advice, each human step moving the request's status. - governanceBodyId on bezwaaradviescommissie is the mapping record. The roster is where the sharp edge was. `members[]` may also contain the chair, and the seam keys a seat on (person, body) — so sending a uid twice overwrites its first seat, and the committee silently loses its chair while every row still looks well-formed. Officers are seated first and seen uids skipped; testChairIsNotDemotedByAppearingInMembers pins it and goes red when the de-duplication is removed. Also fixed while here: DossiqFlowNodeListenerTest asserted a hard-coded count of 17 nodes. Its sibling test's own comment warns against exactly that, and it had already gone stale. It now derives from the fixture. Verified: 2561 unit tests pass (1 pre-existing warning in ZgwMappingServiceTest, present on the base tree too), PHPCS/PHPMD/PHPStan/Psalm clean on every changed file, all 71 applicable hydra gates green, and the register fragment merge was run through the real RegisterFragmentMerger rather than assumed. * feat(parafering): hold parafeerroutes in the decision app, and refuse to route a voorstel that has no route Routing a document past a sequence of officials for approval is governance, and governance is the decision app's. Its `approval-routes` change built the target and named this app as the consumer; `approval-route-events` built the command seam an in-process migration must use. `parafeerroute` has carried DEPRECATED (migrate-parafering-to-or-approval-workflow) in its schema description for months. IT IS NOT DEPRECATED. Measured today: 15 PHP files, 7 frontend files, 4 routes, 4 test files, ~1,810 lines of engine across six services. The archive says why — that change was "archived prematurely; implementation not present on development" and reverted. A banner describing an intention reads, to anyone who did not check, as a state. This moves the ROUTE, not the runtime chain: - ParaferingDelegationService dispatches ApprovalRouteRequestedEvent, guarded by class_exists across both namespace spellings, failing closed. - MigrateParafeerroutesToDecidiq holds each local route and records the id. - activate() resolves the route through a new ParafeerrouteDirectory, takes the snapshot from those steps, and sends them to the decision app naming the voorstel as subject — so the chain is materialised there and "all pending approvals across apps" becomes a question anyone can ask. An earlier draft had activation READ the route back from the decision app. That was wrong twice: it needed a cross-app read seam that does not exist (ADR-066 lifted the moratorium only for render-and-read collect-events), and it was unnecessary, because dossiq HAS the steps at activation — it just read them. ### A dead end fixed while reading activate() When no route was configured, activate() set `routeSnapshot: []` and carried on, writing `currentStep: 1, status: in_parafering`. Every later action then failed `Current step not found in route snapshot` — a 400 naming the snapshot while the fault was a route nobody configured. The voorstel was parked in parafering with no way forward and no way back. It now refuses, before any write. testActivateReturnsArrayWhenVoorstelFound was passing on exactly that path and had to change with the behaviour rather than be made to keep passing. The decision app refusing does NOT block activation — it is an optional runtime dependency. Whether a voorstel was mirrored is recorded on the voorstel as `approvalRouteId` rather than only logged, because a best-effort step whose only trace is a log line is one nobody can audit. Out of scope, deliberately: the runtime chain. dossiq's pipeline owns a status vocabulary, a return notification, accordering effects and mandate validation that the other engine does not. Replacing it means reproducing all four or losing them, which is its own change with parity tests. Verified: 2576 unit tests pass (1 pre-existing warning in ZgwMappingServiceTest, present on the base tree too), PHPCS/PHPMD/PHPStan/Psalm clean on every changed file, all 71 applicable hydra gates green, and the fragment merge run through the real RegisterFragmentMerger rather than assumed. Mutation-checked: restoring the empty-snapshot behaviour, dropping the step-order fallback, and dropping the step-type translation each turn the suite red. Stacked on the committee change. * test(bezwaar): cover the committee flow node and the migration, and fix the mapping write they exposed CI's coverage ratchet was right: this branch added a flow node and a repair step with no tests of their own. 21 tests now cover both. 🔴 The first one written found a real bug. `recordMapping()` and the repair step both wrote the mapping with `$committee + ['governanceBodyId' => $bodyId]`, and PHP's `+` on arrays KEEPS the left operand's key. A committee row that already carried an empty `governanceBodyId` — which is every row, once the schema declares the property — kept the empty string. The mapping was never recorded, so every run would re-dispatch, and the local side would never show a committee as migrated. Both sites now use array_merge. That is exactly the shape of defect the coverage gate exists to catch: the code looked right, the migration reported success, and nothing recorded anything. The node's tests pin the two behaviours it exists for — it FAILS the step when the committee cannot be raised (so a run never continues past a committee in no shared register), and it short-circuits on an already-mapped one (so a heartbeat does not re-dispatch on every pass). The step's tests pin that it REFUSES without runAsSystem() rather than warning, because $output->warning() does not fail an upgrade and the silent no-op is the failure nobody notices. One test-harness note worth keeping: the fake object service must implement searchObjectsBySlug(), not findAll(). The SearchesObjects trait calls the former, and a fake with the wrong method makes the step report "could not list committees" while the assertions blame the migration. Verified: 2582 unit tests pass (1 pre-existing warning), PHPCS/PHPMD/PHPStan clean on both files. * test(parafering): cover the route directory and the migration, and fix the same mapping write The coverage ratchet applies here too: this branch added a directory and a repair step with no tests. 18 tests now cover both. The repair step carried the SAME `$route + ['approvalRouteId' => …]` bug the committee migration had — PHP's `+` keeps the left operand's key, so a row that already declares the property (which every row does, once the schema does) kept its empty string and the mapping was never recorded. Fixed with array_merge and pinned: restoring the `+` turns the suite red. BesluitvormingParafeerService::activate() was already using array_merge, so the bug was confined to the migration. The directory's tests are mostly about the EMPTY cases, because that is the whole reason it was extracted: the lookup used to be inline in activate(), which carried on with an empty step list and parked the voorstel in `in_parafering` with nothing to travel. "No route" is now a value the caller has to handle, and these pin that it is reported rather than papered over — including when OpenRegister is absent, when the register is unconfigured, when the read throws, and when the stored steps are unusable. Verified: 2615 unit tests pass (1 pre-existing warning), PHPCS and PHPMD clean. * test(bezwaar): cover the branches the coverage guard named The guard reported the exact shortfall: 86.69% (254/293 statements) on the changed files against a 92.86% base, so 39 statements were uncovered. The coverage artifact named them line by line rather than leaving it to guesswork. They were all real branches, not filler: the dispatcher throwing, a committee row arriving as an ENTITY rather than an array, a read that fails, a best-effort mapping write that fails, members given as objects with their own external flag, a members list that is not a list, entries with no usable uid, an unusable relation value, the node's scope predicate, and the migration's two catch arms. Two of them are worth their own line. The entity-shaped committee: OpenRegister hands a row back unflattened when the read did not expand it, and reading that as an array finds nothing and reports the committee as missing. The failed mapping write: it must NOT fail the step, because the decision app has already raised the body by then and failing would discard work that succeeded. Verified: 2593 unit tests pass, PHPCS and PHPMD clean.
#1547) * test(e2e): drive the four surfaces this round of work actually changed Playwright against a live instance, for the things a user can see. The backend seams stay covered by unit and mutation tests; these cover what those cannot. - The projected workflow flows are listed, and EVERY one is disabled. Verified by mutation against the live database: flipping a single flow to enabled turns this red. - An inspector cannot escalate an LHS intervention by posting a body that claims a harsher recommendation. Before the fix that returned 200 with a stored override. - The LHS recommendations settings entry is gone, and its PAGE still resolves, so deep links do not 404. - Activating parafering on a voorstel that cannot be routed does not answer 200. Three things learned writing them, kept in the comments because each one is a way an e2e goes green while proving nothing. The menu test needed a CONTROL. Its first version asserted only the absence and would have passed against a navigation that never rendered. It now asserts a sibling settings entry is visible first. The labels are the DUTCH ones the app renders. Asserting the English source strings failed and read as a broken feature rather than a broken test. The settings entries live in a collapsed foldout, so they are in the DOM but not visible until it is opened — the test opens it, because that is what a user does. Asserting DOM presence instead would have passed on a foldout that can never be opened. The flow assertion reads the flow store through the API rather than scraping the list: it is about what was PROJECTED, and a rendering change should not be able to turn it either colour. * style(e2e): apply prettier to the changed-surfaces spec quality / Frontend Check (format) runs prettier --check across the repo. Re-ran the four tests against the live instance after formatting, so the reflow is verified not to have changed what they assert.
npm ci could not resolve, so every frontend job failed at install:
Found: postcss-html@2.0.0
peer postcss-html@"^2.0.0" from stylelint-config-recommended-vue@2.0.0
peer postcss-html@"^1.0.0" from stylelint-config-html@1.1.0
The stylelint packages had ended up SPLIT across two generations:
recommended-vue on v2 demanding postcss-html ^2, config-html on v1
demanding ^1. No value of postcss-html satisfies both at once.
They are a set and must share a generation. This app runs
@nextcloud/stylelint-config@3, which pins the v1 line, so all three go to
v1 together rather than dragging the v2 line in behind one package.
That split is how the fleet broke tonight: eight apps could not install,
and the majors behind it were merged one at a time, each looking harmless
on its own.
Verified: the lockfile resolves from a clean tree.
Co-authored-by: Conduction Release Bot <release-bot@conduction.nl>
Bumps [commander](https://github.com/tj/commander.js) from 12.1.0 to 15.0.0. - [Release notes](https://github.com/tj/commander.js/releases) - [Changelog](https://github.com/tj/commander.js/blob/master/CHANGELOG.md) - [Commits](tj/commander.js@v12.1.0...v15.0.0) --- updated-dependencies: - dependency-name: commander dependency-version: 15.0.0 dependency-type: direct:development update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
…#1554) The control assertion hard-coded the Dutch labels, and broke the moment the instance served English — a test that depends on the session locale tells you about the locale, not about the feature. Both spellings are now accepted. That control has now earned its place twice. Its first version asserted only the absence and would have passed against a navigation that never rendered; its second passed only in Dutch. Neither failure was the feature. Verified: all four tests pass against a live instance serving English, having previously passed against the same instance serving Dutch.
…#1557) changed-surfaces.spec.ts fails on development: Error: no projected flow found - run `occ dossiq:workflows:migrate-to-flows` 1 failed, 142 passed The spec asserts that every workflow definition appears as a disabled projected flow. Nothing in ci-seed.sh produced those flows, and the app does not project them on install: it is a one-shot migration, which the failure message itself names. What makes this worth fixing rather than retrying: the spec passed most of the time. The flows were arriving incidentally, from whatever else had touched the instance first, so the precondition held by accident. A test whose setup depends on ordering fails on ordering, not on the behaviour it claims to check -- and reports the app as broken when it is not. Running the projection in the seed makes the precondition the seed's job. `php occ` bare matches the other seeds in the fleet; the shared workflow invokes this script with cwd at the Nextcloud server root. Idempotent, so a warm instance re-projects rather than duplicating. Verified: bash -n clean.
…1556) dossiq's E2E job failed on development: no projected flow found — run `occ dossiq:workflows:migrate-to-flows --user=admin` first changed-surfaces.spec.ts asserts the PROJECTION — that every workflow definition has a flow, and that every one arrived DISABLED. Nothing in the register import creates those flows, so the spec was failing on a migration that had simply never run. The message reads as a broken projection; the projection was never made. The spec is right to fail rather than skip. Its own header says an e2e that cannot tell 'absent' from 'broken' reports both as fine, so the fix belongs in the seed, not in the assertion. Placed after section 2 deliberately: the migration reads the workflow definitions the register import creates, so on an empty register it would succeed having projected nothing and the spec would still fail. The command is idempotent and creates the flows DISABLED by design — the definitions still drive cases, and an enabled projection would move every case a second time on each status change. That invariant is exactly what the spec checks, so seeding must not enable them. Verified against the dev instance rather than assumed: --dry-run exits 0 and reports the projections with the `dossiq:workflowTemplate:` prefix the spec matches on. bash -n passes.
…#1561) * refactor(dmn): consume OpenRegister's shared decision-table evaluator openregister#3186 moved dossiq's DMN engine into OpenRegister and deliberately left dossiq's copy in place, because deleting a working evaluator on the strength of a new one that had not yet run its data would have been the same mistake in the other direction. It has now run its data, so the copy goes. Normalising namespace and class name, DecisionEngine and DecisionTableEvaluator differ only in docblocks and the two extra hit policies. Same call signature, same return shape. For every table dossiq ships, this is a delete. What it fixes: the DecisionTable schema has always offered five hit policies in its enum while the engine refused two of them, so the form offered a choice it would not honour. All five now work. What it does not fix, despite appearances: the seeded `Voorbeeld Name 3` table declares PRIORITY and is tempting to cite as a broken seed repaired here. Run through the real shared evaluator it still fails, with type_mismatch. It is placeholder data whose input entries are the literal string "Voorbeeld Inputentries 3" against a boolean column, and it never evaluated under either engine. The claim is only that PRIORITY is no longer refused for being PRIORITY. EvaluateDecisionHandlerTest built a real engine and asserted on real evaluation. It now drives a mocked evaluator and asserts what the handler actually owns: mapping inputs in, mapping outputs onto the case, and refusing to write when evaluation fails. The evaluation itself is proven in openregister. The 31 grammar tests travelled first, in openregister#3191. Had the class gone first, that coverage would have left the fleet with the file. * fix(zgw): assert the absence of caseType instead of reading an absent key testListMappingsReturnsAllKeys asserted `assertNull($mappings['caseType'])`. There is no `caseType` key: RESOURCE_KEYS is the Dutch ZGW vocabulary, and `caseType` is the English alias for `zaaktype`. So the assertion read a missing index, PHP warned "Undefined array key", the expression evaluated to null, and the assertion passed on the warning rather than on the service agreeing with it. Now it asserts what is actually true: `caseType` is not a resource key. Three docblocks offering `'zaak', 'caseType'` as example keys are corrected to `'zaak', 'zaaktype'`, since they are where the wrong idea came from. Pre-existing; found while running the suite for the DMN change.
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
hydra-gates v1.10.0 -> v1.10.0 nc-vue 2.25.1 -> 2.26.0 Lock-only: both packages are already declared with caret ranges that permit these versions, so nothing about what this app ACCEPTS changes - only what it currently resolves to. Opened by the weekly fleet shared-dependency bump, because a lock nobody re-resolves is a pin nobody chose. Merging is gated by this repository's own suite, deliberately: taking hydra-gates v1.8.1 added patchObject() to a published interface, which is a load-time fatal for any concrete double that implements it without the method. CI is the only thing that can tell a safe bump from that. Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
…eObject (#1564) * fix(dmn): carry rule priority, so PRIORITY can actually rank dossiq#1561 made the engine accept PRIORITY. Driving it against a live instance showed that was only half the story: the table evaluated, returned 200, and gave the WRONG answer, picking the priority-1 rule over the priority-10 one. Two things dropped the rank before it reached the evaluator. The DecisionTable schema had no `priority` property on a rule, because dossiq's old engine never implemented the policy that uses it. Added, defaulting to 0. DecisionTableService::validateRules() rebuilds every rule from a fixed list of four keys, so `priority` was stripped on write even once the schema allowed it. It is now carried, and only when the author supplied it: writing a default onto every rule of every table would put a meaningless field on tables that do not use it. Verified end to end: a PRIORITY table with rules ranked 1 and 10 now returns the rule ranked 10. Neither the unit tests nor reading the code found this. The e2e did, which is the argument for having written it. * fix(openregister): pass the uuid as the uuid to deleteObject, in all five places OpenRegister's signature is `deleteObject(uuid, register, schema, ...)`. Five dossiq services called it as `deleteObject($register, $schema, $id)`, which transposes all three arguments: the register id lands in the uuid slot and the object's uuid lands in the schema slot. Every one of those deletes has been failing. On a live instance the decision-table delete answered HTTP 500 with "Did expect one result but found none" against openregister_registers, because it was looking up a register whose id was really a schema's. A sixth, TenantSaasService, passed `id:` as a named argument. There is no `$id` parameter, so that raised "Unknown named parameter" and was swallowed by the surrounding catch. InspectionChecklistService already carries a docblock describing this exact defect, found the same way against a live instance: "no checklist has ever been deleted through the UI". That instance was fixed; the class never was. Swept now, and every remaining call site verified to use named arguments. Why no test caught it: AdvisoryBodyServiceTest's fake declared `deleteObject(string $register, string $schema, string $id)`. The fake had been written to agree with the CALLER rather than with OpenRegister, so it accepted the transposed call and the test passed on it. The fake now carries the real signature, and a new test asserts the uuid arrives in the uuid slot. It fails when the transposed call is put back, which is the only reason to trust it. Verified on a live instance: DELETE now answers 200 where it answered 500. * docs(openspec): record that PRIORITY needed a follow-up to become true The dossiq-consumes-shared-dmn change asserts "PRIORITY MUST return the matching rule with the highest priority". When it merged, that was not satisfiable: the schema carried no `priority` property and validateRules() stripped the field, so a PRIORITY table returned 200 with the wrong rule. The requirement is right and is now met. The task list should say that it took a second change, rather than reading as though it was true on the day. * fix(quality): format the e2e spec and tag the two changed methods Two CI findings, both mine. Prettier: the DMN e2e test I added was not formatted. Ran the repo's own `format:fix`; the whole tree is clean. gate-16 spec-coverage: `RoutingRuleService::deleteRule()` and `DecisionTableService::validateRules()` changed without an `@spec`. Both do implement a specified requirement, so the tags say something true rather than silencing the gate: deleteRule carries the KCC task its class docblock already names, validateRules the dmn-decision-tables spec its siblings all carry.
Carries the two KPI-card fixes this dashboard reads: the canonical card is flat and horizontal (no grey box inside the white card CnWidgetWrapper already draws), and a calendar-aligned date range follows the reader's calendar rather than UTC — "Current month" previously showed a To of 1 September in CEST on 31 August. The caret range already allowed it; only the lockfile pinned this app back. Lockfile only, and npm pruned nothing.
Release: merge development into beta
Contributor
Quality Report — ConductionNL/dossiq @
|
| Check | PHP | Vue | Security | License | Tests |
|---|---|---|---|---|---|
| lint | ✅ | ||||
| phpcs | ✅ | ||||
| phpmd | ✅ | ||||
| psalm | ✅ | ||||
| phpstan | ✅ | ||||
| phpmetrics | ✅ | ||||
| eslint | ✅ | ||||
| stylelint | ✅ | ||||
| build | ✅ | ||||
| check-manifest | ✅ | ||||
| check-vue3-compile | ✅ | ||||
| test-l10n | ✅ | ||||
| format | ✅ | ||||
| check-schema-l10n | ✅ | ||||
| check-l10n-js | ✅ | ||||
| composer | ✅ | ✅ 106/106 | |||
| npm | ✅ | ✅ 540/540 | |||
| app:check-code | ⏭️ | ||||
| info.xml | ✅ | ||||
| REUSE | ❌ | ||||
| PHPUnit | ✅ | ||||
| Newman | ⏭️ | ||||
| Playwright | ✅ | ||||
| Hydra gates | ✅ |
Quality workflow — 2026-08-31 12:32 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.
Brings main level with beta, which is 27 commits ahead.
development is green on its last completed push run with Playwright E2E passing, and beta was brought level with development earlier today, so this carries verified code rather than a snapshot of unknown state.
Security alerts are measured on main but fixed on development, so this promotion is what actually clears them.