Skip to content

train: land 32 ready PRs (11 Oct, b) - #3742

Merged
rubenvdlinde merged 231 commits into
developmentfrom
train/2026-10-11-b
Oct 12, 2026
Merged

rubenvdlinde merged 231 commits into
developmentfrom
train/2026-10-11-b

Conversation

@rubenvdlinde

@rubenvdlinde rubenvdlinde commented Oct 11, 2026 •

Copy link
Copy Markdown
Contributor

Merge train 2. Each PR head below is merged with --no-ff, so GitHub marks each PR merged when this lands. Do not squash. Train 1 (#3718) has already landed; this branch was built on development after that.

Included heads (32)

#3608 df9f1cf, #3632 28dfe82, #3643 921a391, #3660 c3c9dc4, #3661 364cae0, #3667 5f4509b, #3670 8deab16, #3719 ba6f654, #3626 c6f1148, #3603 62f11a1, #3724 9a8dd6f, #3644 d614fb4, #3674 0174c67, #3654 e10eb2b, #3664 a042113, #3728 c0a7f9f, #3663 1ee9c17, #3628 77c23c4, #3627 fb3fd0c, #3622 2efe4ab, #3613 0670fcd, #3605 96cdc24, #3583 8609a83, #3607 e64c25b, #3625 86ea512, #3629 6e39327, #3648 1cb50dc, #3659 19f14db, #3624 144e37f, #3665 5e31133, #3668 13cb508, #3658 258f41e.

Left out

Conflict notes

Fixes the merge needed (no PR branch carries them)

Refactor for phpmd

The seventeen @SuppressWarnings the train added are removed and the four phpmd findings are fixed by splitting classes. Nothing is suppressed and no baseline is raised. New classes are generic and named by what they do (decision 182). No behaviour changes. Each new class has its own unit test.

Old class What moved out New class
WooRequestIntake (complexity 53, coupling 14, now 27 and 12) the system reads of the case type and the case, and the case write Service\Intake\SystemCaseStore
the resident's own collection: read, linked item by item, recorded once Service\Intake\CollectionCaseLinks
one object by id, as the system and unscoped Service\Support\UnscopedObjectReader
WOODocumentAssessmentService (complexity 52, coupling 13, now 21 and 12) validation against the classifications and the refusal-ground list Review\DisclosureAssessmentValidator
the documents still waiting for a verdict Review\OutstandingDocuments
WOODecisionService (coupling 13, now 12) the case title and the assessment tally of the decision record Service\DecisionDocument\DecisionRecordComposer
the ready-to-publish mark on the case Service\DecisionDocument\PublicationReadiness
WooPublicationService (coupling 13 under a suppression, now 12) loading the case, the decision and the disclosable documents, with the disclosure matrix Service\Publication\PublicationSources
the delivered set around the send, the files, the depublish Service\Publication\CatalogTransport
DecisionConcludedListener (coupling 13 under a suppression, now 12) the record and case of the decision, and the advice deviation Service\DecisionDocument\ConcludedDecisionRecord
waking the flow run that waits on the decision Service\Flow\WaitingRunResumer
BeschikkingController (complexity 61 under a suppression, now 49) reading the JSON body and picking the sent fields Controller\Support\JsonRequestBody
RoleLinkDefinition, SessionDefinition, LinkMarkerRule (static factories, long parameter lists) the constructor reads and validates the configuration entry; reading the list moves out RoleLinkDefinitionReader, SessionDefinitionReader, LinkMarkerRuleReader
FreezeDefinition, DecisionDocumentDefinition (11 and 13 constructor parameters, boolean flags) the constructor reads and validates the configuration entry none needed

Three of the seventeen suppressions hid nothing and are simply gone: the one on PackageSchemaReferences (FleetAppId is already exempt in the ruleset) and the two boolean-flag ones on SessionDefinition::flag() and SessionRules::assertAttendance().

One signature changed: WooRequestIntake takes ?WooReceivedAnswers $receivedAnswers where it took ?CaseDateNormaliser $dates. The container wires it the same way.

Fixes found on the way

Development's own reds, fixed here

Landing this train should leave development green, so these are fixed on the train although development brought them.

Red on development Cause Fix
PHPUnit: 33 risky tests (CaseEndingActsTest, DeadlineReportingControllerContractTest, MigrateWorkflowDefinitionsLabelsTest, VthCaseTypeChildSeederTest and nine more test classes) LanguageMapText ran without being listed @uses LanguageMapText on each test class. No assertion changed.
PHPUnit: ObjectListenerRegistrarTest, 2 risky OnboardingListenerRegistrar not listed @uses on the test class
PHPUnit: IntakeTermStartListenerTest, 1 risky CaseDateNormaliser not listed @uses on the test class
PHPUnit: StubApiDriftTest (McpTool) the stub's constructor had 8 arguments, the real class 10 already fixed by #3583 in this train. Checked against openregister development 91146be: 70 tests pass.
PHPUnit: ManifestColumnBindingTest the Organisations list bound a column its schema lacks fixed on development by #3779, merged in
phpmd: PublicationChecks, 10 constructor parameters a text reader added as a tenth argument the class builds its own reader
phpmd: MigrateWorkflowDefinitions, complexity 58 grew with the title fix steps and transitions move to Service\Workflow\BackfilledTemplateShape; rows are read through the shared trait. Complexity 47.
format: 7 files not run through prettier prettier, no other change

Exit codes (full runs)

  • COMPOSER_PROCESS_TIMEOUT=0 composer check:strict: 0, ALL CHECKS PASSED, on 4127f39 (lint, refusal-grounds snapshot, phpcs, phpmd, psalm, phpstan, PHPUnit 8178 tests). The merge after it brought no PHP and no unit test.
  • PHPUnit with coverage (pcov, openregister beside the app, as in CI): 8178 tests, 0 risky, 0 errors. One failure only in my read-only container: RefusalGroundsSnapshotTest rewrites a file in the tree. It passes on the host.
  • On the head, after the last merge: php vendor/bin/phpunit --no-coverage 0 (8178 tests). npx vitest run --maxWorkers=2 0 (295 files, 3341 tests). npm run lint 0. format 0. test:l10n, check:schema-l10n, check:l10n-js, check:manifest, check:types-e2e, check:dexie: 0.
  • node scripts/debt-ratchet.js: 1. phpmd.suppressWarnings is 194 and the baseline is tightened from 196 to 194. The exit code comes only from eslint.inlineDisables, 15 against 14, which is development's own.

Still red, and why

  • check:debt-ratchet: eslint.inlineDisables 15 against 14. Development counts the same fifteen at the same places. The one named for the difference is MyWorkWidget.vue:160, a prop declared on purpose and never read. I did not raise the baseline and did not remove the prop, because that changes the widget. This needs a reviewer: either the baseline goes to 15, or the widget changes.
  • Hydra gates were not run locally. E2E was not run (decision 139).

Live passes still open: opencatalogi#1897 (import stamp, #3608/#3632), hermiq#1147 and #1148 (#3607, #3629).

…them without a grant

Every curated read (deadline dashboard, lead times, KPIs, workload, available
transitions, overdue complaints) now declares reach: 'user'. Without it a
two-segment id resolves to external and needs an explicit grant.
Needs openregister build/dep-dossiq-mcptool-reach on the instance first.
… and work its terms

Curated write tools, each on the service its controller calls:
transitionCase (StatusTransitionService, guards and status record intact),
reassignCase (new CaseAssignmentService::reassign, coordinators only, the
receiver is notified), completeTask (the engine's complete verb),
extendDeadline/pauseDeadline/resumeDeadline (new TermTools: the term is read
and its CASE must be changeable by the caller before the term services run).
Refusals come back as envelopes with the service's own rule and sentence.

WIP: AppointmentTools and BeschikkingTools are listed but not written yet.
…chikking

AppointmentTools (scheduleAppointment, cancelAppointment; external reach: the
resident is told) run AppointmentController's case checks and call
AppointmentService. BeschikkingTools offers draftBeschikking only, through
BeschikkingService::compose(); approving, signing and sending stay with the
caseworker. A test holds the tool classes off ObjectService.

hermiq-ai-tooling 3.1-3.3 ticked; the spec names the services as found.
# Conflicts:
#	lib/Settings/register.d/60-termijnbewaking.json
#	tests/schemas/schema-version-digests.json
… verdict

woo-review-triage section 1: wooDocumentReview, wooTriageRule and
wooReviewBatch schemas (87-woo-review.json, register 0.20.23); only an
in-scope document needs a verdict and an unmarked one is always outstanding;
POST relevance and GET summary on the Woo case; a repair step marks documents
assessed before this change in scope so they do not block their case.
(unit tests written, run pending a slot)
# Conflicts:
#	lib/Settings/register.d/60-termijnbewaking.json
#	tests/schemas/schema-version-digests.json
…duleAppointment (phpmd finds nothing without it)
…1-4)

Decision 179 (Ruben, 10 Oct 2026): a form submits into its destination object, with no in-between intake layer, and is valid against that object's schema when built. Cross-app change: hydra openspec/changes/form-submits-into-its-destination-object (ADR-117, hydra#748). Spec only.
# Conflicts:
#	appinfo/info.xml
#	tests/schemas/schema-version-digests.json
…rmed only when its term runs

WooRequestIntake::receive() takes opencatalogi's answer keys (portal-form and
opencatalogi origins, subjectRef optional for those two), writes the Woo case
directly (decision 179: no intake object in between), counts the term from
when the requester sent it, and reads the term back: armed only when a
statutory instance with an engine timer ends on the case deadline; otherwise
not-armed with an internal timeline entry, or unavailable with nothing
written. PortalContributionProvider::receiveWooRequest() has opencatalogi's
signature. Register 0.20.26 (wooRequest origin enum, phone and address).
…t, a due invoice and a published award

