The ACL upserts with PATCH, so every approval notification carries actie `partial_update`.
Accepting it makes the INGEDIEND → INGESCHREVEN transition project. `update` stays accepted
so a PUT-shaped write behaves the same; `destroy` deliberately does not — removing a
registration from the public register is its own decision, not a side effect of this one.
ADR-0030 records why the actie list is what it is.
The e2e reached INGEDIEND but never INGESCHREVEN. NRC's own log says why:
{"event": "notification_received", "action": "partial_update",
"resource_url": "http://objecten.local:8000/api/v2/objects/a9a7f125-..."}
The ACL PATCHes the object on approval. DRF routes a PATCH through the notifying `update()`
but reports the action as `partial_update`, so accepting only `create`/`update` drops every
approval on the floor — the exact state change the slice exists to project.
Red: the approval case is now a Theory over both acties, and the partial_update one fails.
The projection check now opens its zaak through the ACL, which puts it in the same bind
run-domain-check.sh already handles:
400 {"name":"zaaktype","code":"bad-url","reason":"Voer een geldige URL in."}
OpenZaak reflects the request Host into the zaaktype `url` it returns and then rejects that
same URL on zaak-create when the host is single-label. The stack's ACL is configured with
`http://openzaak:8000/`, so it has to be recreated with ACL_OPENZAAK_BASEURL pointed at
OpenZaak's container IP first — the mechanism compose already documents on that variable.
Same class of constraint as the objecten.local alias (ADR-0029), and the third module now
known to reflect a request Host into data another module validates.
Bring-up timed out with
TIMEOUT: 'objecten' not healthy (status=none)
while the very `docker ps` it dumps showed infra-objecten-1 "Up 9 minutes (healthy)".
`--filter name=` is a substring match, so `objecten` also matches objecten-db,
objecten-redis and (since #152) objecten-celery. `head -1` took whichever docker listed
first; the celery worker declares no healthcheck, so it inspected as status=none and the
wait sat there until the deadline.
Not objecten-specific — `objecttypen` matches objecttypen-db the same way. The bug has been
latent since those services landed and was decided by listing order, which is why it only
surfaced now. Anchored on the replica suffix, matching both docker compose and
podman-compose naming — the same anchoring the verify check scripts already use.
For a `resource: object` notification Objecten sends the object as both hoofdObject and
resourceUrl — the object *is* the main resource — so `HoofdObject ?? ResourceUrl` was a
branch that can never take its left side and that no test could distinguish. It came across
from the zaken path, where hoofdObject genuinely differed (the zaak behind a status).
Tests unchanged and green.
Comment only — the assertions were already reference-matched and hold unchanged. Names the
new chain (ACL → Objecten → NRC → event-subscriber → projection) so the INGEDIEND assertion
reads as the proof of the re-source that it now is.
Records the re-source and the three decisions inside it: the ACL writing an INGEDIEND record
on submit (without which re-sourcing silently drops every submitted registration), the dedup
key being the projected row rather than the notification, and the notification log holding
the row rather than the event.
Closes out ADR-0028's stated direction and the caveat it left open — the register record was
written but not yet read, and the two had to agree; there is now one source.
Completes the re-source (ADR-0030): the Event Subscriber's abonnement moves from `zaken` to
`objecten`, in both the local stack's `nrc-subscribe` and the CI projection check. The
OpenZaak → NRC check keeps its own `zaken` abonnement — OpenZaak still publishes, nothing
in the product listens.
- register-abonnement.py subscribes to `objecten`, and now treats the kanaal as part of
"already current" — an abonnement left from before this slice points at the right callback
but the wrong kanaal, and would never have been replaced on IP alone.
- run-projection-check.sh opens its zaak *through the ACL* instead of straight against
OpenZaak, because the ACL is what writes the register record the projection is now derived
from. A zaak created behind the ACL's back produces no row — which is the re-source working.
- The acceptance scenario is restated in register terms and gains the approval case: the same
row moving INGEDIEND → INGESCHREVEN is now one registration's record being updated, not two
unrelated ZGW events.
HandleAsync reads the record at the notification's object URL through the ACL and writes it
to the projection verbatim — the record already carries id, status and reference, so there
is no mapping and no enrichment hop.
The dedup key is the object plus the state that write projects. It cannot be the object URL
alone (the ACL upserts one object per registration, so submit and approval notify about the
same URL and the approval would be swallowed), nor include the actie (a retried approval is
a second `update`). Keying on the projected row collapses redeliveries and lets genuine
state changes through — §8.6.
Ports, schema and failing tests for the subscriber half of S-19b-2, ahead of the
implementation.
The subscriber now listens on the `objecten` kanaal instead of `zaken`. An Objecten
notification carries no record data — only the object URL — so the record is read back
through the ACL (§8.1), and the zaak-shaped surface goes away: IsZaakCreated /
IsZaakStatusSet / ZaakUrl / ZaakId and ToEntry's `Resource == "status"` mapping are replaced
by IsRegisterRecordWritten + ObjectUrl.
The notification log now holds the projected row itself (register id, status, reference),
so a rebuild is a replay with no mapping rules and no upstream reads. The migration drops
the old columns rather than renaming them — EF scaffolded renames that would have carried
ZGW values into columns meaning something else — and empties both tables, since a
pre-slice row is neither reprojectable nor re-derivable from the new source.
Red: HandleAsync recognises a register write but does not yet read or project it, so the
seven projection assertions fail on an empty store.
- OpenZaakAsync upserts a RegisterRecord with status INGEDIEND after opening the zaak,
keyed on the same zaak id approval later upserts to INGESCHREVEN. The reference comes
from the registration, so this path needs no ZGW read-back.
- ObjectenGateway.GetAsync fetches an object by the URL a notification carried — no
objecttype resolution, no search — and reads 404 as "no record" rather than an error.
- POST /register-records/read exposes it to the Event Subscriber, which may not talk to
Objecten itself (§8.1).
Ports and failing tests for the ACL half of S-19b-2, ahead of the implementation.
Once the projection is sourced from Objecten (ADR-0028's stated direction), a submitted
registration has to exist in the register the moment the zaak is opened — otherwise
re-sourcing silently drops every INGEDIEND row, since today only approval writes a record.
So `OpenZaakAsync` gains a second write, and approval upserts that same record to
INGESCHREVEN.
The subscriber gets only an object URL on an `objecten` notification (the payload carries no
record data) and may not read Objecten itself (§8.1), so `IRegisterRecordGateway` gains a
read and `AclService` exposes it.
Red:
- AclService does not yet write on open → the record assertion fails on an empty list.
- ObjectenGateway.GetAsync is a shell throwing NotImplementedException; its tests pin the
contract: fetch the object URL directly (no objecttype resolution, no search), the CRS
header a geo API requires, static Token auth, and a 404 read as "nothing to project"
rather than an error (§8.6).