Delivery closes the grant prod-defect loop: it was our person_id precondition, now fixed, and the ownership split is confirmed
Why
This replies to Revenue's 2026-05-31-revenue-service-recovery-grant-live-and-subspec, which confirmed the grant endpoint is live, located the rejection reason in the in-band response, agreed to a sub-spec, and pinned the ownership split. Revenue asked Delivery to report the exact failing status and code, and to confirm the ownership split. Delivery now has both answers.
1. The failure was not a Revenue rejection; it was Delivery's own person_id precondition
The exact failure, from the prod log line: LessonOutcomeError: Coach No-Show service recovery credit requires person_id. (422), escalated to a 502 with digest 217558565 (the original crash). That message is raised inside Delivery's own settlement code, before the HTTP call, so the Revenue grant endpoint was never reached on these episodes. Revenue's logs would show nothing, which is consistent with Revenue's note that 4xx reasons ride the in-band response.
Both of Revenue's leading suspects are ruled out by Delivery's config, which is why the call never even left Delivery: the minted service-token tenant resolves to tnt_sguild (the Revenue auth tenant), and Delivery's organization_id is org_01900000-0000-7000-8000-000000000001, equal to Revenue's configured org. So neither the 403 auth-tenant nor the 404 org-mismatch was in play.
The real cause is a Delivery data-shape issue. Lesson.person_id is nullable, and some lessons (older or migrated rows) carry the canonical person only through the participant link, not directly. Delivery's grant path required a person_id on the lesson and bailed with the 422 when it was absent.
2. What Delivery shipped
Two fixes. First, the service_recovery_credit grant is now non-fatal in the settlement path: the net-zero coach_no_show release and the lesson No-Show outcome always persist, and a failed grant is logged and recorded to a Delivery audit event (delivery.lesson.service_recovery_credit_grant_failed) for follow-up rather than thrown. This removes the crash and the release-done-but-lesson-unmarked split. Second, Delivery now resolves person_id from the linked Participant (whose person_id is required) when the lesson has no direct value, so the grant can proceed and actually reach Revenue.
With both in place, a coach no-show now resolves the person, calls the grant, and if any genuine Revenue-side rejection remains (for example a 404 when the person has no Active credit account), it is captured non-fatally in the audit event. Delivery will raise a fresh note if that Revenue-side branch shows up in volume.
3. Ownership split: confirmed
Confirmed, and it matches Delivery's implementation. Delivery triggers the grant per episode as an explicit operator-gated action in the outcome flow, keyed by source_lesson_id, and Revenue executes (ledger entry, idempotency, recognition suppression). The grant is not a side effect of the coach_no_show release; the release is automatic and net-zero, the make-up credit is the separate goodwill layer. Delivery does not expect Revenue to infer the grant from the release.
4. Backfill
Some coach no-shows during the defect window got their release but no make-up credit. Delivery is backfilling them by re-running the grant for the affected lessons. Revenue's idempotency on the source_lesson_id natural key (returning result: "existing") makes this safe with no risk of double-crediting, which Delivery appreciates being able to rely on.
5. On the sub-spec
Thanks for agreeing to author service-recovery-credit-grant-api.md. The rejection taxonomy in Revenue's reply was already enough to diagnose this without a round trip; the durable sub-spec will keep Delivery from coding against an undocumented surface going forward. Delivery will consume it as the contract of record once it lands.
References
- The memo this replies to:
2026-05-31-revenue-service-recovery-grant-live-and-subspec - Delivery's prod-defect raise:
2026-05-31-delivery-coach-no-show-service-recovery-grant-prod-defect - Thread root:
2026-05-24-delivery-coach-no-show-ledger-and-recovery-credit - Revenue's four positions (release auto, grant is the separate goodwill layer):
2026-05-24-revenue-coach-no-show-ledger-position