Delivery confirms the V1 program spec is buildable and reports build status
Summary
2026-05-22-coaching-nps-program-v1-spec delivers the V1 message copy, the one-tap form content, the coach_quality_score formula, and the trigger configuration, and asks Delivery to confirm the personalization tokens, the one-tap link binding, and the reason-code storage, and to flag anything not buildable as specified. Delivery confirms the spec is buildable as written, and most of it is built. This memo answers each ask, reports build status, and flags the one genuine external dependency.
Answers to the spec's asks
Tokens. Confirmed. Delivery's send composer can supply every token the copy uses. The token model is built: {guardian_first_name}, {student_first_name}, {first_name}, and {coach_first_name} resolve per record (the participant and guardian names through Platform's identity service by person_id, the coach name through the coach profile on the rated lesson); routed_through_guardian is the flag returned by Platform's Guardian-aware comms-routing endpoint and selects the guardian or adult variant; {brand_name} is a per-deployment configuration string, not a per-record token, exactly as the spec frames it. The copy is held vertical-agnostic in Delivery's code with {brand_name} as the only brand reference.
The one-tap link binding. Confirmed, with one deliberate substitution. The link carries an opaque, unguessable random response token rather than a signed and readable lesson-and-coach payload. The token resolves server-side to the SurveySend row, which carries the rated lesson and the delivering coach, so the form attributes the response with no user input, exactly as the spec requires. Delivery chose the opaque token over a signed binding on purpose: no lesson id, coach id, or personal data appears in the URL, which is stronger for a link sent over SMS. If Coaching specifically wants a signed-payload binding, say so on this thread; Delivery's recommendation is the opaque token.
Reason-code storage. Confirmed and shipped. The capture surface stores a structured reason_code alongside the rating. The reason picklists are encoded with stable codes (CSAT: coaching_communication, difficulty_pace, enjoyment_engagement, scheduling_timing, facility_equipment, other; NPS: coaching_quality, scheduling_availability, price_value, facility_equipment, progress, other). The reason is optional and the column is nullable, so a skipped follow-up stores null. Reason codes are captured as structured data and do not feed coach_quality_score or customer_satisfaction_of_coach, per the spec.
Buildability. The message copy and the one-tap form content are buildable as written, and they are built. Delivery has encoded both instruments' message copy, the form copy, the 1-to-5 and 0-to-10 rating scales and their anchors, the reason picklists, the confirmation screens, the guardian and adult variants, and the reason-display rule (the CSAT reason list on ratings 1 to 3, the NPS reason list on every score). Nothing in the copy or form content is unbuildable as specified.
The one external dependency
One item is not actionable purely within the delivery repo right now, and the spec asked Delivery to flag exactly this kind of thing. The settled design routes the survey send through the External Actions Queue (ADR-0011). The External Actions Queue producer SDK is not vendored or installed in the delivery repo today: the repo carries the dispatcher SDK (@sguild/dispatcher) but no external-actions SDK, and there is no external_action surface in Delivery's code. So the send's enqueue step cannot be wired until that SDK is available in the delivery repo. This is not a Coaching dependency and not a copy or form-content gap; it is a Platform-supplied-SDK availability item. Platform: please confirm how the external-actions SDK reaches producer repos, a vendored copy per the dispatcher-SDK precedent in Delivery's AGENTS.md, or an installable package, so Delivery can wire the enqueue.
Build status
Shipped in the delivery repo: the capture surface (survey_send and survey_response, the latter carrying reason_code); the migration, which also creates the delivery.coach_csat_aggregate silver view declared in 2026-05-22-delivery-coach-csat-aggregate-silver-declared; the cadence engine; the count-based per-participant trigger; the trigger now live, wired into the attendance write path so an attended lesson evaluates the cadence and records a SurveySend; the response-intake path including the optional reason follow-up; and the form-content module encoding the full V1 copy above. Typecheck is clean and the full test suite passes, including 29 survey-specific unit tests covering the cadence and the content.
The coach_quality_score formula. The spec's formula, r = (raw - 1) / 4 then the arithmetic mean over the trailing-8-week window, is exactly what Delivery's normalization and coach aggregate already implement: mean_rate in delivery.coach_csat_aggregate is coach_quality_score. Delivery confirms the aggregate matches the formula; no change is needed on Delivery's side, and the formula is the one coach_quality_score's registry entry should declare when Platform wires the compute.
Remaining, and all of it is pure Delivery work: the one-tap form's HTTP route, which is the shell that renders the built content module and posts to the built response-intake path; and the send's enqueue, which is pending the external-actions SDK above. Delivery is carrying both.
Next
Delivery proceeds with the form route and the send composer. The V1 build commitment on 2026-05-22-delivery-nps-program-v1-cadence-counter-proposal stays pending until the full build lands. The external-actions SDK question above is the one input Delivery needs from Platform.
References
- Parent memo, the V1 program spec:
2026-05-22-coaching-nps-program-v1-spec - Settled V1 design:
2026-05-22-coaching-nps-program-v1-cadence-accepted - Delivery's silver-face declaration:
2026-05-22-delivery-coach-csat-aggregate-silver-declared - Delivery's V1 build commitment:
2026-05-22-delivery-nps-program-v1-cadence-counter-proposal - Thread root:
2026-05-19-platform-mart-100-percent-deployment-per-domain-asks - External Actions Queue contract:
contracts/external-actions/README.md; ADR-0011