Four x-openregister-notifications rules on the supplier schemas mail the
supplier's contact address through supplierRef, using OpenRegister's new
email recipient kind (portal-contribution T3).
…nd act as dossiq on the case layer

The bridge called createPlan and getPlan with uid null, which OpenRegister
refuses, so every case start created nothing. It now calls the system verbs
from openregister#4544 and names an OpenRegister that lacks them.

The drain (retire-cmmn-caseplanstate 2.1-2.3): occ dossiq:cmmn:migrate-case-plans
and a post-migration repair step hand each blob to ensureItems with the
recorded states and the event log as history, verify every state, then clear
the blob. Unmappable cases keep their blob and are reported.
…chable

formerReferences (list of {application, reference}) on the case schema, an
exact search declaration, register 0.20.27.
OpenCatalogiWooCase maps every status to its stage (received, in_progress,
awaiting_clarification, decided, withdrawn), carries start, requester,
channel, old reference, extension count and end date, on opencatalogi's own
request shape as fixture. First part of takes-over 4.2.
…s no approval floor

Decision 176 (Q-dossiq-L6-2): REQ-MCP-206 now says hermiq decides which AI
writes wait for a person. dossiq declares readOnlyHint: false and a scope on
every write so hermiq can gate it, and ships no grant or waiver. CHANGELOG
entry for the curated tools (task 4.3).
…who gets a task for it

