From 8a4682ab356aae515b5a2277985fecde6005783b Mon Sep 17 00:00:00 2001 From: "bert.hausmans" Date: Tue, 28 Apr 2026 20:32:35 +0200 Subject: [PATCH] docs: BACKLOG + ARCH-BINDINGS appendix + RFC v1.2 for registry alignment (WS-6) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Two new BACKLOG entries capture the deliberate v1 deferrals: - ARTIST-ADV-BINDING-MODEL — design how artist_advance form data relates to Artist + AdvanceSection entities (when, if ever, an Eloquent Artist class is needed, and whether bindings are even the right abstraction for OUTPUT-shaped advance forms). - FORM-BINDING-JSON-PATH — extend binding registry to support JSON-path attributes (custom_fields.dietary_preferences etc). For v1 the recommendation is TAG_PICKER + tag_categories config. ARCH-BINDINGS.md gets an appendix explaining the v1 scope decisions explicitly: why 'artist' has no registry entries (model class absent + advance forms are OUTPUT-shaped, not provisioning-shaped), why JSON-path attributes are out of scope (v1 is column-level only), and how BindingTypeRegistryConsistencyTest prevents future drift. RFC-WS-6.md → v1.2 with a §3 Q9 addendum tracking the registry alignment + the 3 renames, 5 removals, and 1 new column landed in this branch. Refs: WS-6 sessie 3a binding-target drift audit, sessie 3a.5 cleanup Co-Authored-By: Claude Opus 4.7 (1M context) --- dev-docs/ARCH-BINDINGS.md | 52 +++++++++++++++++++++++++++++++++++++++ dev-docs/BACKLOG.md | 47 +++++++++++++++++++++++++++++++++++ dev-docs/RFC-WS-6.md | 40 ++++++++++++++++++++++++++++-- 3 files changed, 137 insertions(+), 2 deletions(-) diff --git a/dev-docs/ARCH-BINDINGS.md b/dev-docs/ARCH-BINDINGS.md index 52bb2fca..92e4d720 100644 --- a/dev-docs/ARCH-BINDINGS.md +++ b/dev-docs/ARCH-BINDINGS.md @@ -4,6 +4,7 @@ - v0.1 (skeleton) — 2026-04-25 - v0.4 — 2026-04-28 — § 8.2 IDOR class tests (sessie 3a backend hardening) +- v0.5 — 2026-04-28 — Appendix on v1 registry scope (sessie 3a.5 model alignment) - Owner: Bert - Authoritative for the binding pipeline architecture, complementing ARCH-FORM-BUILDER.md §17 and §31. @@ -594,6 +595,57 @@ might never persist on rollback — fixed in WS-6. - **Daily failure digest mailable** — depends on notification framework - **Wall-clock concurrent load testing** — BACKLOG: `LOAD-TEST-FOUNDATION` +## Appendix — v1 binding registry scope + +The `App\FormBuilder\Bindings\BindingTypeRegistry` covers the four +binding-target entities active in WS-6 v1: `person`, `company`, +`user`, and an intentionally-empty `artist` (omitted from registry). + +### Why `artist` has no registry entries in v1 + +The `artists` table exists (since 2026-04-08) and `subject_type='artist'` +is a valid `form_submissions` value. But: + +1. No Eloquent `Artist` model class exists yet. Polymorphic subject + relations work via the morph map (string → table) but cannot be + Eloquent-loaded without a class. +2. The `artist_advance` purpose is OUTPUT-shaped: the advance form + **gathers** information FROM the artist (rider, hospitality, + technical needs) — it does not provision Artist attributes the way + `event_registration` provisions Person attributes. +3. Bindings as a concept may not be the correct abstraction for advance + forms. v2 work tracked via BACKLOG `ARTIST-ADV-BINDING-MODEL`. + +In v1: `artist_advance` schemas may exist with `required_bindings: []`. +PublishGuards enforce only the cross-cutting invariants (no identity +key conflicts, no append-on-scalar, etc). The applicator runs (per +RFC §3 Q4 two-transaction pattern) but resolves bindings to an empty +list, completing in COMPLETED state with zero applications. The +Person/Company/User-level effects of WS-6 (apply_status, action_failures, +retry/dismiss workflows) all apply uniformly. + +### Why JSON-path attributes are not in v1 + +`persons.custom_fields` is a JSON column; semantically a Person can +have `dietary_preferences` (etc.) inside that column. But binding +targets in v1 are **column-level** scalars, lists, and relations — +not JSON-path. Adding JSON-path support is tracked via BACKLOG +`FORM-BINDING-JSON-PATH`. + +For v1 the recommendation: model `dietary_preferences` (and similar +`custom_fields` properties) as a `TAG_PICKER` form_field with a +`tag_categories` config. The TAG_PICKER → user_organisation_tags sync +(per ARCH-FORM-BUILDER §31.10) handles this without requiring +binding-target column mapping. + +### Drift prevention + +`BindingTypeRegistryConsistencyTest::test_every_registry_entity_maps_to_an_eloquent_model_with_the_attribute` +asserts that every registry entry corresponds to a real Eloquent model +class plus a real column on that model's table. Future drift (renamed +column without registry update, or registry entry without column) +becomes a test failure, not a runtime surprise. + ## 11. Related docs - `RFC-WS-6.md` — design decisions diff --git a/dev-docs/BACKLOG.md b/dev-docs/BACKLOG.md index 5f67b477..baa8568f 100644 --- a/dev-docs/BACKLOG.md +++ b/dev-docs/BACKLOG.md @@ -1142,6 +1142,53 @@ staging-API. Separate workstream from WS-6. **Trigger:** pre-release hardening phase. **Refs:** RFC-WS-6.md §4 V4. +#### ARTIST-ADV-BINDING-MODEL + +WS-6 v1 omits artist binding-target registry entries entirely. The +artist_advance purpose accepts schemas without bindings (Artist subject +resolved via portal_token, not via field-to-attribute mapping). For v2, +decide: + +- Should there be an `Artist` Eloquent model class? Currently the + `artists` table exists but no class — only the morph map alias points + at `App\Models\Artist` (a string). +- Which artist attributes are bindable from form data? The advance form + is OUTPUT-shaped: it gathers info FROM the artist (rider, hospitality, + technical needs); it does not provision Artist attributes the way + event_registration provisions Person attributes. +- Is the binding model even the right abstraction for advance forms, + or do they use a different sync mechanism (e.g. typed AdvanceSection + fields)? + +**Trigger:** when v2 design discussions for artist_advance feature +work begin. May result in registry entries, an Artist model, OR a +domain-specific alternative to bindings. +**Refs:** RFC-WS-6.md §3 Q9 v1.2 addendum, ARCH-BINDINGS.md appendix. + +#### FORM-BINDING-JSON-PATH + +WS-6 v1's binding-target registry handles only top-level model columns. +JSON-path attributes (e.g. `persons.custom_fields.dietary_preferences`) +are not bindable in v1. Adding support requires: + +- Registry shape extension (path-string syntax: + `'custom_fields.dietary_preferences'`) +- Applicator's `setAttribute` change → typed json_set / array_set helper +- Conflict resolution: do JSON-path siblings resolve independently? +- Type-validation: dietary_preferences is a list, but custom_fields + itself is JSON — what does identity_key_eligible mean here? + +For v1 the recommendation: model dietary_preferences (and similar +`custom_fields` properties) as a `TAG_PICKER` form_field with a +tag_categories config. The TAG_PICKER → user_organisation_tags sync +(per ARCH-FORM-BUILDER §31.10) handles this without requiring +binding-target column mapping. + +**Trigger:** when an organisation requests dietary_preferences (or +other `custom_fields` properties) as a form binding target. May +coincide with Crewli's v2 dietary management feature work. +**Refs:** RFC-WS-6.md §3 Q9 v1.2 addendum, ARCH-BINDINGS.md appendix. + --- _Laatste update: April 2026_ diff --git a/dev-docs/RFC-WS-6.md b/dev-docs/RFC-WS-6.md index 3697027e..f1239784 100644 --- a/dev-docs/RFC-WS-6.md +++ b/dev-docs/RFC-WS-6.md @@ -3,8 +3,8 @@ ## 1. Status - **State:** Authoritative for sessions 1, 2, 3 of WS-6 -- **Frozen:** 2026-04-25 (v1.0); refined post-session-2 cleanup as v1.1 (see §10) -- **Version:** v1.1 +- **Frozen:** 2026-04-25 (v1.0); refined post-session-2 cleanup as v1.1, then again as v1.2 (see §10) +- **Version:** v1.2 - **Owner:** Bert Hausmans - **Origin:** Architectural session 2026-04-25 (Claude Chat) — 13 design decisions, 4 refinements, 3 observations - **Related:** @@ -278,6 +278,37 @@ create-as-needed fallback chain when seeding event_registration schemas, so dev environments don't fail the publish guard out of the box. +#### Q9 v1.2 addendum — Registry alignment with model columns + +Sessie 3a surfaced that several entries in the `BindingTypeRegistry` +config did not match actual Eloquent model column names +(`person.phone_number` vs `phone`, `company.email` vs `contact_email`, +`company.phone_number` vs `contact_phone`) and that an `Artist` Eloquent +model class is absent. + +Sessie 3a.5 corrected this: + +- **Renames** (registry → matches model column): `person.phone_number` + → `person.phone`, `company.email` → `company.contact_email`, + `company.phone_number` → `company.contact_phone`. +- **New column**: `companies.kvk_number` (nullable, indexed) added so + the registry's B2B identity-key candidate is now legitimately + bindable. +- **Removed entries** (intentional v1 deferrals): `person.dietary_preferences` + (custom_fields JSON path; BACKLOG `FORM-BINDING-JSON-PATH`), + `artist.email` / `artist.stage_name` / `artist.tech_rider` / + `artist.hospitality_rider` (column absent + Artist model absent), + and the `artist` entity removed entirely from the registry (BACKLOG + `ARTIST-ADV-BINDING-MODEL`). +- **Drift-prevention test**: `BindingTypeRegistryConsistencyTest` + extended with a model-existence + column-existence assertion. Future + drift surfaces as a test failure, not a runtime surprise. + +`artist_advance` schemas may still exist in v1 with empty +`required_bindings`; the applicator runs and resolves to an empty +binding list, COMPLETED state. See ARCH-BINDINGS.md appendix for the +rationale. + ### Q10 — Section-level submit: stub now, activate later **Decision:** Build the listener-class structure now; gate the runtime via `config('form_builder.section_apply_enabled', false)`; activate when ARTIST_ADVANCE feature work lands. @@ -468,3 +499,8 @@ This document. Sessions 2 and 3 reference RFC sections by number rather than re- - **§3 Q9 addendum** — `form_schemas.default_crowd_type_id` (nullable ULID, no DB-level FK) replaces the silent `CrowdType::oldest()` heuristic. New `RequiresDefaultCrowdType` publish guard wired into `EventRegistrationGuards`. Runtime failsafe in `PersonProvisioner::resolveCrowdTypeId()`. - Snapshot dual-key cleanup (separate from RFC §3): legacy `binding` (singular) snapshot key dropped; `bindings` (plural list) is the single source of truth in `schema_snapshot.fields[*]`. ARCH-BINDINGS.md §6.4 / §6.1 already specified the plural list — code converged. - Route model binding (separate from RFC §3): controller-level workaround `$request->route('formSubmissionActionFailure')` replaced with explicit `Route::bind()` in `AppServiceProvider::boot()` plus `->withoutScopedBindings()` on org-scoped routes. Type-hinted parameters restored. RFC V3 (FK-chain tenant policy) unchanged. +- 2026-04-28 — v1.2 — Registry alignment with model columns (sessie 3a.5): + - 3 renames (registry → model): `person.phone_number` → `phone`, `company.email` → `contact_email`, `company.phone_number` → `contact_phone`. + - 5 removals (deferred to BACKLOG): `person.dietary_preferences` (FORM-BINDING-JSON-PATH); `artist.email`/`stage_name`/`tech_rider`/`hospitality_rider` plus the `artist` entity itself (ARTIST-ADV-BINDING-MODEL). + - 1 new model column: `companies.kvk_number` (nullable, indexed). + - `BindingTypeRegistryConsistencyTest` extended with a model-existence + column-existence assertion preventing future drift.