docs(backlog): close no-compromises gaps from WS-6 v1.3-delta review
Three edits closing concessies surfaced in chat review of the closure
docs-PR:
1. FORM-05 'Resterend werk' sub-paragraph: surgical replacement of
resolveStatus references (method removed in D2, PR #11 23a5696).
Updated to describe post-D2 reality: gate + invariant +
handle()-internal status derivation. Ticket stays open (the
detectMatchesByValues extension is unbuilt).
2. FRONTEND-ECHO-IDENTITY-MATCH-SUBSCRIPTION (NEW): tracks the frontend
follow-up where the portal IdentityMatchBanner subscribes to the
submission.{id} channel for live banner updates. Previously
documented in PR #11 body and RFC §Q1 v1.3 add 2 commentary but
without an actionable BACKLOG ticket.
3. HARD-DEADLINE-QUERY-TIMEOUT (NEW): tracks the upgrade from soft
post-call microtime deadline to a hard deadline that can interrupt
hanging MySQL queries (connection-level timeouts, MAX_EXECUTION_TIME
hints, or pcntl_alarm). Previously documented as 'soft deadline
limitation' inline in code comments without an actionable BACKLOG
ticket.
No spec changes; no code changes. Closes the chat-identified gaps so
WS-6 v1.3-delta closure has zero un-anchored mental TODOs.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -465,22 +465,122 @@ Person. De portal `IdentityMatchBanner` leest dit veld en toont de
|
|||||||
juiste copy. Contract ligt vast in
|
juiste copy. Contract ligt vast in
|
||||||
`tests/Feature/FormBuilder/Listeners/TriggerPersonIdentityMatchOnFormSubmitTest`.
|
`tests/Feature/FormBuilder/Listeners/TriggerPersonIdentityMatchOnFormSubmitTest`.
|
||||||
|
|
||||||
**Resterend werk (de eigenlijke FORM-05):** public form submissions
|
**Resterend werk (de eigenlijke FORM-05):** post-D2 (PR #11 `23a5696`)
|
||||||
(subject_type=null) krijgen momenteel *altijd* 'pending' omdat er nog
|
schrijft de queued `TriggerPersonIdentityMatchOnFormSubmit` voor
|
||||||
geen Person bestaat om tegen te matchen. Breid uit met:
|
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:
|
Breid uit met:
|
||||||
`detectMatchesByValues(array $values, string $organisationId): MatchResult`
|
|
||||||
- Een extra tak in `TriggerPersonIdentityMatchOnFormSubmit::resolveStatus`
|
- Nieuwe methode op `PersonIdentityService`:
|
||||||
die voor public submissions de values uit `FormSubmission->values`
|
|
||||||
extraheert (email / first_name / last_name via de schema binding),
|
```php
|
||||||
deze methode aanroept, en 'matched' / 'pending' / 'none' schrijft.
|
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
|
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
|
**Prioriteit:** Medium. Bundel met de organizer
|
||||||
`person_identity_matches` UI (ook nog een frontend gap).
|
`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
|
### ~~TECH-02 — scopeForFestival helper op Event model~~ ✅ OPGELOST
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|||||||
Reference in New Issue
Block a user