Commit Graph

5 Commits

Author SHA1 Message Date
0e986f42cb refactor(form-builder): align binding registry with model column reality (WS-6)
Three renames (registry → matches actual Eloquent model column):
  - person.phone_number       → person.phone
  - company.email             → company.contact_email
  - company.phone_number      → company.contact_phone

Six removals (registry attribute does not exist as model column,
intentionally deferred):
  - person.dietary_preferences  (custom_fields JSON path; BACKLOG
    FORM-BINDING-JSON-PATH)
  - artist.email                (Artist model absent + column absent)
  - artist.stage_name           (column absent)
  - artist.tech_rider           (column absent)
  - artist.hospitality_rider    (column absent)
  - artist entity removed entirely (no v1 bindable attributes)

Decisions documented inline in binding_targets.php and tracked
via BACKLOG entries (Task 4 of this session).

Tests touched:
- BindingTypeRegistryTest:
    test_resolve_person_dietary_preferences_returns_collection_array →
      renamed test_resolve_collection_attribute_returns_collection_array,
      uses Config::set to inject a synthetic 'test_entity.tags' collection
      target. v1 has no production collection targets (BACKLOG
      FORM-BINDING-JSON-PATH).
    test_validate_append_strategy_accepts_collection_target — same pattern.
    test_entities_returns_known_entities — drop 'artist' from expected list.
    test_attributes_for_person_includes_email_and_dietary_preferences →
      renamed _includes_email_and_phone (the renamed attribute).
- AppendStrategyRequiresCollectionTargetTest:
    test_passes_with_collection_target — same Config::set synthetic-
    target pattern.
- MaxOneIdentityKeyPerTargetEntityTest:
    test_passes_with_one_identity_key_each_on_different_entities —
    'company.email' → 'company.contact_email' to match registry rename.

Refs: WS-6 sessie 3a binding-target drift audit

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-29 00:14:13 +02:00
16a9265430 feat(form-builder): add PurposeSubjectResolver per purpose (WS-6)
Parallel interface to PurposeGuardProvider for runtime subject
resolution. Seven concrete resolvers, one per v1.0 purpose. Wired
through purposes.php via subject_resolver_class key.

EventRegistration uses PersonProvisioner (may create). Other purposes
resolve from existing context (portal token, production request, auth).
IncidentReport is the only purpose allowed to return null (anonymous-
allowed configurations); the others return concrete model types
(narrowed via PHP covariance) for caller convenience.

Refs: RFC-WS-6.md §3 (Q9)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-26 12:57:21 +02:00
e3c9211e3f feat(form-builder): wire PurposeGuardProvider per purpose (WS-6)
Adds PurposeGuardProvider as a parallel interface to PurposeDefinition
(value object stays untouched). Seven concrete providers, one per v1.0
purpose, each declaring its publish-guard list. Registry resolves and
caches providers via guards_class config key.

Universal guards (MaxOneIdentityKeyPerTargetEntity,
AppendStrategyRequiresCollectionTarget, NoAmbiguousTrustLevels,
IdentityKeyBindingsOnlyInFirstSection) wire into every purpose. The
section guard is a cheap no-op when section_level_submit=false.

ArtistAdvanceGuards omits RequiresIdentityKeyBinding because the
artist subject is resolved via portal token, not form data. Same
reasoning for supplier_intake (production_request) and the auth-based
purposes.

Includes a cross-cutting BindingTypeRegistryConsistencyTest that
verifies tasks 5/7/8 do not contradict each other (registry ↔ guards ↔
purpose required_bindings).

Refs: RFC-WS-6.md §3 (Q9, Q13)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-25 23:01:19 +02:00
0dd991c688 feat(form-builder): add BindingTypeRegistry as single source of truth for target shapes (WS-6)
Config-driven mapping from (target_entity, target_attribute) to storage
shape (scalar/collection/relation), PHP type, and identity-key
eligibility. Replaces any name-suffix matching (e.g. _tags, _skills) —
those are convention-not-contract and reject by design.

Used by publish guards now and (in session 2) by FormBindingApplicator.

Refs: RFC-WS-6.md §4 (V1)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-25 22:41:25 +02:00
e93207765b feat(form-builder): introduce purpose registry and definition value object
Lands the v1.0 purpose registry (WS-2 of the consolidation sprint) as a
first-class concept: a `PurposeDefinition` value object, a
`PurposeRegistry` service keyed by slug, and a declarative
`config/form_builder/purposes.php` registry with exactly the seven
purposes from ARCH-CONSOLIDATION §6.4.

Also rebuilds the morph-map in `AppServiceProvider::boot` into three
labelled blocks: (1) domain subject types derived from
`PurposeRegistry::allSubjectTypes()`, (2) non-purpose domain types
hardcoded with comments (form_schemas owner_types, activity-log
subjects), (3) framework types (spatie/activitylog; Sanctum stays
absent per addendum Q4).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-24 14:35:18 +02:00