docs(form-builder): WS-5c sign-off — SCHEMA v2.5 + ARCH v1.7 §8 + addendum Q3

SCHEMA v2.5:
- form_fields: conditional_logic row removed; cross-reference note
  added pointing at the two new tables and the addendum Q3 WS-5c
  Uitvoering (no library mirror).
- New sections: form_field_conditional_logic_groups (tree nodes,
  adjacency-list via parent_group_id) and
  form_field_conditional_logic_conditions (leaves; value JSON
  nullable for empty/not_empty). Both tables use the Q2 declarative
  FK-chain resolver via tenantScopeStrategy() — group chain 3 hops,
  condition chain 4 hops (fits the WS-5c-raised cap of 5).

ARCH v1.7 §8 restructured into sub-sections mirroring the §17.4 /
§17.5 pattern:
- 8.1 Tree structure (read-side contract)
- 8.2 Relational tables (column specs, cascade, scope)
- 8.3 Service boundary (logicFor/replaceLogic/toJsonShape/
  assertSpecsValid/assertNoCycles)
- 8.4 Operator catalogues (group + comparison)
- 8.5 Cycle detection (contract preserved, implementation moved)
- 8.6 Activity log (dual-events: field.updated +
  field.conditional_logic_replaced; FormField subject only)
- 8.7 Legacy JSON migration (strict dispatch, rollback reversible)

Addendum Q3 extended with "Uitvoering — WS-5c (2026-04-26)":
- No-library-mirror decision reaffirmed (simple FK, no morph)
- Two-table tree-structure rationale (groups + conditions semantic
  purity over single-table mixed-nullables)
- OrganisationScope cap raise 3 → 5, rationale: legitimate 4-hop
  conditions chain + headroom for future deeper trees without
  denormalising form_field_id onto conditions
- Cycle detection migrated to service, contract unchanged
- Snapshot + resource JSON contract byte-identical via toJsonShape
- Strict validator on save at FormRequest boundary
- Scope-sibling discipline: WS-5c adds two FK-chain models (not
  morph); base-class extraction still parked for WS-5d

Sign-off table: WS-5c afronding 2026-04-26 added.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-04-25 00:09:12 +02:00
parent 079d10975b
commit 9e181092fc
3 changed files with 260 additions and 14 deletions

View File

