# WP-60 — Write-divergence resilience (local + ZGW writes) Status: todo Phase: 10 — OpenZaak production hardening ## Why A citizen action today does a local `Aanvraag`/`Document` write and a paired ZGW write (create zaak/status/document); these aren't transactional. If the ZGW call fails after the local write succeeds (or vice versa), the two diverge silently — `openzaak-integration.md` flags this explicitly as "acceptable for a demo backend; a production arc needs retry/reconciliation or an outbox." This is the one genuine correctness gap standing between the current integration and something safe to call production. ## Read first - [openzaak-integration.md](../reference/openzaak-integration.md) (the section discussing this gap) - `backend/src/BigRegister.Api/Data/ApplicationStore.cs`, `Zgw/OpenZaakZaakSource.cs` (the two write sides) - [ADR-0005 — OpenZaak behind the BFF](../reference/architecture/0005-openzaak-behind-bff.md) ## Decisions Intentionally left open for kickoff — this is exactly the kind of ambiguous-root-cause, multi-file design call the `planner` agent should make, not something pre-decided here. Options to weigh at kickoff: - (a) an outbox table — write local + an outbox row in one local transaction, a background worker drains the outbox to ZGW with retry. - (b) a simpler synchronous retry-with-backoff at the call site, plus a reconciliation job that periodically diffs local vs. ZGW state and flags/repairs divergence. Pick the smaller one that closes the gap — don't build a generic outbox framework if a bounded retry+reconcile suffices for this POC's actual write volume. ## Files Likely `Data/ApplicationStore.cs`, a new reconciliation/outbox mechanism, `Zgw/OpenZaakZaakSource.cs`, `Program.cs` (background job registration if needed). ## Steps 1. Design review with the `planner` agent — pick outbox vs. retry+reconcile. 2. Implement the chosen mechanism for the create-zaak and status-transition write paths. 3. Add a test that simulates a ZGW failure mid-write and asserts the system recovers (retries successfully, or is left in a detectably-inconsistent-but-flagged state) rather than silently diverging. ## Acceptance criteria - [ ] A simulated ZGW failure after a successful local write no longer leaves permanent silent divergence — either it retries to consistency or the divergence is detectable/flagged. - [ ] No new synchronous latency added to the happy path beyond what the chosen mechanism requires. ## Verification A new integration test that fails a stubbed ZGW call mid-write and asserts recovery/flagging behavior; `cd backend && dotnet test`. ## Out of scope A general-purpose outbox framework reusable beyond this one write pair (YAGNI unless a second write pair appears — note WP-66 is exactly that second pair, so revisit scope if WP-66 lands first); UI surfacing of reconciliation state (backend-only fix for now). ## Risks Over-building this (a generic outbox/saga framework) for a POC's actual write volume — ladder check at kickoff: does a bounded retry + periodic reconcile job cover it before reaching for an outbox table?