Skip to content

fix(flow): the ask that named nobody, and three templates left behind by a rename - #1804

Merged
rubenvdlinde merged 1 commit into
developmentfrom
fix/askperson-assignee-fallback-and-template-renames
Sep 4, 2026
Merged

fix(flow): the ask that named nobody, and three templates left behind by a rename#1804
rubenvdlinde merged 1 commit into
developmentfrom
fix/askperson-assignee-fallback-and-template-renames

Conversation

@rubenvdlinde

Copy link
Copy Markdown
Contributor

What this fixes

Three defects from a fresh-rig acceptance proof. Two of the three reported were
already gone; what ships here is the third, plus what sweeping for the first
one's shape actually turned up.

1. ask-indiener could not name anybody, so the run died

The supplement step declared assignee: "{{ case.assignee }}", and assignee
is not in the case schema's required. A case filed from the New case
dialog with only a title and a case type therefore has none, the template
resolved to nothing, the node threw
could not resolve the assignee "{{ case.assignee }}", and the run failed.
The case then sat in Wacht op aanvulling with no task for anybody and nothing
waiting on it. Reproduced twice on independent clean installs. The e2e never
caught it because every case it files names an assignee.

Who it should address: the behandelaar, and it already did. An indiener has
no Nextcloud identity in this app at all. The case carries them as
initiatorSourceId (a BSN, a KvK number or a contact URI) or as the
pseudonymous portalSubject, while a task's assignee is a uid or a group id
and the completion gate resolves it as one. The ask cannot go to the applicant.
It goes to the handler, who asks the applicant and records the supplement,
which is also the only way description gets filled and the loop back to
check-complete ever takes its compleet exit. The node is ask-aanvulling
now, and its Dutch copy addresses the person who holds it.

Behaviour when there is none: a declared fallback, not a silent one.
assigneeFallback: "behandelaars", the same group task-behandelaar already
uses and ProvisionAssignedGroups already creates, so unclaimed work reaches
the handlers' queue instead of killing the case. Failing closed stays the last
word: no fallback, or a fallback that resolves to nobody, still refuses, since
OpenRegister's resume guard lets anyone answer a step that names no
assignee.

2. The same sweep found the bezwaar-advice flow dead

register.d/72-committees-to-decidiq.json ships two dossiq.askPerson nodes
that declared no assignee at all. validateConfig refuses that outright, so
every bacAdviceRequest ever created failed its run at the first human step.
CaseFlowDeclarationTest could not see it: it read the case flow alone. It now
walks every declaration file, the way its edge-condition test already did.

Both steps go to bezwaarcommissie, kept separate from behandelaars on
purpose: Awb art. 7:13 requires the advisory committee to be independent of the
officials who handled the case. The flow's description names the secretary and
the chair, and both uids exist, on the bezwaaradviescommissie. They are not
reachable from here: the item json is the bacAdviceRequest, whose committee
is a bare reference the trigger does not expand, so {{ committee.secretary }}
would resolve to nothing on every run and quietly mean the fallback. The group
is the honest principal until that reference is expanded into the run.

3. Three templates a rename left behind