POST /api/cases/{id}/woo/batches takes a list of documents or a filter on
the rule that marked them, refuses a document already in another open
batch (409, naming the batch), writes the batch on each review and hands
the reviewer an engine task. GET lists the batches with assessed of total.

Also: WooThroughputReport gets its row in the date-arithmetic audit that
development's EveryTermOnTheCalendarTest now requires.
…siq.fileCase

dossiq.fileCase carries the citizenIntake mark with scope and action create,
which is how hermiq now recognises an intake tool (decision 177). It writes
through the create form's own path: one save on the case schema as the caller,
so the schema, the intake requirements, the duplicate policy and RBAC all run,
and a refusal is thrown so hermiq hands the resident to a person. No
intermediate intake object (decision 179). The six curated reads carry
outsideAgent for hermiq's outbound surface.

ai-features-on-the-case-consume-hermiq 5.1, 5.2; screens.md added.
…d the viewer reports the pages seen

Review depth per document type (every page, or a sample drawn from a seed
recorded on the review). POST .../documents/{documentRef}/pages-seen
appends each displayed page with the reviewer and time; the first page
count a review hears sets its required pages from the recorded depth.
caseType.aiFeatures (register fragment 43) maps hermiq's feature slug to
case, intake or none, and holds no provider, model or residency. Register
0.20.25, info.xml bumped, en/nl strings. Ticks ai-features 1.1, and 1.2/2.1
with the files that already carry them.
# Conflicts:
#	lib/AppInfo/Registrar/ObjectListenerRegistrar.php
#	lib/Settings/dossiq_register.json
#	tests/schemas/schema-version-digests.json
…ctor; list readers are instances

FreezeDefinition, DecisionDocumentDefinition, RoleLinkDefinition, SessionDefinition
and LinkMarkerRule take their configuration entry and validate it in the
constructor, which replaces the static fromArray() and the long positional
parameter lists. The list factories move to RoleLinkDefinitionReader,
SessionDefinitionReader and LinkMarkerRuleReader, so the services no longer
reach a class statically. No behaviour change.
…inks to generic collaborators

WooRequestIntake (complexity 53, coupling 14) keeps start(), receive() and the
case it composes. The system reads and the case write go to SystemCaseStore,
the resident's own collection (owned, linked item by item, recorded once) to
CollectionCaseLinks, and the unscoped read by id to UnscopedObjectReader.
receive() takes its answers mapper injected instead of building it from the
date reader. Complexity 27, coupling 12, no suppression. No behaviour change.
…outstanding read to collaborators

WOODocumentAssessmentService (complexity 52, coupling 13) keeps bulkUpsert,
the redaction proposal and the lookup. Validation against the classifications
and the refusal-ground list moves to DisclosureAssessmentValidator, the
documents still waiting for a verdict to OutstandingDocuments. The public
validate() and getOutstanding() delegate. Complexity 21, coupling 12. No
behaviour change.
…s through collaborators

