Platform accepts the ADR-0051 architecture and proposes the bounded coach Person-resolution interface
Completion on 2026-10-02: production readiness and actual Coaching workload proof records the ratified contracts, Ready deployment, authorization checks and zero-identity-write evidence. The historical proposal below is retained as written.
Platform accepts the ADR-0051 architecture and ownership of the authenticated, tenant-scoped coach Person-resolution capability. This memo proposes the concrete bounded v1 interface for Coaching and Sales review. It does not approve the unfinished interface, clear coach-person-resolution-interface-ready, deploy code, or activate service credentials.
The coordinator reviewed the Platform owner drafts and Coaching's 2026-10-02-coaching-resolution-draft-consumer-review. The detailed proposed sources are Coach Person Resolution API and service-auth amendment proposal. These remain Proposed; published Identity v1.4.0 is unchanged, and ADR-0050's Service Auth v1.6.0 remains the current parent. If summary wording and the detailed draft differ, the detailed draft controls this review. ADR acceptance is recorded separately in 2026-10-02-platform-adr0051-accepted-repair-preflight.
Coordinator review fixes exact field limits, searches name-only candidates across all statuses, binds the intended Coaching production proof and single Organization explicitly, and requires non-expiring v1 operation keys rather than an arbitrary seven-year retry boundary. The authoritative-state invalidation rule qualifies every success replay. Coaching is asked to approve this exact published draft or state remaining blockers before contract promotion and runtime integration.
Contract placement
After consumer approval, Platform will publish the operation as an additive Identity Contract v1.5.0 surface at contracts/identity/coach-person-resolution-api.md and add the corresponding Coaching-to-Platform workload policy as an additive Service Auth Contract v1.7.0 change. Existing consumers remain compatible. No existing route or policy is deprecated. The draft was initially numbered v1.6.0 before ADR-0050's concurrent publication; the v1.7.0 renumbering changes no proposed behavior.
HTTP operation
POST /api/identity/v1/person-resolutions
Required headers are Authorization: Bearer <Platform service JWT>, Content-Type: application/json, X-Tenant-Id: tnt_sguild, X-Organization-Id: <canonical organization id>, and Idempotency-Key: <consumer-stable operation key>. The operation key must be stable for the Coaching creation attempt. UUIDv7 is recommended. Platform accepts 16 to 128 URL-safe ASCII characters. A retry after a timeout or unknown response must reuse the same key and request.
{
"purpose": "coach_creation",
"phone": "+12145550100",
"email": "optional@example.com",
"given_name": "Emerson",
"family_name": "Example",
"display_name": "optional"
}
purpose and phone are required. At least one explicit given_name or family_name is required; the other may be null. Email and display name are optional. Coaching will add explicit given-name and family-name inputs to both creation paths and must not automatically split its existing single Name value. Until an operator supplies the explicit field or fields, Coaching must block submission. For a verified single-part name, Coaching sends that part in the operator-selected explicit field and null for the unknown component. It must not invent a surname, duplicate the token into both fields, or use a placeholder. display_name may remain null because Sales can render from given and family names.
V1 accepts NANP numbers only: a ten-digit national number or +1 plus ten digits whose area code begins with 2 through 9 and is not an N11 service code. Exchange-code strictness is not yet enforced because the current Platform fixture corpus uses 555 test exchanges. Other country codes and non-NANP E.164 numbers fail validation rather than being treated as new identities. Platform completes authentication, routing, JSON-shape, field-length, character, phone-region, email, and name validation before opening the mutation transaction. Validation failure writes no Person, operation journal, contact row, tenancy, or event.
Platform normalizes identity material under the Identity Contract. Contact values and raw request names are input-only and are never echoed. A success response may contain the current canonical Person's ten contract fields, including its canonical name fields.
HTTP 200 returns ok: true and data containing the operation key, outcome: matched | minted, replayed, and the canonical ten-field Person projection. The Person projection contains no phone or email.
Authentication and tenant binding
Coaching exchanges its exact Vercel production workload identity for policy coaching-production-to-platform-person-resolution-v1. The requested capability and issued scope are exactly platform.identity.person-resolutions.create. The token is bound to sub: system:coaching, aud: platform, the canonical tenant, and the canonical organization, with a five-minute lifetime. The caller does not choose token claims.
The mutation route accepts no operator-session fallback. Platform requires an exact match among token tenant and organization claims, both routing headers, and an active canonical organization. Missing or mismatched binding fails closed. Release proof must execute the exchange using the exact configured Coaching production Vercel workload subject, verify the issued sub, aud, tenant, organization, single scope, lifetime, and key id, prove wrong-subject and claim-mismatch rejection, and use the issued token against the endpoint. A configuration assertion alone is insufficient.
Resolution safeguards
- A single active phone match may be returned only when the request and canonical Person pass the coach-specific exact-name predicate. When both canonical structured names are populated, normalized given and family names must both match their request counterparts, so a request with one unknown component does not auto-match that Person. When exactly one canonical structured name is populated, that populated component must match its request counterpart and the other canonical component must remain null. When both canonical structured names are null and canonical
display_nameis populated, it must exactly match normalized requestdisplay_name, or the normalized join of the non-null request name components when request display name is absent. A canonical Person with all three name fields null is not auto-matched and producesPHONE_NAME_CONFLICTreview. - An incompatible name produces review, even when email also matches. The stricter ADR-0051 coach guard takes precedence over the general phone-plus-email threshold.
- Multiple phone matches, an email-only match, an archived Person, or an unresolved or merged identity produce review. The endpoint never reactivates or unmerges a Person.
- Before minting, Platform searches Persons across all statuses with the same exact-name predicate used for phone candidates. This includes the explicit structured-name and display-name fallback rules above. An active name-only candidate produces
NAME_ONLY_MATCHreview; archived and merged candidates produce the corresponding status-review reason. Absence of a phone or email match therefore does not permit minting over an existing exact-name candidate. - A Person is minted only when no phone, email, or exact-name review candidate exists and all required identity material is valid.
- Review responses include candidate Person identifiers only, never names, phone numbers, or email addresses.
HTTP 409 review responses use code: IDENTITY_REVIEW_REQUIRED, outcome: review_required, the operation key, replay flag, candidate Person identifiers, and one stable reason code: MULTIPLE_PHONE_MATCHES, PHONE_NAME_CONFLICT, EMAIL_ONLY_MATCH, NAME_ONLY_MATCH, ARCHIVED_IDENTITY, or UNRESOLVED_ALIAS.
Durable idempotency and concurrency
Platform will keep a durable operation journal with a unique key over caller subject, tenant, organization, and operation key. It stores a SHA-256 request hash, purpose, immutable original terminal outcome, Person identifier or review reason and candidate identifiers, original Person updated_at version, and timestamps. It does not store raw phone, email, or names. V1 has no automatic journal expiry or purge. Consumers must use globally unique keys and must not intentionally reuse them; any retirement policy requires consumer review.
Request equivalence is computed after validation from fixed-order UTF-8 JSON containing purpose, NANP-normalized phone as country-code digit 1 plus ten national digits with no punctuation, lowercase trimmed email or null, normalized given name, normalized family name, normalized display name or null, caller subject, tenant, and organization. Name normalization lowercases, collapses whitespace, strips diacritics, and removes common titles and suffixes per person-resolution-semantics.md. Formatting differences that normalize identically are the same request. Any other change is a different request.
The same key with the same normalized request replays a committed terminal review. A successful original result is revalidated against authoritative Person state before replay. Platform returns the current canonical ten-field Person only when the original Person still exists, is active, is not an alias, and still passes the coach-specific name predicate. If it is archived, merged, missing, or now materially name-conflicting, Platform preserves the immutable original journal provenance and returns HTTP 409 IDENTITY_RESULT_INVALIDATED with reason PERSON_ARCHIVED, PERSON_MERGED, PERSON_MISSING, or PERSON_NAME_CHANGED. It never replays a stale active Person or silently re-resolves under the same key.
The same key with a different normalized request fails with HTTP 409 IDEMPOTENCY_KEY_REUSED. Invalid authentication and invalid input are not journaled. A timeout or unknown result must be retried with the same key and an equivalent request. Once Coaching receives a terminal review or invalidated result, corrected identity material is a new logical attempt and must use a new key. Editing a terminal attempt in place is forbidden.
If Platform returns success but Coaching's local Coach transaction fails or has an unknown outcome, Coaching must keep the Platform operation key and normalized request immutable, replay the Platform operation, and retry or reconcile only its local Coach transaction. It must not use a new key to mint another Person. After a terminal review, an operator resolves the identity issue, Coaching preserves the reviewed attempt for audit, and the corrected submission uses a new key linked locally to the predecessor attempt.
Platform will use serializable isolation and a transaction-scoped advisory lock keyed by tenant and normalized phone. Different operation keys and different Platform writers for the same phone therefore serialize before candidate selection or minting. Existing intake and bridge writers will share the locking and mint-serialization primitive so they cannot race the endpoint. ADR-0051 does not change their existing matching thresholds or make the coach-specific strict name predicate an intake policy. Any broader intake-policy change requires separate review. Direct repository creation remains internal.
The endpoint resolves Person identity only. The organization header is an authorization boundary, not a Coach-role, organization-membership, or tenancy assignment. Platform success must not create or activate a Coach, project a Coach role, or mark the Person as an active coach. Coaching remains authoritative for its subsequent local Coach transaction and status.
The Identity Contract defines transactional person.created publication for every mint, but person.created is currently absent from both event-type registries and has no registered payload schema. Before this endpoint can be Ready, Platform must register and mirror person.created v1, publish its full ten-field payload in the same transaction as a mint, and validate the registry. This endpoint must use coach-resolution provenance only and must not fabricate an intake.captured origin or emit intake.matched.
Failures and retry behavior
- HTTP 400: malformed JSON, missing required fields, or invalid idempotency key.
- HTTP 401: missing or invalid service token.
- HTTP 403: wrong subject, audience, scope, tenant, or organization.
- HTTP 409: review required, invalidated prior result, or idempotency-key reuse with a different request.
- HTTP 422: invalid phone or identity material.
- HTTP 503: transient serialization or database failure after bounded internal retries. Coaching retries the identical request with the same key and observes
Retry-Afterwhen present. - HTTP 500: unexpected failure. Coaching retries the identical request with the same key. If the transaction committed before response loss, the journal replays it.
Privacy and audit
Success responses may contain the canonical Person's ten contract fields, including canonical given_name, family_name, and display_name. Responses must not echo raw request names or contact values. Review and invalidation responses must not expose candidate or prior Person names. Journal rows and application logs contain no raw request names, canonical names, phone numbers, or email addresses. Audit records contain request identifier, operation key, caller subject, tenant, organization, outcome, Person identifier or candidate identifiers, and reason code. Responses use Cache-Control: no-store.
Approval request and release boundary
Coaching, please confirm operation-key ownership, request fields, stable outcomes, review handling, and retry behavior. Sales, please confirm the read-only verification surface is sufficient for its accepted ADR-0051 role.
After approval, Platform will publish the additive contract changes, implement the endpoint and shared resolver, prove same-key replay and different-key concurrency, and report a Ready deployment with service-auth configuration evidence. Until those steps complete, coach-person-resolution-interface-ready remains uncleared and Coaching must not depend on this endpoint in production.
Ready evidence must also include exact production workload-exchange proof, zero-mutation validation failures, single-part-name cases, terminal-review recovery, unknown-response and local-Coach-write recovery, stale-Person replay invalidation, and transactional person.created publication after the registry discrepancy is resolved.