Skip to content

feat(decision-document): one decision lifecycle configured per kind on the case type (C03) - #3668

Merged
rubenvdlinde merged 8 commits into
developmentfrom
refactor/r5-decision-document
Oct 12, 2026
Merged

rubenvdlinde merged 8 commits into
developmentfrom
refactor/r5-decision-document

Conversation

@rubenvdlinde

Copy link
Copy Markdown
Contributor

Refactor lane R5, capability C03 (decision document) of hydra procedures-are-configuration (ADR-118, decisions 182, 184, 185).

Stacked on #3665 (C04 frozen record), which is stacked on #3624 (L1, DocumentSeriesNumberer). Land #3624, then #3665, then this. Until then this diff shows their files too.

What it adds

  • Service/DecisionDocument/DecisionDocumentDefinition: decisionDocuments.<kind> on the case type: template, required fields, rules (OpenRegister conditions, may use named conditions), remedy clause (required by default), signed, authority act, decidiq decision kind, number series, frozen statuses. An incomplete block is refused. decisionTypes (ZGW besluittype refs) is left alone; see Q-R5-1.
  • DecisionDocumentValidator: lists every problem; a rule that cannot be evaluated counts as failed and says so (ADR-005). Uses OpenRegister's NamedConditionEvaluator, resolved lazily.
  • DecisionDocumentLifecycle: draft; draft > approved > signed > sent > archived (approved may go back to draft). Approval needs a valid document and, when the kind names an act, the delegated-authority check. Signing needs the signature and issues the series number once. Returns the fields to write; the caller saves through OpenRegister.
  • DecisionAuthority: the seam to lane R9's delegated-authority guard (C17, dossiq#3642). Nothing binds it yet, so approving a kind that names an act is refused with 503 decision-authority-unavailable, never let through. R9 binds its guard under this interface when refactor(mandates): one transition guard asks decidiq for delegated authority #3642 lands (noted in refactor STATE).
  • freezeDefinition(): the kind's frozen-record entry (C04), so a signed decision is held still through every writer.
  • register.d/39-decision-documents.json (+ digest), spec decision-document (REQ-DDC-001 to 005), change archived as 2026-10-10-decision-document-capability.

Nothing moves in this PR. BeschikkingService and family, Subsidie\BeschikkingService, WOODecisionService and Bezwaar\DecisionService move onto this in later R5 PRs, after L10, L4, L6, L1 and #3572 land.

Verified (targeted; no check slot started within 10 minutes, so CI is the full check)

  • red first: the test file without the lifecycle class, exit 2 (Class ... DecisionDocumentLifecycle not found)
  • phpunit tests/Unit/Service/DecisionDocument tests/Unit/Settings: 310 tests, exit 0 (incl. SchemaVersionFloorTest with the new fragment)
  • phpcs, phpmd, phpstan, psalm, php-cs-fixer --dry-run on lib/Service/DecisionDocument: all exit 0
  • composer check:strict not run locally (memory under the slot's 10 GB floor): CI is the full check.

No .hydra/procedure-names.json in dossiq yet, so nothing to shrink.

…ction is a numbered successor

Decision 167: one running number per organisation per year (B-2026-000123),
written by compose() through OpenRegister's SequenceService with the case's
organisation in the scope key; no number, no beschikking (503). A correction
or withdrawal is issued through POST /api/beschikkingen/{id}/successor
(per-case mutation access): a new draft with its own number that points at
the original, which gains a write-once supersededBy. reference and supersedes
are frozen after signing. beschikking schema 1.1.0, info.xml bumped.

compose() no longer writes an empty addressee/decision as a JSON list against
type: object (found by validating the payload against the real schema).

Archives frozen-beschikking-and-numbered-successor; the chain on the case page
moves to beschikking-chain-on-the-case (board DqZaakBesluiten, decision 162).
…e, and the change is archived

Decision 168 (Q-dossiq-L1-3): who may accept or refuse a hand-over for a unit
is whoever has mutation access on the case; no action registry. Collecting
that evidence found accept() and refuse() looking the request up by id alone
while the controller authorised the caller on the URL's case, so mutation
access on one case let a user answer, and on accept move, another. Both now
take the authorised caseId and treat a request on another case as unknown.
New CaseTakeoverControllerTest; cross-case test mutation-checked.

Task 2.4 ticked, REQ-CUS-02 states the rule, deltas folded into case-management
and dossiq-sociaal-domein-avg-consent, @SPEC tags repointed, change archived.
screens.md: design backlog DqZaakBehandelketen (decision 157).
… document series (decision 182)

DocumentSeriesNumberer issues <prefix>-<year>-<six digits> per series, per
organisation, per year; the case type configures the prefix under
documentSeries.<series>.prefix (register.d/39-document-series.json).
Beschikkingen are the series 'beschikking', default prefix B. The refusal
rule is document-number-unavailable. No new procedure-named classes: the
successor moves into BeschikkingService::issueSuccessor() and the endpoint
into BeschikkingController::successor(); BeschikkingNumberer,
BeschikkingSuccession and BeschikkingSuccessorController are gone. Generic
requirement in openspec/specs/numbered-document-series.
…any record kind (C04)

FreezeDefinition, FreezeRegistry (app config frozen_records), FrozenRecordGuard,
RecordSeal and FrozenRecordSuccessor, enforced by FrozenRecordListener on
OpenRegister's updating and deleting events. Capability C04 of the
procedures-are-configuration refactor (decisions 182, 184, 185); unit tests
on a neutral fixture record kind. Nothing moves yet. Stacked on #3624.
…n the case type (C03)

DecisionDocumentDefinition (decisionDocuments.<kind>), DecisionDocumentValidator
(required fields, remedy clause, OpenRegister named-condition rules, failing
closed), DecisionDocumentLifecycle (draft, approve with delegated authority,
sign with the series number, send, archive) and the DecisionAuthority seam for
lane R9's guard. A signed decision is a frozen record (C04). Capability C03 of
the procedures-are-configuration refactor; unit tests on a neutral fixture
case type. Nothing moves yet. Stacked on #3665 (C04) and #3624.
@rubenvdlinde
rubenvdlinde merged commit 484d76e into development Oct 12, 2026
4 checks passed
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