WS-6 v1.3-delta — Closure docs-PR #12
@@ -465,22 +465,122 @@ Person. De portal `IdentityMatchBanner` leest dit veld en toont de
|
||||
juiste copy. Contract ligt vast in
|
||||
`tests/Feature/FormBuilder/Listeners/TriggerPersonIdentityMatchOnFormSubmitTest`.
|
||||
|
||||
**Resterend werk (de eigenlijke FORM-05):** public form submissions
|
||||
(subject_type=null) krijgen momenteel *altijd* 'pending' omdat er nog
|
||||
geen Person bestaat om tegen te matchen. Breid uit met:
|
||||
**Resterend werk (de eigenlijke FORM-05):** post-D2 (PR #11 `23a5696`)
|
||||
schrijft de queued `TriggerPersonIdentityMatchOnFormSubmit` voor
|
||||
self-registered public submissions (`registration_source='self'`)
|
||||
standaard `'none'` zodra de exact-email-match in
|
||||
`PersonIdentityService::detectMatches` leeg terugkomt — het bestaande
|
||||
fuzzy-name fallback-pad wordt expliciet overgeslagen voor self-registered
|
||||
Persons (zie `PersonIdentityService::detectMatches` regel 117–119, *"skip
|
||||
fuzzy matching for self-registered persons — they provided their own
|
||||
email, so that's the canonical identity"*). Public submitters die een
|
||||
typo in hun email maken of een ander email-account gebruiken dan eerder
|
||||
bij de organisatie geregistreerd, krijgen daardoor geen "we may have
|
||||
found you" signaal en moeten organiser-side handmatig gereconcilieerd
|
||||
worden.
|
||||
|
||||
- Nieuwe methode op PersonIdentityService:
|
||||
`detectMatchesByValues(array $values, string $organisationId): MatchResult`
|
||||
- Een extra tak in `TriggerPersonIdentityMatchOnFormSubmit::resolveStatus`
|
||||
die voor public submissions de values uit `FormSubmission->values`
|
||||
extraheert (email / first_name / last_name via de schema binding),
|
||||
deze methode aanroept, en 'matched' / 'pending' / 'none' schrijft.
|
||||
Breid uit met:
|
||||
|
||||
- Nieuwe methode op `PersonIdentityService`:
|
||||
|
||||
```php
|
||||
public function detectMatchesByValues(
|
||||
array $values,
|
||||
string $organisationId,
|
||||
): Collection {
|
||||
// Multi-signal match: email + first_name + last_name + DOB +
|
||||
// optionele custom-field whitelist, gewogen naar
|
||||
// betrouwbaarheid van de bron-velden. Werkt los van
|
||||
// $person->registration_source, dus self-registered submissions
|
||||
// krijgen óók fuzzy-name + DOB candidates terug.
|
||||
}
|
||||
```
|
||||
|
||||
- Een extra match-arm in de status-derivation van
|
||||
`TriggerPersonIdentityMatchOnFormSubmit::handle()`. D2 inlinete de
|
||||
status-derivation in `handle()` zelf — er is geen aparte
|
||||
`resolveStatus` methode meer. De huidige logica:
|
||||
|
||||
```php
|
||||
$matches = $this->identityService->detectMatches($person);
|
||||
$status = match (true) {
|
||||
$person->user_id !== null => 'matched',
|
||||
$matches->isNotEmpty() => 'pending',
|
||||
default => 'none',
|
||||
};
|
||||
```
|
||||
|
||||
wordt uitgebreid met een value-based fallback wanneer de Person geen
|
||||
`user_id` heeft én de exact-email matcher leeg terugkomt:
|
||||
|
||||
```php
|
||||
$matches = $this->identityService->detectMatches($person);
|
||||
$valueMatches = $matches->isEmpty() && $person->user_id === null
|
||||
? $this->identityService->detectMatchesByValues(
|
||||
$this->extractFormValuesForMatching($submission),
|
||||
$submission->organisation_id,
|
||||
)
|
||||
: collect();
|
||||
$status = match (true) {
|
||||
$person->user_id !== null => 'matched',
|
||||
$matches->isNotEmpty() || $valueMatches->isNotEmpty() => 'pending',
|
||||
default => 'none',
|
||||
};
|
||||
```
|
||||
|
||||
De `extractFormValuesForMatching` helper trekt de relevante velden
|
||||
(email / first_name / last_name / date_of_birth + eventuele
|
||||
whitelisted custom-fields) uit `FormSubmission->values` via de
|
||||
schema-binding metadata.
|
||||
|
||||
Zo krijgt de portal-UX een betekenisvol signaal in plaats van een
|
||||
constante 'pending'.
|
||||
`'none'` voor elke public submitter die niet exact-email matched.
|
||||
|
||||
Prioriteit: Medium. Kan gebundeld worden met de organizer
|
||||
`person_identity_matches` UI (ook nog een frontend gap).
|
||||
**Prioriteit:** Medium. Bundel met de organizer
|
||||
`person_identity_matches` UI (frontend gap) én met
|
||||
`FRONTEND-ECHO-IDENTITY-MATCH-SUBSCRIPTION` (de portal banner update zo
|
||||
dat hij de eindstatus real-time ontvangt).
|
||||
|
||||
---
|
||||
|
||||
### FRONTEND-ECHO-IDENTITY-MATCH-SUBSCRIPTION
|
||||
|
||||
**Aanleiding:** WS-6 v1.3-delta D2 (PR #11 `23a5696`, 2026-05-08) introduceerde
|
||||
de `FormSubmissionIdentityMatchResolved` broadcast event class en bijbehorende
|
||||
`submission.{id}` private channel. De backend dispatcht het event maar de
|
||||
frontend portal IdentityMatchBanner subscribet nog niet — de banner refresht
|
||||
momenteel via TanStack Query refetch-on-window-focus, wat een interim-pad is
|
||||
maar niet de ontworpen UX.
|
||||
|
||||
**Wat:**
|
||||
|
||||
- Pinia store-extension OF inline `Echo.private('submission.${submissionId}')`
|
||||
subscription in de IdentityMatchBanner component
|
||||
- Op event-receipt: invalidate de TanStack Query cache key voor de submission
|
||||
resource zodat de banner copy automatisch update naar de finale staat
|
||||
('matched' / 'pending' / 'none')
|
||||
- Authorization wordt al afgedwongen door `routes/channels.php` callback
|
||||
(submitter-only voor nu — zie ook TECH-CHANNEL-AUTH-ORG-ADMIN)
|
||||
- E2E test: simuleer een form submission, verify dat de banner copy in real-time
|
||||
van "we're checking matches…" naar de finale status flipt zonder reload
|
||||
|
||||
**Vereisten:**
|
||||
|
||||
- Laravel Echo client setup in `apps/app/` (verifieren: bestaat al voor andere
|
||||
channels of moet nog worden toegevoegd?)
|
||||
- Soketi server in development docker-compose en in productie deployment
|
||||
(verifieren: WS-7 noemt Soketi in stack maar is broadcast wiring al getest?)
|
||||
|
||||
**Trigger-conditie:** Naar voren halen wanneer (a) de UX van TanStack
|
||||
refetch-on-focus klagende klanten oplevert, OF (b) de eerstvolgende form-builder
|
||||
frontend sprint (RFC-FORM-BUILDER-UI / S3b) gepland wordt — neem het mee in
|
||||
diezelfde sprint zodat alle Echo-wiring tegelijk landt.
|
||||
|
||||
**Estimate:** 0.5–1 dag (afhankelijk van of Echo client setup nog moet).
|
||||
|
||||
**Refs:** `dev-docs/RFC-WS-6.md` v1.3.1 §Q1 v1.3 add 2, `dev-docs/ARCH-BINDINGS.md`
|
||||
v1.2 §5.3, `apps/app/src/...` (portal IdentityMatchBanner location to be
|
||||
identified during implementation), `routes/channels.php`.
|
||||
|
||||
---
|
||||
|
||||
@@ -1027,6 +1127,50 @@ ARCH-discussie en RFC.
|
||||
|
||||
---
|
||||
|
||||
### HARD-DEADLINE-QUERY-TIMEOUT
|
||||
|
||||
**Aanleiding:** WS-6 v1.3-delta D2 (PR #11 `23a5696`, 2026-05-08) introduceerde
|
||||
`FormBindingApplicator::withDeadline(int $seconds)` — een **soft** deadline via
|
||||
post-call microtime check. Deze beschermt tegen "applicator runs slow over many
|
||||
bindings" (de long tail) maar **kan niet onderbreken** tijdens een hangende
|
||||
MySQL-query of een lock-for-update wait die langer duurt dan de deadline.
|
||||
|
||||
Voor enterprise SaaS op piekmomenten (vrijwilligersregistratie-windows,
|
||||
festival check-in flows) is een hangende query een reëel risico — PHP-FPM
|
||||
worker blokkeert, queue depth loopt op, downstream impact.
|
||||
|
||||
**Wat:**
|
||||
|
||||
Drie potentiële uitvoeringspaden, te kiezen tijdens RFC-fase:
|
||||
|
||||
1. **MySQL connection-level timeouts**: zet `read_timeout` en `write_timeout`
|
||||
op de Eloquent connection config voor de inner-transaction context.
|
||||
Eenvoudig, maar globale impact (alle queries via die connection krijgen de
|
||||
timeout).
|
||||
|
||||
2. **Per-query `MAX_EXECUTION_TIME` hints**: MySQL 5.7+ ondersteunt
|
||||
`/*+ MAX_EXECUTION_TIME(N) */` query hints voor SELECT statements. Niet
|
||||
ondersteund door MariaDB. Gericht maar database-vendor-specifiek.
|
||||
|
||||
3. **`pcntl_alarm` + signal handler**: hard interrupt via SIGALRM in de queue
|
||||
worker context (CLI sapi). Werkt niet in HTTP context (PHP-FPM disablet
|
||||
pcntl in de meeste configuraties). Combineren met optie 1 voor HTTP context.
|
||||
|
||||
**Trigger-conditie:** Naar voren halen wanneer (a) eerste productie-incident
|
||||
waar `FormBindingApplicatorTimeoutException` getrigerd wordt en de soft
|
||||
deadline blijkt onvoldoende (post-mortem toont een hangende query), OF (b)
|
||||
SLO-vereisten van een enterprise klant strikte response-time-garanties
|
||||
opleggen die de soft deadline niet kan halen.
|
||||
|
||||
**Estimate:** 2-3 dagen (RFC + implementatie + tests + connection config voor
|
||||
alle environments).
|
||||
|
||||
**Refs:** `dev-docs/RFC-WS-6.md` v1.3.1 §Q1 v1.3 add 4, `dev-docs/ARCH-BINDINGS.md`
|
||||
v1.2 §5.3, `api/app/FormBuilder/Bindings/FormBindingApplicator.php` (soft
|
||||
deadline implementation).
|
||||
|
||||
---
|
||||
|
||||
### ~~TECH-02 — scopeForFestival helper op Event model~~ ✅ OPGELOST
|
||||
|
||||
---
|
||||
|
||||
Reference in New Issue
Block a user