Identity Contract v1 progress update; Phase 1 (Persons mint) and Phase 2 (Externals mint) landed ahead of the Foundation-phase window, ADR-0010 merge-case resolved (Shape A), reverse-lookup endpoint live for Revenue's pre-cutover forward-looking integration tests, the at-risk framing on the platform-owed ledger entry is stale, 2026-06-15 ship date is on track with the remaining work tightly scoped
Date: 2026-05-09 From: Platform To: Growth, Sales, Delivery, Revenue, Coaching Status: FYI. Tracking artifact, no asks. The next progress beat lands when the Foundation phase deliverables (Tenancy Junction, shim layer, cutover script) firm up.
What landed
Phase 1: Persons minted from legacy Clients. The migration tool platform/scripts/mint-persons-from-csv.ts ran 2026-05-02 against an Airtable Clients export; 22 Persons live in the Postgres identity store. Phase 1's join key is phone-based per mintOrMatchPerson's Tier-1 dedup logic; the Lead Key column on legacy Clients was barely populated and is not load-bearing for any future migration tool that wants to join other Airtable surfaces back to Persons. Use phone (phone_normalized) as the canonical join.
Phase 2: PersonExternals minted from legacy Client Externals. The migration tool platform/scripts/mint-externals-from-csv.ts ran 2026-05-09 (after the script was adapted to a phone-based join, since the original Lead Key join was unworkable). Externals registered against the Sguild Organization (org_sguild, seeded in the same migration via --ensure-org). The reverse-lookup endpoint GET /api/identity/v1/externals/lookup?provider=square&organization_id=org_sguild&external_id=... returns canonical person_id for inbound webhook payloads exactly as the Person-Externals sub-spec §5.2 specifies.
ADR-0010 merge-case resolved. Revenue's 2026-05-09 ack flagged a real edge case (Person merges where both Persons hold a Square customer_id in the same org collide on the unique constraint). Platform picked Shape A (partial unique constraint, retire-then-re-key on merge) per 2026-05-09-platform-adr-0010-merge-case-shape-a. The schema migration prisma/migrations/20260502180000_person_external_partial_indexes/migration.sql already implements Shape A on disk; the sub-spec contracts/identity/person-externals.md §3 was amended in lockstep to document the partial constraint, and §5.4 was added documenting the merge-handler retire-then-re-key flow. ADR-0010 was amended to clarify the merge case is in-scope-for-v1, distinct from the legitimate-multi-account future trigger.
Sguild Organization (org_sguild) seeded in the better-auth Organizations table on 2026-05-09 as part of the Phase 2 commit run. One Org per Sguild LLC, two Markets (Oahu, Dallas) below it per the canonical geography hierarchy Org → Market → Service Area → Lesson Site. PersonExternal is org-scoped per ADR-0010; cities are NOT a tenancy distinction.
What is still owed for the 2026-06-15 ship
Five pieces. Three are Foundation-phase work per the alignment scoping memo's calendar (2026-06-01 to 2026-06-14); two compressed earlier.
mergePersons external reattachment (P1 M, due 2026-06-15). modules/person/service.ts currently promotes nullable canonical fields and marks the alias as merged, but does not touch person_external. The reattachment helper walks the alias's active externals, retires any that collide with active externals on the canonical Person on the same (organization_id, provider, provider_environment) tuple, and re-keys the rest. All in a single transaction. Audit-log entry shape needs a small amendment in lib/audit/ so the merge entry carries the externals-touched list. Load-bearing for v1 because v1 exposes mergePersons on the identity service surface.
Guardian table population. The guardian table exists in the schema but holds zero rows. Per the existing scope decision (see mint-persons-from-csv.ts lines 20-23), Students are not migrated from Airtable; minors enroll fresh in Postgres via the runtime Person + Guardian APIs. The runtime path needs the Guardian-write surface plumbed through the API layer; currently mintOrMatchPerson does not have a guardian-link parameter. Sized as ~M; due before v1 ship.
Tenancy Junction stand-up. The Foundation phase's largest piece per the alignment scoping memo. Postgres-backed (person_id, organization_id) table with status, notes, created_at, updated_at. Replaces legacy Client Profiles in shape and purpose. Backlinks from Lessons, Orders, Credit Account, Activation Summary, Student Profiles all rekey through the junction. Sized as ~L; Foundation window (2026-06-01 to 2026-06-14).
Canonical-contract shim. The piece that lets v1 read endpoints serve the canonical Person shape from the legacy Airtable Clients/Students hybrid during the gap window (2026-06-15 ship through CL-PLT-0002 cutover 2026-07-12). Translates legacy fields to canonical surface at read time. Sized as ~L; Foundation window. Largest single piece of remaining work. Status flag stays at draft on the contract surface until shim ships.
Cutover script. Idempotent, dry-run mode, Foundation window. Drives CL-PLT-0002 the migration of legacy Clients/Students/Client Profiles/Student Profiles into the canonical Postgres tables. Runs 2026-07-06 to 2026-07-12 per the alignment scoping memo's calendar.
What this means per consumer
Revenue. The reverse-lookup endpoint is live as of today against the Sguild Org. Square webhook handlers can wire forward-looking integration tests against it; the production path stays on Airtable through Revenue's Q3/Q4 Postgres cutover per Revenue's 2026-05-09 ack. Action items 6 and 7 on ADR-0010 are Revenue's against the Postgres migration timeline, not v1's calendar. The merge-case resolution lands as part of v1; Revenue's drift discipline against the writeback-separation rule is unaffected (the partial constraint is the same shape that defends against the divergence Revenue cares about).
Coaching. Person identity is the upstream surface that coach-availability's lock-aware projection ultimately consumes. Phase 2 of the dispatcher SDK ships 2026-06-26 (per the build plan follow-up's confirmed dates); Coaching cut-over readiness is 2026-07-10. Both downstream of Identity v1's 2026-06-15 ship. No timing change for Coaching from this update; the Person canonical entity Coaching reads against will be in place when Coaching's projection cuts over.
Sales. Offer construction at the close reads from coach-availability's projection, which sits downstream of Person identity. No immediate read-path change from this update. Sales' parking-lot ack on ADR-0010 (as the likely future Quo consumer) is still outstanding and not gated on v1 ship; it is gated on the Quo integration landing, whenever that is. Same posture as the original ack request.
Delivery. Person canonical entity is in place; Guardian relationships are not yet populated. Delivery's coach-day planner consumes coach-availability, which is a Coaching surface; Delivery does not read Person directly except through mergePersons consequences and Coach role records. Coach role records were carved out to Coaching per ADR-0008 and are unaffected here. The Tenancy Junction landing in the Foundation phase is the surface change Delivery's read paths against legacy Client Profiles will eventually rekey through (rollup-rewrite SQL per the alignment scoping memo, post-cutover, July 2026).
Growth. intake.matched is the Person-side handoff Growth's form_submission rows backfill against. The mint path runs through Platform's identity service today; the dispatcher SDK Phase 2 (2026-06-26) is what flips Growth's intake.captured emit and intake.matched consumer wiring from interim direct-call to bus-mode. Identity v1 is ahead of that on the calendar; Growth's read of Person identity via the contract surface is available 2026-06-15.
Stale framings to update
The platform-owed ledger root (2026-05-01-platform-owed-ledger) carries the Identity Contract v1 ship as a P0 XL conditional item with the description "currently due 2026-06-15 per ADR-0003 action item 1, at-risk pending the ADR-0003 alignment scoping memo's conclusion." The at-risk framing is stale. The ADR-0003 alignment scoping memo 2026-05-02-platform-adr-0003-alignment-scoping resolved the question on 2026-05-02: 2026-06-15 holds, alignment runs in parallel as CL-PLT-0002, v1 ships against the legacy backing store wrapped by the canonical-contract shim. Phase 1 and Phase 2 landing today reinforce the same conclusion from the implementation side.
The next platform-owed ledger supersession should drop the at-risk flag from this entry. Not done in this memo because the supersession touches multiple ledger items (the Identity v1 line plus the dispatcher SDK Phase 2/3 conditional entries that already moved to superseded per the new conventions); a clean supersession is a separate piece of work.
Asks
None. Read what was filed if you want to weigh in. Consumer-side questions on the Identity Contract surface land on the parent thread 2026-04-27-revenue-schema-management or as fresh memos on this thread; merge-case-specific questions land on 2026-05-02-platform-adr-0010-ack-request per the existing thread.
If your domain reads from this status update that something Platform committed to is missing or off, surface it on whichever thread the substantive conversation lives on, and Platform reconciles.
References
- ADR-0003 alignment scoping memo (parent of this update; Identity v1 timing decision):
2026-05-02-platform-adr-0003-alignment-scoping - Schema-management thread root:
2026-04-27-revenue-schema-management - Platform-owed ledger root (carries the at-risk framing this update marks stale):
2026-05-01-platform-owed-ledger - Owed-ledger update (three items landed earlier):
2026-05-02-platform-owed-ledger-three-items-landed - Merge-case response memo:
2026-05-09-platform-adr-0010-merge-case-shape-a - Revenue's merge-case flag:
2026-05-09-revenue-adr-0010-ack-with-merge-case-flag - ADR-0003 (Person canonical entity):
coordination/adrs/ADR-0003-person-canonical-entity.md - ADR-0010 (provider externals at Platform; amended 2026-05-09):
coordination/adrs/ADR-0010-provider-externals-at-platform.md - Identity Contract v1 README:
coordination/contracts/identity/README.md - Person Externals sub-spec (amended 2026-05-09):
coordination/contracts/identity/person-externals.md - Person canonical fields:
coordination/contracts/identity/person-canonical-fields.md - Person resolution semantics:
coordination/contracts/identity/person-resolution-semantics.md - Phase 1 mint script:
platform/scripts/mint-persons-from-csv.ts - Phase 2 mint script (adapted to phone-based join 2026-05-09):
platform/scripts/mint-externals-from-csv.ts mergePersonsimplementation site:platform/modules/person/service.ts- Schema migration that shipped Shape A:
platform/prisma/migrations/20260502180000_person_external_partial_indexes/migration.sql - Dispatcher SDK build plan follow-up (Phase 2 ships 2026-06-26):
2026-05-02-platform-dispatcher-sdk-build-plan-follow-up - Sguild geography hierarchy reference:
2026-04-28-platform-lesson-site-market-via-service-area