The reported VoorstelDetail defect retired with the whole voorstel model in
af1ff19 (#1796). Its shape had not. A vue/no-undef-properties sweep of
src/** found three live components where a Dutch name was replaced by an
English one in the script and left standing in the template. Vue resolves an
unknown root identifier to undefined and says nothing.

Component What broke
DsoCaseDetail Prop renamed to case, id computed to zaakId; template still read zaak.* and caseId. Every field rendered blank and the three sub-dialogs got an empty id. case is a JS reserved word, so a template cannot name that prop at all: the zaak computed is the only way in, not a nicety.
SamenwerkverzoekDialog Field renamed to requestedCompetentAuthority, aangezochtBevoegdGezag left in the submit button's :disabled. The button was pinned disabled; the dialog could not be submitted at all.
BeschikkingComposerDialog Field renamed to rationale, textarea left writing to motivering. Typing a motivering did nothing and the composed decision went out without one.

tests/vitest/dialogTemplateBindings.spec.js mounts all three with a
realistic object. A shallow mount never evaluates the template, which is the
only place these defects exist. All seven assertions red on the unfixed
components and green on the fixed ones, proven by stashing the fix.

What was already gone

The voorstel detail page and concludeParafering both retired in af1ff19
(#1796, "a voorstel is a case, not a record of its own"), which deleted 68
files: the proposal / parafeerroute / parafeeractie /
paraferingAuditEntry schemas, the /voorstellen pages and the parafering
runtime across 21 PHP classes. The acceptance rig ran against a build from
before it.

No surviving transition has concludeParafering's shape: of 80 declared
lifecycle transitions, the 76 without a requires are ordinary steps a person
is meant to click.

Verified locally

CI is bottlenecked, so this was verified locally and individually. Exit codes,
not summary lines.

Check Result
PHPUnit 2926 tests, Failures: 1 — see below
PHPCS exit 0, 0 errors, 435 warnings, identical to development's baseline
Psalm exit 0
PHPStan exit 0, no errors
PHPMD exit 0 per touched subdirectory (lib/Flow, lib/Repair, lib/Settings), both rulesets
Vitest exit 0, 36 files, 366 tests, all pass
Prettier exit 0
ESLint exit 0, 0 errors, 823 warnings, matching development's baseline
Hydra gates v1.13.0 --scope-to-diff exit 0, 54 of 54 applicable gates ran and passed

The one PHPUnit failure is environmental, not dossiq's.
StubApiDriftTest compares dossiq's stubs against a sibling openregister
checkout at ../openregister, which on this machine is stale. It reported
StoreDescriptor::__construct() takes 5 arguments … the real class takes 4.
Asking the remote settles it: origin/development of openregister has the
5-argument constructor, so dossiq's stub is correct and CI, which clones
openregister fresh, is green. The same failure reproduces on a clean checkout
of development, so it is not introduced here.

Not run locally: the e2e suite (needs a live rig). case-flow-live-journeys
was updated for the renamed node and the new task title.

🤖 Generated with Claude Code

… by a rename

Three defects, from a fresh-rig acceptance proof. Two of the three reported
were already gone (see below); what shipped here is the third, plus what
sweeping for the first one's SHAPE actually turned up.

ask-indiener could not name anybody, so the run died
----------------------------------------------------
The supplement step declared `assignee: "{{ case.assignee }}"`, and `assignee`
is NOT in the case schema's `required`. A case filed from the New case dialog
with only a title and a case type therefore has none, the template resolved to
nothing, the node threw `could not resolve the assignee "{{ case.assignee }}"`,
and the run FAILED. The case then sat in `Wacht op aanvulling` with no task for
anybody and nothing waiting on it. Reproduced twice on independent clean
installs. The e2e never saw it because every case it files names an assignee.

The node is named for the indiener and addresses the behandelaar, and the
behandelaar is right. An indiener has NO Nextcloud identity in this app: the
case carries them as `initiatorSourceId` (a BSN, a KvK number or a contact URI)
or as the pseudonymous `portalSubject`, while a task's `assignee` is a uid or a
group id and the completion gate resolves it as one. The ask cannot go to the
applicant. It goes to the handler, who asks the applicant and records the
supplement, which is also the only way `description` gets filled and the loop
back to `check-complete` ever takes its `compleet` exit. So the node is
`ask-aanvulling` now, and its Dutch copy addresses the person who holds it.

The no-assignee behaviour is a DECLARED fallback, not a silent one:
`assigneeFallback: "behandelaars"`, the same group `task-behandelaar` already
uses and ProvisionAssignedGroups already creates. Unclaimed work reaches the
handlers' queue instead of killing the case. Failing closed stays the last
word: no fallback, or a fallback that resolves to nobody, still refuses, since
OpenRegister's resume guard lets ANYONE answer a step that names no assignee.

The same sweep found the bezwaar-advice flow dead
-------------------------------------------------
`register.d/72-committees-to-decidiq.json` ships two `dossiq.askPerson` nodes
that declared no assignee AT ALL. validateConfig refuses that outright, so
every bacAdviceRequest ever created failed its run at the first human step.
CaseFlowDeclarationTest could not see it: it read the case flow alone. It now
walks every declaration file, the way its edge-condition test already did.

Both steps go to `bezwaarcommissie`, a group kept separate from `behandelaars`
on purpose: Awb art. 7:13 requires the advisory committee to be independent of
the officials who handled the case. The flow's own description names the
secretary and the chair, and both uids exist, on the bezwaaradviescommissie.
They are not reachable from here: the item json is the bacAdviceRequest, whose
`committee` is a bare reference the trigger does not expand, so
`{{ committee.secretary }}` would resolve to nothing on every single run and
quietly mean the fallback. The group is the honest principal until that
reference is expanded into the run.

Three templates a rename left behind
------------------------------------
The reported VoorstelDetail defect retired with the whole voorstel model in
af1ff19 (#1796). Its SHAPE had not: a `vue/no-undef-properties` sweep of
`src/**` found three live components where a Dutch name was replaced by an
English one in the script and left standing in the template. Vue resolves an
unknown root identifier to `undefined` and says nothing.

  - DsoCaseDetail renamed its prop to `case` and its id computed to `zaakId`,
    while the template still read `zaak.*` and `caseId`. Every field rendered
    blank and the three sub-dialogs got an empty id. `case` is a JS RESERVED
    WORD, so a template cannot name that prop at all: the `zaak` computed is
    the only way in, not a nicety.
  - SamenwerkverzoekDialog renamed its field to `requestedCompetentAuthority`
    and left `aangezochtBevoegdGezag` in the submit button's `:disabled`. The
    button was pinned disabled; the dialog could not be submitted at all.
  - BeschikkingComposerDialog renamed its field to `rationale` and left the
    textarea writing to `motivering`, so typing a motivering did nothing and
    the composed decision went out without one.

tests/vitest/dialogTemplateBindings.spec.js MOUNTS all three with a realistic
object. A shallow mount never evaluates the template, which is the only place
these defects exist. All seven assertions red on the unfixed components.

What was already gone
---------------------
The voorstel detail page and `concludeParafering` both retired in af1ff19
(#1796, "a voorstel is a case, not a record of its own"), which deleted 68
files: the proposal/parafeerroute/parafeeractie/paraferingAuditEntry schemas,
the /voorstellen pages and the parafering runtime across 21 PHP classes. The
acceptance rig ran against a build from before it. No surviving transition in
the tree has concludeParafering's shape: of 80 declared lifecycle transitions,
the 76 without a `requires` are ordinary steps a person is meant to click.
@rubenvdlinde
rubenvdlinde merged commit a98c715 into development Sep 4, 2026
49 checks passed
@rubenvdlinde
rubenvdlinde deleted the fix/askperson-assignee-fallback-and-template-renames branch September 4, 2026 14:51
@github-actions

github-actions Bot commented Sep 4, 2026

Copy link
Copy Markdown
Contributor

Quality Report — ConductionNL/dossiq @ a18ea34

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 ✅ 541/541
app:check-code ⏭️
info.xml
REUSE
PHPUnit
Newman ⏭️
Playwright ⏭️ deferred — runs on the promotion into beta/main, not on a pull request into development
Hydra gates

Quality workflow — 2026-09-04 14:51 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