S-10a, the workflow/timeout spine of the (split) document-upload slice: the registratie process
now parks at a WachtOpDocumenten user task with an interrupting P30D boundary timer. When
the documents arrive the task completes and the process continues into the diploma routing (S-13) →
Beoordelen; if the 30 days lapse, the timer cancels the wait, runs a RegistratieVerlopen
external-worker task, and the domain expires the aggregate to a new terminal status Verlopen.
Backend only — the real upload trigger (portal → BFF → ACL → Documenten API) is S-10b (#103).
Mechanism recorded in ADR-0017; opened as proposal #104. Mirrors the S-14 escalation
(boundary-timer + external-worker) and S-11 withdrawal (interrupting cancel) patterns.
Definition of Done
Linked Gitea issue (above).
Failing test committed before the implementation (red→green pairs per layer).
Implementation makes the test pass.
Conventional Commits referencing the issue (refs #102).
CI green — all Gitea Actions jobs (pending on this PR).
docker compose up health unaffected (no new services; deploy path unchanged).
Domain (Registration.Expire() + Verlopen), application (ExpireRegistrationWorker), infra (RegistratieVerlopenProcessor/Pump, IRegistratieVerlopenClient, Flowable
acquire/complete + CompleteDocumentWaitAsync) — the timeout counterpart to the OpenZaak/escalation
worker trios; idempotent per §8.6.
BPMN verified live against a flowable-rest probe: complete WachtOpDocumenten → routes to
Beoordelen; fire the P30D timer → RegistratieVerlopen job (carrying registrationId) + the wait
task cancelled. verify-domain exercises both branches in-stack (completes the wait in every existing
block; fires the timer and asserts Verlopen in a new block).
Scope boundary: on expiry the aggregate goes Verlopen and the process ends, but the ZGW zaak
is not yet set to a cancellation status — that needs a new ACL method + statustype seeding and is
folded into S-10b (noted in ADR-0017).
CompleteDocumentWaitAsync is built and HTTP-tested here but not yet called from a domain endpoint;
S-10b wires the upload trigger to it.
## What & why
S-10a, the **workflow/timeout spine** of the (split) document-upload slice: the registratie process
now parks at a **`WachtOpDocumenten`** user task with an **interrupting `P30D` boundary timer**. When
the documents arrive the task completes and the process continues into the diploma routing (S-13) →
Beoordelen; if the 30 days lapse, the timer cancels the wait, runs a `RegistratieVerlopen`
external-worker task, and the domain expires the aggregate to a new terminal status **`Verlopen`**.
Backend only — the real upload trigger (portal → BFF → ACL → Documenten API) is S-10b (#103).
Closes #102
Mechanism recorded in **ADR-0017**; opened as proposal #104. Mirrors the S-14 escalation
(boundary-timer + external-worker) and S-11 withdrawal (interrupting cancel) patterns.
## Definition of Done
- [x] Linked Gitea issue (above).
- [x] Failing test committed before the implementation (red→green pairs per layer).
- [x] Implementation makes the test pass.
- [x] Conventional Commits referencing the issue (`refs #102`).
- [x] CI green — all Gitea Actions jobs (pending on this PR).
- [x] `docker compose up` health unaffected (no new services; deploy path unchanged).
- [x] Docs updated (ADR-0017, demo-script, BACKLOG split).
- [x] ADR added (`docs/architecture/adr-0017-document-wait-timeout-cancellation.md`).
- [x] Demo note in `docs/demo-script.md`.
## Notes for reviewers
- **Domain** (`Registration.Expire()` + `Verlopen`), **application** (`ExpireRegistrationWorker`),
**infra** (`RegistratieVerlopenProcessor`/`Pump`, `IRegistratieVerlopenClient`, Flowable
acquire/complete + `CompleteDocumentWaitAsync`) — the timeout counterpart to the OpenZaak/escalation
worker trios; idempotent per §8.6.
- **BPMN** verified live against a `flowable-rest` probe: complete `WachtOpDocumenten` → routes to
Beoordelen; fire the P30D timer → `RegistratieVerlopen` job (carrying `registrationId`) + the wait
task cancelled. `verify-domain` exercises both branches in-stack (completes the wait in every existing
block; fires the timer and asserts `Verlopen` in a new block).
- **Scope boundary:** on expiry the aggregate goes `Verlopen` and the process ends, but the ZGW *zaak*
is not yet set to a cancellation status — that needs a new ACL method + statustype seeding and is
folded into S-10b (noted in ADR-0017).
- `CompleteDocumentWaitAsync` is built and HTTP-tested here but not yet called from a domain endpoint;
S-10b wires the upload trigger to it.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
S-10 (#11) spanned six net-new surfaces incl. a new ZGW boundary — too large
for one slice (§13). S-10a is the workflow/timeout spine (backend); S-10b is
the document-upload vertical (ACL Documenten API + portal). #11 closed.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
RED: Registration.Expire() moves an open registration to a new terminal
Verlopen status, needs no zaak, is idempotent on redelivery, and is rejected
once the registration has been decided or withdrawn.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Adds the terminal Verlopen status and Expire(), reusing the RequireOpenForDecision
guard so only an INGEDIEND/IN_BEHANDELING registration can lapse; idempotent once
Verlopen.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
RED: ExpireRegistrationWorker loads the registration a RegistratieVerlopen job
correlates to and expires it (idempotent on redelivery, throws on unknown so the
job is redelivered); RegistratieVerlopenProcessor drains the parked jobs and
completes each, leaving a failing one un-completed (§8.6). Mirrors the OpenZaak
and escalation worker/processor pairs.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Adds RegistratieVerlopenJob, IRegistratieVerlopenClient, the ExpireRegistrationWorker
application handler, and the RegistratieVerlopenProcessor drain loop — the timeout
counterpart to the OpenZaak/escalation worker trios.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
RED: the Workflow Client posts the RegistratieVerlopen topic and parses the
correlated registration id, completes the expiry job, and (documents-in-time)
completes the WachtOpDocumenten user task in the instance — best-effort no-op if
that task is no longer open.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
FlowableWorkflowClient implements IRegistratieVerlopenClient (acquire/complete the
RegistratieVerlopen jobs) and CompleteDocumentWaitAsync (complete WachtOpDocumenten,
best-effort). Wires the RegistratieVerlopenProcessor + hosted RegistratieVerlopenPump
into the domain host and excludes the pump from mutation (like the other pumps).
Fakes updated for the new IWorkflowClient member.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Inserts a WachtOpDocumenten user task after OpenZaakAanmaken with an interrupting
P30D boundary timer: "documents received" completes it and the process continues to
the diploma routing; on timeout the RegistratieVerlopen external-worker task runs
and the process ends as verlopen (ADR-0017). Verified live against flowable-rest:
complete -> routes to Beoordelen; timer fire -> RegistratieVerlopen job (carrying
registrationId) + the wait task cancelled.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
BDD for S-10a: a registration parked at WachtOpDocumenten expires to VERLOPEN when
the 30-day timer fires, and does NOT expire when documents arrive first. Drives the
real RegistratieVerlopenProcessor + ExpireRegistrationWorker against an in-memory
Flowable stand-in, mirroring the escalation feature.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Every registration now parks at WachtOpDocumenten first, so each existing block
completes that task (documents received) before expecting Beoordelen/CBGVAdvies.
Adds a timeout block: a registration whose documents never arrive has its P30D
timer fired via the management-API move idiom, and the domain expires it to VERLOPEN.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Records the interrupting P30D WachtOpDocumenten timer, the RegistratieVerlopen
worker, and the new terminal Verlopen status; notes the S-10a/S-10b boundary
(ZGW zaak-close deferred). Demo covers both branches.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
RED: the ProvideDocuments use case completes the document wait via the Workflow
Client, owner-scoped by the caller's bsn (a different bsn is NotFound), and is
best-effort when no process was started yet — mirroring WithdrawRegistration.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The provide-documents use case completes the WachtOpDocumenten task via the
Workflow Client (owner-scoped by bsn, best-effort), exposed as an owner-scoped
domain endpoint. This is the trigger that unblocks the process; the real file
upload + ZGW storage lands in S-10b.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
RED: POST /self-service/registrations/{id}/documents requires a digid token, takes
the bsn from the token, forwards to the domain, and relays the domain's 404 for an
unknown/not-owned registration. Adds the IDomainClient.ProvideDocumentsAsync port +
client + fake; the endpoint itself follows.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Authenticated self-service endpoint that takes the bsn from the DigiD token,
forwards "documenten aanleveren" to the domain, and relays 404 for an unknown or
not-owned registration. Regenerates the committed openapi.json.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
RED: after submitting, a "Documenten aanleveren" action posts to the BFF keyed by
the reference and the page confirms; a failure surfaces an alert and keeps the
action. Regenerates the api-client from the updated BFF spec.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
RED: if the citizen withdrew while parked at WachtOpDocumenten, the RegistratieVerlopen
job finds a terminal (INGETROKKEN) aggregate; the worker must no-op and let the job
complete, not throw into a redelivery loop.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Expire only a still-open (INGEDIEND/IN_BEHANDELING) registration; an already
resolved one (expired, or withdrawn/decided while it waited) is left untouched so
the job completes without violating the aggregate invariant (§8.6). Closes the
S-10a/S-11 race where a withdrawal-while-waiting would loop the expiry job.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
After submitting, the zorgprofessional supplies their documents; the page posts to
the BFF keyed by the reference and confirms ("Uw documenten zijn aangeleverd"), with
a surfaced failure + retry. The registration e2e provides documents before the
behandelaar step, since the process now parks at WachtOpDocumenten first.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Records why the provision trigger (domain + BFF + portal 'Documenten aanleveren')
lives in S-10a — the WachtOpDocumenten gate would otherwise leave the e2e red — and
narrows S-10b to the real ZGW document storage. Notes the withdrawal-while-waiting
follow-up.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The process only parks at WachtOpDocumenten once the OpenZaak worker has opened the
zaak, so providing documents immediately after submit raced the wait and no-op'd.
Check the openbaar INGEDIEND row on a second page (proving the zaak is open, hence
the process is at the wait) while the self-service tab keeps its submitted state,
then provide documents there — unblocking the werkbak.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
not
merged commit 4777ff2b1d into main2026-07-20 09:42:03 +00:00
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
What & why
S-10a, the workflow/timeout spine of the (split) document-upload slice: the registratie process
now parks at a
WachtOpDocumentenuser task with an interruptingP30Dboundary timer. Whenthe documents arrive the task completes and the process continues into the diploma routing (S-13) →
Beoordelen; if the 30 days lapse, the timer cancels the wait, runs a
RegistratieVerlopenexternal-worker task, and the domain expires the aggregate to a new terminal status
Verlopen.Backend only — the real upload trigger (portal → BFF → ACL → Documenten API) is S-10b (#103).
Closes #102
Mechanism recorded in ADR-0017; opened as proposal #104. Mirrors the S-14 escalation
(boundary-timer + external-worker) and S-11 withdrawal (interrupting cancel) patterns.
Definition of Done
refs #102).docker compose uphealth unaffected (no new services; deploy path unchanged).docs/architecture/adr-0017-document-wait-timeout-cancellation.md).docs/demo-script.md.Notes for reviewers
Registration.Expire()+Verlopen), application (ExpireRegistrationWorker),infra (
RegistratieVerlopenProcessor/Pump,IRegistratieVerlopenClient, Flowableacquire/complete +
CompleteDocumentWaitAsync) — the timeout counterpart to the OpenZaak/escalationworker trios; idempotent per §8.6.
flowable-restprobe: completeWachtOpDocumenten→ routes toBeoordelen; fire the P30D timer →
RegistratieVerlopenjob (carryingregistrationId) + the waittask cancelled.
verify-domainexercises both branches in-stack (completes the wait in every existingblock; fires the timer and asserts
Verlopenin a new block).Verlopenand the process ends, but the ZGW zaakis not yet set to a cancellation status — that needs a new ACL method + statustype seeding and is
folded into S-10b (noted in ADR-0017).
CompleteDocumentWaitAsyncis built and HTTP-tested here but not yet called from a domain endpoint;S-10b wires the upload trigger to it.
🤖 Generated with Claude Code
RED: POST /self-service/registrations/{id}/documents requires a digid token, takes the bsn from the token, forwards to the domain, and relays the domain's 404 for an unknown/not-owned registration. Adds the IDomainClient.ProvideDocumentsAsync port + client + fake; the endpoint itself follows. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>After submitting, the zorgprofessional supplies their documents; the page posts to the BFF keyed by the reference and confirms ("Uw documenten zijn aangeleverd"), with a surfaced failure + retry. The registration e2e provides documents before the behandelaar step, since the process now parks at WachtOpDocumenten first. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>