docs(runbooks): add form-builder binding failures section

Per RFC-WS-6 §Q3 v1.3 + ARCH-BINDINGS §11.

Nieuwe runbook-sectie §7 (na §6 Audit trail) die de triage-flow
documenteert wanneer GlitchTip een FormBindingApplicatorException
event opbrengt:

- §7.1 failure_response_code classificatie (schema_config_error /
  temporary_error / data_integrity_error / unknown_error) drijft het
  initiële triage-pad
- §7.2 form_schema.has_public_token tag onderscheidt klant-zichtbare
  failures (alert-waardig) van organizer-driven failures (admin-UI only)
- §7.3 retry/dismiss decision-matrix met form-failures:retry artisan
  command + DismissalReasonType enum cases
- §7.4 severe-failure escalatie criteria (>10/uur op één schema = P1)
- §7.5 cross-references naar RFC, ARCH-BINDINGS, en erasure-runbook

Companion van de operationele GlitchTip alert-rule (apart geconfigureerd
in de GlitchTip web UI op monitoring.hausdesign.nl).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
2026-05-08 08:57:02 +02:00
parent 23a5696288
commit 7ba01a6dfa

View File

@@ -268,3 +268,115 @@ Voor incidents waar 1+ klant geraakt is, log een externe
incident-summary (klant, impact, timeline, fix) in een aparte
incident-tracker — niet in GlitchTip zelf, want GlitchTip data wordt
na 90 dagen gepurged.
---
## §7 Form-builder binding failures
### Context
`FormBindingApplicator` draait synchroon binnen
`ApplyBindingsOnFormSubmit::handle` voor elke form-submission. Als die
throwt — schema-misconfiguratie, infra-issue, data-integrity violation
of deadline-timeout — vangt de listener de throw op, schrijft een
`form_submission_action_failures` rij in de outer-transaction, en zet
`apply_status='failed'` op de parent-submission. GlitchTip vangt het
exception-event op met de `crewli-api` project-tags.
De alert-rule op `apply_status=failed AND form_schema.has_public_token=true`
brengt alleen de klant-zichtbare failures naar boven — organizer-driven
private-form failures zijn zichtbaar in de Form Failures admin-UI zonder
alert, omdat hun blast radius beperkt is tot één organisatie.
Per RFC-WS-6.md §Q3 v1.3 + ARCH-BINDINGS.md §6 (two-transaction pattern)
en §11 (failures lifecycle).
### 7.1 Eerste check — `failure_response_code`
De submission-rij draagt een denormalised classification-token in
`failure_response_code`. Vier waardes:
| Token | Oorzaak | Triage-pad |
|---|---|---|
| `schema_config_error` | Organizer-config issue (renamed kolom, deleted target-entity, ontbrekende identity-key binding die publish-guards toch doorlieten) | Contact de organizer van het schema; **NIET retryen** — retry produceert dezelfde exception |
| `temporary_error` | Infra-issue (DB-connection, lock-for-update wait, deadline-wrapper timeout) | Retry via `php artisan form-failures:retry --id={failure_ulid}` of admin-UI; als een tweede retry óók faalt, escaleer als infra-incident |
| `data_integrity_error` | Data-shape violation (type-mismatch, FK-violation, soft-deleted target-entity) | Onderzoek de failing binding's `target_entity` + `target_attribute` in `form_field_bindings`; lost meestal op als een schema-config issue verkleed als data |
| `unknown_error` | Iets buiten de binding-applicator hiërarchie (raw `\Throwable`, `IdentityMatchInvariantViolation`) | Triage via de GlitchTip exception-trace; dit is dev-investigation territory |
De exception-class op de action-failure rij is de canonical truth;
`failure_response_code` is de afgeleide classificatie die door de
response-renderer gebruikt wordt.
### 7.2 Tweede check — public-token presence
De GlitchTip event-tag `form_schema.has_public_token` onderscheidt:
| Tag-waarde | Betekenis | Severity |
|---|---|---|
| `true` | Public submission flow (vrijwilliger registratie-window, public form-fill) | Customer-impact bevestigd; mobiliseer binnen minuten tijdens active festival registratie |
| `false` | Organizer-driven private flow (managed crew roster, internal data import) | Blast radius beperkt tot één organisatie; onderzoek binnen uren |
### 7.3 Retry vs dismiss
Gebruik het artisan-command:
```bash
# Retry een enkele failure
php artisan form-failures:retry --id={failure_ulid}
# Retry alle open failures voor een submission
php artisan form-failures:retry --submission={submission_ulid}
# Dry-run eerst als onzeker
php artisan form-failures:retry --id={failure_ulid} --dry-run
```
Wanneer wel retryen:
- `temporary_error` — ja, bijna altijd; de deadline-wrapper of infra-hiccup
is waarschijnlijk inmiddels weg.
- `unknown_error` — alleen na dev-investigatie die bevestigt dat de
throw transient was.
Wanneer dismissen:
- `schema_config_error` nadat de organizer het schema heeft gefixt en
opnieuw heeft gesubmit — de originele failure-rij blijft staan als
audit, dismiss met reden `schema_deleted` of `binding_removed` per
het relevante geval.
- `data_integrity_error` nadat dev-investigatie het herleidt tot een
one-off data-state die niet meer geldt — dismiss met
`data_quality_issue`.
- Iedere failure voor een submission die de organizer expliciet als
duplicaat geclassificeerd heeft — dismiss met `duplicate_submission`.
De `DismissalReasonType` enum heeft zes cases; `OTHER` vereist een
free-text note. Per ARCH-BINDINGS §11.3.
### 7.4 Severe-failure escalatie
Als GlitchTip `>10` events in 1 uur toont gescoped op één
`form_schema_id`, behandel als P1:
1. Acknowledge de alert in het team-channel.
2. Identificeer het schema via `form_submission.form_schema_id` op een
willekeurige failed submission.
3. Pauzeer publieke toegang tot het schema tijdelijk — disable het
public token tot de root-cause geïdentificeerd is.
4. Standaard incident-triage geldt verder.
Dit patroon wijst bijna altijd op een publish-guard gap: een
schema-configuratie die de guards hadden moeten weigeren is er toch
doorheen geslipt, en elke submission ertegen produceert dezelfde
exception.
### 7.5 Cross-references
- [`RFC-WS-6.md`](../RFC-WS-6.md) v1.3.1 — volledige architectuur, in
het bijzonder §Q3 v1.3 additions.
- [`ARCH-BINDINGS.md`](../ARCH-BINDINGS.md) v1.2 — §6 two-transaction
pattern, §11 failures lifecycle, §11.3 dismissal reasons.
- [`observability-erasure.md`](./observability-erasure.md) — Art. 17
procedure als triage in een GDPR-deletion-verzoek omdraait.
- BACKLOG `TECH-CHANNEL-AUTH-ORG-ADMIN` — bekende follow-up voor
live-update channel-auth uitbreiding (org-admin scope).