WOODecisionService (coupling 13) keeps assembleDecision and its guards.
The case title and the assessment tally move to DecisionRecordComposer, the
ready-to-publish mark on the case to PublicationReadiness. Coupling 12,
complexity 12. No behaviour change.
…catalog through collaborators

WooPublicationService (coupling 13) keeps publish, withdraw, the payload and
the case and decision state. Loading the case, the decision and the
disclosable documents, with the disclosure matrix, moves to
PublicationSources; the delivered set around the send, the files, the
depublish and their logs to CatalogTransport. The public collectDelivery()
and selectDisclosableDocuments() delegate. Coupling 12, complexity 26. No
behaviour change.
Development closed the register to everybody outside dossiq-medewerkers
(#3753); the reviewer-access block on wooDocumentAssessment still granted
read, create and update to every signed-in account. They go to the staff
group and the background account; delete stays with the coordinators.
Schema 1.2.1, digests regenerated.
…umes the run through collaborators

DecisionConcludedListener (coupling 13) keeps handle() and the event
projection. Finding the record and case of the decision, and mirroring an
advice deviation, move to ConcludedDecisionRecord; waking the run suspended
on the decision to WaitingRunResumer. Coupling 12, complexity 22. No
behaviour change.
…RequestBody

BeschikkingController (complexity 61) loses its own body reader and the two
successor field helpers to the generic JsonRequestBody (read, only,
textOrNull). Complexity 49, no suppression. No behaviour change.
# Conflicts:
#	tests/l10n/language-neutral-keys.json
The seventeen suppressions the train added are gone, and a train PR had
already removed two of development's, so the baseline drops from 196.
No number is raised.
With a coverage driver, as in CI, 57 tests on the train were risky because
they ran a class their coverage metadata did not list: the review, reviewer
access, import and linked-case classes of the merged PRs, and the
collaborators of this refactor. Each now carries its @uses. The 33 that
development carries itself are left.
Gate 51 on #3724: rateBands[].upTo and rateBands[].rate had no title and no
description in the register fragment and the mock register. Both get them,
with the four catalogue keys in English and Dutch, in the words #3769 uses
so that branch merges without a conflict.
Development's own PHPUnit run counts 33 risky tests: LanguageMapText in 13
test classes, OnboardingListenerRegistrar in the registrar test and
CaseDateNormaliser in the intake term start test. Each test class now lists
the class in its @uses. No assertion changes.
phpmd: PublicationChecks builds its text reader instead of taking a tenth
constructor argument; MigrateWorkflowDefinitions (complexity 58) hands the
steps and transitions of a basis template to BackfilledTemplateShape and
reads rows through the shared trait instead of its own copy (complexity 47).
format: prettier on the seven files development left unformatted. No
behaviour change.
@github-actions

Copy link
Copy Markdown
Contributor

Quality Report — ConductionNL/dossiq @ 44c6f62

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 ✅
check-debt-ratchet ❌
check-types-e2e ✅
composer ✅ ✅ 109/109
npm ✅ ✅ 659/659
app:check-code ⏭️
info.xml ✅
REUSE ❌
lockfile sync ✅
PHPUnit ✅ 8174/8174 · 4 skipped
Newman ⏭️
Playwright ⏭️ deferred: E2E runs locally and on the promotion path only. This pull request targets development, so the suite is asked once per promotion into beta and main rather than once per push per open pull request. Run it locally with npx playwright test, or from the Actions tab on a branch with no open pull request into development.
Hydra gates ❌

Quality workflow — 2026-10-12 07:17 UTC

Download the full PDF report from the workflow artifacts.

@rubenvdlinde
rubenvdlinde merged commit 102aba0 into development Oct 12, 2026
48 of 50 checks passed
@github-actions

Copy link
Copy Markdown
Contributor

Quality Report — ConductionNL/dossiq @ f98c290

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 ✅
check-debt-ratchet ❌
check-types-e2e ✅
composer ✅ ✅ 109/109
npm ✅ ✅ 659/659
app:check-code ⏭️
info.xml ✅
REUSE ❌
lockfile sync ✅
PHPUnit ✅ 8174/8174 · 4 skipped
Newman ⏭️
Playwright ⏭️ deferred: E2E runs locally and on the promotion path only. This pull request targets development, so the suite is asked once per promotion into beta and main rather than once per push per open pull request. Run it locally with npx playwright test, or from the Actions tab on a branch with no open pull request into development.
Hydra gates ❌

Quality workflow — 2026-10-12 07:38 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.

1 participant