WS-5a commit 1 of 4 per ARCH-CONSOLIDATION-ADDENDUM-2026-04-24 Q3.
Creates the relational home for what was form_fields.binding JSON and
form_field_library.default_binding JSON. Owner discriminator is polymorphic
morph (owner_type/owner_id) — the pattern the rest of WS-5 (5b validation_rules,
5d options) will reuse.
Migration backfills rows from both JSON sources in a single transaction and
is genuinely reversible (rollback reconstructs the JSON). Old columns remain
in place until commit 3 has switched all readers.
Pattern B (binding=null) is represented by absence of row. mode enum covers
entity_owned / mirrored only.
Cascade on owner delete via observer — bindings are physical state, not
historical audit. FormFieldBindingScope enforces multi-tenancy via UNION over
both owner chains (form_field → schema → org OR form_field_library → org) —
Q2's declarative tenantScopeStrategy() can't walk morph parents.
Tests: migration forward/back, morph relation, cascade observer, scope
isolation, enum coverage.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Reduces the FormPurpose vocabulary from 22 variants + a `custom` escape
to the seven v1.0 purposes registered in the new PurposeRegistry.
- Purge migration deletes any form_schemas row whose `purpose` is not
in the v1.0 set (cascades through form_fields, form_submissions,
form_values, form_value_options, form_schema_sections,
form_submission_section_statuses, form_submission_delegations,
form_schema_webhooks, form_webhook_deliveries via existing FK).
- Drop migration removes the `custom_purpose_slug` column + its index.
- Both migrations declare their `down()` as a hard failure — we do not
support reversing a purge (pre-launch, no production data).
- `FormPurpose` enum slims to the seven cases; the legacy helpers
(defaultSubmissionMode / defaultSubjectType / allowsPublicAccess)
now delegate to PurposeRegistry so callers keep working.
- FormSchema fillable / FormSchemaResource / StoreFormSchemaRequest /
UpdateFormSchemaRequest / FormSchemaFactory drop every reference to
`custom_purpose_slug` and the `custom` purpose.
- VerifyFormsDataIntegrity drops the custom-slug mismatch check and
sources the subject-type allow-list from PurposeRegistry.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
9 backed string enums covering purpose, field type, submission status/mode/review,
field width, value storage hint, snapshot mode, webhook delivery status.
FormPurpose/FormFieldType include helper methods per ARCH §3/§5. All with
declare(strict_types=1) and values() helpers for validation rules.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>