feat(form-builder): FORM-02 TAG_PICKER sync listener (ARCH §31.10)
Rebuilds the tag-sync flow purged in S2a, now listener-driven against the universal FormBuilder (ARCH §31.10). - SyncTagPickerSelectionsOnSubmit listener: ShouldQueue on connection=redis queue=default. Filters to event_registration + person subjects with at least one TAG_PICKER form_value. Logs on failure, never rethrows so sibling listeners keep running. - AppServiceProvider registers the listener via Event::listen alongside the existing S1 observers. - PersonIdentityService::confirmMatch now calls FormTagSyncService::rebuildForPerson after setting person.user_id — the deferred-sync path for persons who filled in TAG_PICKER fields before their account was linked. - ARCH-FORM-BUILDER.md §31.10 rewritten with the authoritative contract block from this session. Header bumped to v1.2.1. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -4,11 +4,11 @@
|
||||
> Any discrepancy with SCHEMA.md is resolved in favour of this document
|
||||
> during the refactor. SCHEMA.md is updated at the end of the refactor.
|
||||
>
|
||||
> **Status:** Approved — about to enter S1 implementation
|
||||
> **Version:** 1.2 (expanded with per-purpose lifecycles, integration
|
||||
> **Status:** Approved — S2b in progress (service + API layer)
|
||||
> **Version:** 1.2.1 (§31.10 FORM-02 contract rewritten authoritatively)
|
||||
> **Previous version:** 1.2 April 2026 — per-purpose lifecycles, integration
|
||||
> contracts, user guidance principles, documentation coverage requirements,
|
||||
> in-app copy catalogue, and concrete gap fills from v1.1 review)
|
||||
> **Previous version:** 1.1 committed April 2026
|
||||
> in-app copy catalogue, and concrete gap fills from v1.1 review
|
||||
> **Created:** April 2026
|
||||
> **Owner:** Architecture doc; every session reads this before starting
|
||||
>
|
||||
@@ -2958,26 +2958,45 @@ ARCH section update) before merge.
|
||||
### 31.10 Tag sync integration (BACKLOG FORM-02)
|
||||
|
||||
Replaces the S1-era `TagSyncService` that read the legacy
|
||||
`person_field_values` table. Purged in S2a; rebuilt in S2b/S2c against
|
||||
the new FormBuilder.
|
||||
`person_field_values` table. Purged in S2a; rebuilt in S2b against the
|
||||
new FormBuilder.
|
||||
|
||||
**Trigger:** `FormSubmissionSubmitted` event (ARCH §17.1) OR explicit
|
||||
call from `PersonController::approve()` after status transitions to
|
||||
`approved`.
|
||||
**Contract (authoritative):**
|
||||
|
||||
**Listener:** `SyncTagPickerValuesToUserTagsListener` — for the given
|
||||
submission, finds all `form_values` whose field has
|
||||
`field_type=TAG_PICKER`, and upserts rows into `user_organisation_tags`
|
||||
with `source=self_reported`, respecting `person.user_id` (skip if null).
|
||||
Only syncs to the subject person's user account.
|
||||
```
|
||||
Trigger: FormSubmissionSubmitted event where
|
||||
form_schema.purpose = 'event_registration' AND
|
||||
submission.subject_type = 'person' AND
|
||||
submission contains at least one TAG_PICKER form_value.
|
||||
|
||||
**Failure mode:** log at warning level; never throws into the submission
|
||||
lifecycle. Reason: a tag-sync failure must not block registration.
|
||||
Listener: SyncTagPickerSelectionsOnSubmit
|
||||
|
||||
Behaviour:
|
||||
1. Resolve the Person from submission.subject_id.
|
||||
2. If person.user_id is null → no-op (log at info, tag sync will
|
||||
trigger on future identity link).
|
||||
3. Call FormTagSyncService::rebuildForPerson($person).
|
||||
4. Never mutates organiser_assigned tags. Only rebuilds
|
||||
source = self_reported to match the union of TAG_PICKER values
|
||||
across that person's submitted event_registration submissions.
|
||||
|
||||
Re-trigger on identity link: PersonIdentityService::confirmMatch must,
|
||||
after setting person.user_id, call FormTagSyncService::rebuildForPerson
|
||||
($person). This is the deferred sync path for persons who filled in
|
||||
TAG_PICKER fields before their user account was linked.
|
||||
|
||||
Failure mode: listener logs at error level and does NOT fail the event
|
||||
propagation (other listeners — e.g. §31.3 shift provisioning, §31.1
|
||||
identity matching, §31.8 crowd list auto-add — must still run).
|
||||
|
||||
Idempotent: safe to run multiple times for the same person.
|
||||
```
|
||||
|
||||
**Call site removed in S2a:** `PersonController::approve()` and
|
||||
`PersonIdentityService::syncRegistrationTags()` used to call
|
||||
`TagSyncService::syncFromRegistration($person)` directly. The rebuilt
|
||||
flow is listener-driven — no direct service injection required.
|
||||
flow is listener-driven plus the targeted call inside
|
||||
`PersonIdentityService::confirmMatch` — no ad-hoc cross-module coupling.
|
||||
|
||||
---
|
||||
|
||||
|
||||
Reference in New Issue
Block a user