@@ -186,6 +186,30 @@ WS-5b splitst `form_fields.validation_rules` en `form_field_library.validation_r
**Afronding WS-5b.** 5 commits, baseline tests 1047 → volledig groen na commit 5. WS-5b is hiermee compleet; scope-sibling extractie en WS-5c (`conditional_logic`) / WS-5d (`options`) zijn separate work packages.
### Uitvoering — WS-5c (2026-04-26)
WS-5c splitst `form_fields.conditional_logic` JSON naar een relationele boom over twee tabellen: `form_field_conditional_logic_groups` (AND/OR nodes met `parent_group_id` voor nesting) + `form_field_conditional_logic_conditions` (leaves met `field_slug` / `comparison_operator` / `value` JSON). Tree-structuur met interleaved mixed children (groups en conditions als siblings onder één ouder), sort-order per positie binnen parent.
**Geen library-mirror — Q3 expliciet.** `form_field_library` draagt geen conditional_logic en komt niet in scope. Eenvoudige FK `form_field_id` op de groups-tabel; geen polymorphic morph, geen `owner_type`/`owner_id` kolommen. `FormFieldService::insertFromLibrary` propageert geen conditional_logic — library-entries dragen die state niet.
**Twee-tabel tree-structuur.** Alternatief (single table met nullable `value`/`operator` per rij-rol) zou semantische vervuiling hetzelfde type rot oproepen dat WS-5b's configs-split weg haalde. Groepen en condities zijn semantisch anders — één tabel per concept, interleaving via sort_order bij read-time in `toJsonShape`. Geen `sort_order` drift tussen siblings.
**OrganisationScope cap verhoging van 3 naar 5.** De conditions-scope-chain (`condition → group → field → schema → organisation_id`) is 4 hops; de group-chain is 3. De oude cap van 3 zou bij conditions throwen via `TenantScopeResolutionException`. Raising naar 5 dekt de ketens en geeft headroom voor toekomstige diepere trees zonder `form_field_id` op conditions te denormaliseren (drift-risico). Infrastructurele wijziging — niet beperkt tot WS-5c; volgende WS's mogen diepere chains declareren als dat architectuur-winst oplevert.
**Cycle detection behoud contract.** `FormFieldService::assertNoConditionalCycle` werd vervangen door `FormFieldConditionalLogicService::assertNoCycles`. Zelfde algoritme (DFS over sibling-adjacency), andere implementatie-site: leest de relationele tree, niet meer JSON. Tree-interne cycles zijn structureel onmogelijk via `parent_group_id` adjacency-list.
**Activity log dual-events.** `field.updated` (met gereconstrueerde `old.conditional_logic` / `new.conditional_logic` via `toJsonShape`) + `field.conditional_logic_replaced` (semantisch). FormField subject-only, conform §6.7 WS-5a en §17.4.2 WS-5b. Library-niveau is silent — er IS geen library conditional_logic om te loggen.
**Snapshot + API resource shape ongewijzigd.** Externe JSON-contract blijft byte-gelijk aan pre-WS-5c via `toJsonShape`. Geen frontend breaking change — de portal's `evaluateConditionalLogic` composable blijft dezelfde `{show_when: {...}}` struct consumeren.
**Strict validator op save (commit 3).** `StoreFormFieldRequest` / `UpdateFormFieldRequest` accepteren `conditional_logic` nu als array-shape, niet JSON string. `after()` hook roept `FormFieldConditionalLogicService::assertSpecsValid` aan en rejects unknown operators, root conditions, empty groups, en malformed children als 422 vóór enige write.
**Drie scope-siblings + twee FK-chain strategy users.** `FormFieldBindingScope` + `FormFieldValidationRuleScope` + `FormFieldConfigScope` zijn nog steeds de drie morph-based UNION-scope near-duplicaten (base-class extractie blijft uitgesteld tot WS-5d). WS-5c voegt twee FK-chain declaratieve strategy models toe (`FormFieldConditionalLogicGroup`, `FormFieldConditionalLogicCondition`) — die tellen niet voor de morph-sibling extractie-discipline. WS-5d's `form_field_options` wordt het vierde polymorphic-morph sibling en beslist het "abstract of niet".
**JSON-kolom gedropt.** `form_fields.conditional_logic` gedropt in `2026_04_26_100003`. Geen library-equivalent om te droppen. Rollback-path: "roll back WS-5c commits 13 samen" — de backfill `down()` reconstrueert JSON terug naar de (via commit 3 rollback teruggekomen) kolom.
**Afronding WS-5c.** 4 commits, baseline tests 1104 → 1148 volledig groen na commit 3 (drop-column). Breaking change acceptance: geen bridging compatibility layer — de portal blijft onaangeraakt omdat het externe JSON-contract identiek is. WS-5d (`options`) is het laatste WS-5-werkpakket.
---
## Q4 — Sanctum `personal_access_tokens`
@@ -300,5 +324,6 @@ WS-1 rapport Categorie D bevindingen die geen architect-beslissing vereisten en
- **Product owner:** akkoord per Bert Hausmans 2026-04-24.
- **WS-5a afronding:** 2026-04-24 — relationele `form_field_bindings` tabel, polymorphic owner, snapshot-parity, JSON-kolommen gedropt.
- **WS-5b afronding:** 2026-04-25 — relationele `form_field_validation_rules` + parallel `form_field_configs` tabel; `validation_rules` JSON-kolommen gedropt; frontend-contract migratie naar canonieke key-namen landed in commit 5.
- **WS-5c afronding:** 2026-04-26 — relationele `form_field_conditional_logic_groups` + `form_field_conditional_logic_conditions` tree-tabellen; simple FK op FormField (geen library-mirror per Q3); `conditional_logic` JSON-kolom gedropt; `OrganisationScope` FK-chain cap verhoogd van 3 naar 5 hops; snapshot + API resource JSON-contract byte-identiek via `toJsonShape`.
Volgende stap: prompt opstellen voor WS-2 (Purpose registry) met Q6-consolidatie als integraal onderdeel van de werkstroom.