Two new BACKLOG entries surfaced during Session 3:
- **TECH-OBSERVER-TEST-CONVERGENCE** — track removal of the
artist_advance.bootstrap_on_org_create config flag once the five
FormSchema-counting tests are updated to expect the auto-bootstrapped
schema. Goal: productiegedrag = testgedrag, geen branching.
- **ART-ADVANCE-SECTION-FK** — replace the name-based bridge between
advance_sections (engagement-scoped) and form_schema_sections
(org-scoped) with a real FK. Today's name-match works for default-
seeded schemas but breaks on UI rename and offers no integrity
guarantee. Includes migration outline (form_schema_section_id
nullable FK, ArtistEngagement::created provisioning hook,
best-effort backfill).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
OrganisationObserver was gated on app()->runningUnitTests() — replaced
with config('artist_advance.bootstrap_on_org_create') (default true,
phpunit.xml overrides to false). Behaviour identical, but the seam is
explicit and removable. Tracked for full convergence by new BACKLOG
entry TECH-OBSERVER-TEST-CONVERGENCE — productiegedrag = testgedrag,
geen branching, na test-cleanup.
idempotency_key for the engagement-scoped draft simplified from
'aa-' + sha1(engagement_id)[0:27] to 'aa:' + engagement_id (29 chars,
fits varchar(30)). Same uniqueness guarantee, recognisable shape.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
§17.3 footnote already accurately describes ArtistResolver::fromPortalToken
(checked at commit cc48011). Wired event_id end-to-end on the cleaner
path: FormSubmissionService::createDraft now accepts event_id via the
\$context bag, and the EngagementPortalController passes it from
\$resolved->eventId. Replaces the prior post-save fallback. Per WS-4
denormalisation requirement.
ART-OBSERVER-ADVANCE-AGGREGATE moved from open to closed — landed in
Session 3 as the AdvanceSectionObserver (commit 1716e09).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Three backend endpoints under public throttle:30,1:
GET /p/artist/{token} — engagement summary + sections
GET /p/artist/{token}/sections/{section} — form schema + draft values
POST /p/artist/{token}/sections/{section} — section submit
Token resolution via ArtistResolver::fromPortalToken (Step 2). The
master Artist becomes the FormSubmission subject; engagement.event_id
populates form_submissions.event_id per WS-4 denormalisation. Token
mismatches map to 404 (InvalidPortalTokenException), soft-deleted
master artists to 410 Gone (ArtistDeletedException).
Section submit reuses the existing FormBindingApplicator pipeline
(RFC-WS-6 v1.3.1) by dispatching FormSubmissionSectionSubmitted —
no parallel apply path. Drafts are idempotent on
'artist_advance:{engagement_id}', so repeated POSTs find the same
submission. AdvanceSection (engagement-scoped) ↔ FormSchemaSection
bridge: case-sensitive name match against the org's artist_advance
schema; the default seeder names them in lockstep.
Frontend in Session 5 — backend complete here.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Seeds 5 default sections per RFC v0.2 D15 (General Info, Contacts,
Production, Technical Rider, Hospitality) on a per-organisation
artist_advance FormSchema with section_level_submit=true. Each
section ships with 3-4 illustrative form_fields; organisations
customise via the FormBuilder UI later.
Wired into org-creation via the new OrganisationObserver so new
tenants receive the schema automatically. Existing orgs get
coverage via the new artist:seed-advance-default artisan command
(idempotent — orgs that already own a schema are skipped).
Note: introduces a new production-grade default-seeder convention.
Prior FormBuilder defaults were dev-only via FormBuilderDevSeeder
called from DevSeeder::run(). This is the first non-dev path.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Resolves the artist subject + event_id + engagement for the
artist_advance portal flow. Per RFC v0.2 D15 + ARCH-FORM-BUILDER
§17.3 footnote: master Artist is the subject (preserves
form_submissions.subject_type='artist'), engagement provides
event_id (per WS-4 denormalisation), and engagement itself rides
along so callers can resolve advance_section context without a
second query.
Token comparison uses SHA-256 hex digest matching Session 1's
storage shape (commit eb6d396). Two domain exceptions distinguish
404 (no matching token → InvalidPortalTokenException) from 410
(master artist soft-deleted post-engagement → ArtistDeletedException
with engagementId attached).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Closes ART-OBSERVER-ADVANCE-AGGREGATE. Recomputes
artist_engagements.advancing_completed_count + advancing_total_count
on every section lifecycle event (created / updated-status-only /
deleted). Atomic via DB::transaction + lockForUpdate on both the
parent engagement and the sibling section rows; concurrent section-
status changes serialise correctly. Counter updates use
disableLogging() — counter sync is housekeeping, not audit. The
section's own updated event continues to log via LogsActivity on
AdvanceSection.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>