@sguild/dispatcher 2.0.0 → 2.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (96) hide show
  1. package/README.md +4 -1
  2. package/contracts/README.md +30 -0
  3. package/contracts/coach-availability/README.md +355 -0
  4. package/contracts/coach-availability/README.v2.md +263 -0
  5. package/contracts/coach-availability/schema/payloads/coach.assigned-v1.json +91 -0
  6. package/contracts/coach-availability/validation/delivery-assignment.md +89 -0
  7. package/contracts/coach-availability/validation/sales-offer-construction.md +76 -0
  8. package/contracts/coaching-confirmation/README.md +96 -0
  9. package/contracts/coaching-confirmation/schema/payloads/coaching.lesson.confirmation_decided-v1.json +142 -0
  10. package/contracts/coaching-confirmation/schema/payloads/lead.coach.confirmation.requested-v1.json +124 -0
  11. package/contracts/credit-reservation-funding-state/README.md +147 -0
  12. package/contracts/credit-reservation-lock/README.md +433 -0
  13. package/contracts/credit-reservation-lock/delivery-state-vocabulary.md +73 -0
  14. package/contracts/credit-reservation-lock/reservation-create-api.md +191 -0
  15. package/contracts/credit-reservation-lock/reservation-release-api.md +171 -0
  16. package/contracts/credit-reservation-lock/schema/payloads/credit.locked-v1.json +1 -1
  17. package/contracts/credit-reservation-lock/schema/payloads/credit.reserved-v1.json +2 -3
  18. package/contracts/credit-reservation-lock/validation/lesson-lifecycle.md +318 -0
  19. package/contracts/event-envelope/README.md +205 -0
  20. package/contracts/event-envelope/schema/envelope-v1.json +2 -2
  21. package/contracts/event-envelope/validation/event-vocabulary.md +270 -0
  22. package/contracts/event-types-registry.json +337 -24
  23. package/contracts/external-actions/README.md +338 -0
  24. package/contracts/finance-mart/README.md +238 -0
  25. package/contracts/finance-mart/cac-payback.md +113 -0
  26. package/contracts/finance-mart/cash-position.md +98 -0
  27. package/contracts/finance-mart/cohort-summary.md +72 -0
  28. package/contracts/finance-mart/customer-journey-audit.md +92 -0
  29. package/contracts/finance-mart/ltv.md +92 -0
  30. package/contracts/finance-mart/margin.md +87 -0
  31. package/contracts/finance-mart/pnl.md +83 -0
  32. package/contracts/finance-mart/reconciliation.md +98 -0
  33. package/contracts/finance-mart/revenue-recognition-rollup.md +87 -0
  34. package/contracts/finance-mart/unit-economics.md +94 -0
  35. package/contracts/growth-warehouse-api/README.md +162 -0
  36. package/contracts/identity/README.md +184 -0
  37. package/contracts/identity/person-canonical-fields.md +120 -0
  38. package/contracts/identity/person-externals.md +267 -0
  39. package/contracts/identity/person-resolution-semantics.md +144 -0
  40. package/contracts/identity/person-role-taxonomy.md +120 -0
  41. package/contracts/identity/schema/payloads/intake.captured-v2.json +60 -0
  42. package/contracts/identity/schema/payloads/intake.matched-v2.json +123 -0
  43. package/contracts/identity/schema/payloads/person.updated-v1.json +8 -2
  44. package/contracts/identity/schema/payloads/role.assigned-v1.json +50 -0
  45. package/contracts/identity/schema/payloads/role.retired-v1.json +54 -0
  46. package/contracts/identity/validation/client-table.md +131 -0
  47. package/contracts/identity/validation/coach-handling.md +100 -0
  48. package/contracts/identity/validation/person-graph.md +140 -0
  49. package/contracts/lead-lifecycle/README.md +187 -0
  50. package/contracts/lead-lifecycle/schema/payloads/lead.handoff.context.recorded-v1.json +108 -0
  51. package/contracts/lead-lifecycle/schema/payloads/lead.qualified-v1.json +54 -0
  52. package/contracts/lead-lifecycle/schema/payloads/sales.lead.onboarded-v1.json +120 -0
  53. package/contracts/lesson-lifecycle/README.md +118 -0
  54. package/contracts/lesson-lifecycle/schema/payloads/lesson.cancelled-v1.json +30 -0
  55. package/contracts/lesson-lifecycle/schema/payloads/lesson.delivered-v1.json +29 -0
  56. package/contracts/lesson-lifecycle/schema/payloads/lesson.rescheduled-v1.json +157 -0
  57. package/contracts/lesson-lifecycle/schema/payloads/lesson.scheduled-v1.json +107 -0
  58. package/contracts/lesson-lifecycle/validation/README.md +5 -0
  59. package/contracts/mart-consumer-api/README.md +108 -0
  60. package/contracts/order-flow/README.md +106 -0
  61. package/contracts/order-flow/schema/payloads/order.created-v1.json +58 -0
  62. package/contracts/order-flow/schema/payloads/order.updated-v1.json +63 -0
  63. package/contracts/payment-flow/README.md +157 -0
  64. package/contracts/platform-comms/README.md +84 -0
  65. package/contracts/platform-comms/schema/payloads/platform.comms.inbound-v1.json +83 -0
  66. package/contracts/platform-geography-snapshot/README.md +205 -0
  67. package/contracts/platform-geography-snapshot/schema/payloads/geography.market.archived-v1.json +36 -0
  68. package/contracts/platform-geography-snapshot/schema/payloads/geography.market.upserted-v1.json +59 -0
  69. package/contracts/platform-geography-snapshot/schema/payloads/geography.service-area.archived-v1.json +36 -0
  70. package/contracts/platform-geography-snapshot/schema/payloads/geography.service-area.upserted-v1.json +65 -0
  71. package/contracts/portfolio-mart/README.md +133 -0
  72. package/contracts/portfolio-mart/cohort-funnel-panel.md +76 -0
  73. package/contracts/portfolio-mart/cross-discipline-performance.md +91 -0
  74. package/contracts/portfolio-mart/cross-market-performance.md +84 -0
  75. package/contracts/portfolio-mart/health-composites.md +88 -0
  76. package/contracts/portfolio-mart/org-topology.md +70 -0
  77. package/contracts/portfolio-mart/portfolio-level-funnel-health.md +92 -0
  78. package/contracts/portfolio-mart/validation/consumer-isolation.md +33 -0
  79. package/contracts/portfolio-mart/validation/decoupling-discipline.md +34 -0
  80. package/contracts/refund-flow/README.md +136 -0
  81. package/contracts/refund-flow/sales-callable-refund-initiation-api.md +218 -0
  82. package/contracts/sales-scheduling-surface/README.md +532 -0
  83. package/contracts/sales-scheduling-surface/schema/payloads/delivery.lesson-hold.cancelled-v1.json +42 -0
  84. package/contracts/sales-scheduling-surface/schema/payloads/delivery.lesson-hold.created-v1.json +115 -0
  85. package/contracts/sales-scheduling-surface/validation/composite-hold-create.md +97 -0
  86. package/contracts/sales-scheduling-surface/validation/lock-state-machine-conformance.md +84 -0
  87. package/contracts/sales-scheduling-surface/validation/sales-close-orchestration.md +77 -0
  88. package/contracts/warehouse-silver/README.md +118 -0
  89. package/contracts/warehouse-silver/coaching-utilization-columns.md +105 -0
  90. package/dist/events.d.ts +63 -0
  91. package/dist/events.js +293 -0
  92. package/dist/index.d.ts +2 -0
  93. package/dist/index.js +7 -1
  94. package/dist/postgres-consumer.js +2 -1
  95. package/dist/validator.js +1 -0
  96. package/package.json +1 -1
@@ -0,0 +1,107 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "$id": "https://contracts.sguild/lesson-lifecycle/schema/payloads/lesson.scheduled-v1.json",
4
+ "title": "lesson.scheduled payload v1",
5
+ "description": "Payload for the lesson.scheduled event_type. Emitted by Delivery when a lesson is first scheduled. The event is scheduling-side evidence only; Revenue-owned credit lifecycle facts remain on credit.* events.",
6
+ "type": "object",
7
+ "additionalProperties": false,
8
+ "required": [
9
+ "lesson_id",
10
+ "organization_id",
11
+ "participant_id",
12
+ "reservation_id",
13
+ "reservation_lock_id",
14
+ "coach_id",
15
+ "lesson_site_id",
16
+ "service_area_id",
17
+ "lesson_zip",
18
+ "lesson_type_id",
19
+ "window"
20
+ ],
21
+ "properties": {
22
+ "lesson_id": {
23
+ "type": "string",
24
+ "minLength": 1
25
+ },
26
+ "organization_id": {
27
+ "type": "string",
28
+ "minLength": 1
29
+ },
30
+ "participant_id": {
31
+ "type": "string",
32
+ "minLength": 1
33
+ },
34
+ "reservation_id": {
35
+ "type": [
36
+ "string",
37
+ "null"
38
+ ],
39
+ "minLength": 1
40
+ },
41
+ "reservation_lock_id": {
42
+ "type": [
43
+ "string",
44
+ "null"
45
+ ],
46
+ "minLength": 1
47
+ },
48
+ "coach_id": {
49
+ "type": [
50
+ "string",
51
+ "null"
52
+ ],
53
+ "minLength": 1
54
+ },
55
+ "lesson_site_id": {
56
+ "type": [
57
+ "string",
58
+ "null"
59
+ ],
60
+ "minLength": 1
61
+ },
62
+ "service_area_id": {
63
+ "type": [
64
+ "string",
65
+ "null"
66
+ ],
67
+ "minLength": 1
68
+ },
69
+ "lesson_zip": {
70
+ "type": [
71
+ "string",
72
+ "null"
73
+ ],
74
+ "minLength": 1
75
+ },
76
+ "lesson_type_id": {
77
+ "type": [
78
+ "string",
79
+ "null"
80
+ ],
81
+ "minLength": 1
82
+ },
83
+ "window": {
84
+ "$ref": "#/$defs/window"
85
+ }
86
+ },
87
+ "$defs": {
88
+ "window": {
89
+ "type": "object",
90
+ "additionalProperties": false,
91
+ "required": [
92
+ "start",
93
+ "end"
94
+ ],
95
+ "properties": {
96
+ "start": {
97
+ "type": "string",
98
+ "format": "date-time"
99
+ },
100
+ "end": {
101
+ "type": "string",
102
+ "format": "date-time"
103
+ }
104
+ }
105
+ }
106
+ }
107
+ }
@@ -0,0 +1,5 @@
1
+ # Lesson Lifecycle contract: validation notes
2
+
3
+ No validation notes yet. Validation notes for the lesson-lifecycle payload
4
+ schemas (sample envelopes, conformance checks, producer/consumer fixtures)
5
+ land here as they are written.
@@ -0,0 +1,108 @@
1
+ # Mart Consumer API Contract (Them OS Requirements)
2
+
3
+ **Status:** v1.0.0 — requirements received; sections not yet shipped
4
+ **Date:** 2026-05-17
5
+ **Owner:** platform (mart infrastructure and identity spine); per-section owners listed below
6
+ **Consumers:** Them OS (forecasting engine; sole consumer of this API surface)
7
+ **Related ADRs:** ADR-0016 (medallion warehouse layout), ADR-0015 (Finance and Portfolio as cross-domain domains), ADR-0014 (org as multi-market collective), ADR-0013 (Platform-owned canonical geography), ADR-0019 (OrgMarket junction and tenancy grain)
8
+ **Sub-specs (authoritative):** per-section field manifests land as siblings under this contract as sections ship; see `finance-mart/` and `portfolio-mart/` for the two cross-domain sections already under development
9
+
10
+ ## 1. Purpose and scope
11
+
12
+ This contract records the requirements that Them OS (the forecasting engine) has specified for the data mart. The mart sits between the per-domain operational warehouses and Them OS. It exposes seven read-only API sections. The mart is NOT a forecasting engine; it is a data-shape contract. Them OS does all forecasting in-process and holds no upstream warehouse credentials.
13
+
14
+ The full Them OS requirements document is the authoritative specification. This README is the coordination-repo entry point: it records the section structure, per-section ownership, the cross-cutting requirements that Platform owns, and the known alignment gaps between the requirements and Platform's current schema.
15
+
16
+ ## 2. Architecture (per Them OS spec §1)
17
+
18
+ Three layers:
19
+
20
+ 1. **Per-domain operational warehouses** — Growth, Sales, Delivery, Coaching, Revenue source DBs. Upstream of the mart. Not accessible to Them OS.
21
+ 2. **The data mart** — seven read-only API sections. The firewall lives here. Platform owns the mart infrastructure and the identity/geography spine that all sections share.
22
+ 3. **Them OS** — reads from the mart's seven sections. Forecasts in-process.
23
+
24
+ Section access is asymmetric:
25
+ - **Firewalled sections (1–5):** Growth, Sales, Delivery, Coaching, Revenue. Each reads ONLY its own domain's warehouse. The mart's API output for these sections exposes no shared customer/lesson/coach key across sections.
26
+ - **Cross-domain sections (6–7):** Finance, Portfolio. Read from ANY warehouse. Their API output may carry cross-domain context per the carve-outs in the spec.
27
+
28
+ ## 3. Section ownership
29
+
30
+ | Section | Domain owner | Warehouse source | Status |
31
+ |---|---|---|---|
32
+ | 1. Growth | growth | growth warehouse | Not started |
33
+ | 2. Sales | sales | sales warehouse | Not started |
34
+ | 3. Delivery | delivery | delivery warehouse | Not started |
35
+ | 4. Coaching | coaching | coaching warehouse | Not started |
36
+ | 5. Revenue | revenue | revenue warehouse | Not started |
37
+ | 6. Finance | finance | any | In progress (`finance-mart/`) |
38
+ | 7. Portfolio | portfolio | any | In progress (`portfolio-mart/`) |
39
+
40
+ Platform owns the mart shell, the identity/geography spine, the `org_market_id` namespace, the firewall enforcement mechanism, and the `/health`, `/schema`, `/changelog`, and `/corrections` cross-cutting endpoints per the spec.
41
+
42
+ ## 4. Core terminology (binding, per Them OS spec §1.5)
43
+
44
+ | Term | Definition | Platform schema object |
45
+ |---|---|---|
46
+ | Portfolio | The full set of business units the operator runs | — (logical aggregate) |
47
+ | Org | A discipline/vertical (swim, tennis). NOT geographic. | `Organization` |
48
+ | Org-market | A vertical × market pair (dallas-swim). The operational grain. | `OrgMarket` |
49
+ | `org_market_id` | Stable identifier for an org-market pair | `OrgMarket.id` (see §5 gap) |
50
+
51
+ The rollup hierarchy is `portfolio → org → org-market`. Every firewalled section row carries both `org_id` and `org_market_id`. Cross-section joins at the `org_id` / `org_market_id` level are explicitly permitted; customer-grain cross-section joins are prohibited.
52
+
53
+ ## 5. Platform obligations and known gaps
54
+
55
+ ### 5.1 `org_market_id` slug (gap — action required)
56
+
57
+ The Them OS spec uses examples like `"dallas-swim"` as `org_market_id` values, implying a human-readable compound slug. Platform's `OrgMarket.id` is a raw UUID. Platform must decide and publish the canonical `org_market_id` surface before any section ships:
58
+
59
+ **Option A:** Add a `slug` column to `OrgMarket` (e.g. `dallas-swim`) as the published identifier. Stable, human-readable, URL-safe.
60
+ **Option B:** Derive the slug at mart read time from `{org.slug}-{market.slug}` without storing it. Simpler schema, but mart must recompute on every read.
61
+
62
+ Recommendation: Option A. The slug is the stable coordination key for Them OS; storing it on `OrgMarket` makes it explicit and prevents drift if org or market slugs change independently.
63
+
64
+ ### 5.2 `org_market_list` (Portfolio §9.2)
65
+
66
+ The Portfolio section's `org_market_list` metric is a snapshot of all org-markets with status, parent `org_id`, and market tag. Platform's `OrgMarket` table is the direct source. Platform must expose this via the mart shell or confirm Portfolio reads it directly from the warehouse.
67
+
68
+ ### 5.3 Identity spine as the firewall backbone
69
+
70
+ The §2.5 identifier discipline (each firewalled section exposes `org_id` and `org_market_id` but no cross-section customer keys) depends on Platform's `person_spine` being the identity resolution surface. Silver faces join to `person_spine` on `person_id`; the mart's firewall then strips the `person_id` from the API surface and exposes only the `org_market_id` grain. Platform must document this mechanism for each section owner.
71
+
72
+ ### 5.4 Aggregation rule declarations (`_org_rollup_rule`)
73
+
74
+ The spec requires every metric exposed at both `per-org-market` and `per-org` grains to carry a `_org_rollup_rule` field (sum, weighted_mean_by_revenue, etc.). Platform's mart shell must support this field at the schema level; section owners declare the value per metric.
75
+
76
+ ### 5.5 Cross-cutting endpoints
77
+
78
+ Platform owns the mart-level `/health`, `/schema`, `/changelog`, and `/corrections` endpoints per spec §§2.1–2.8. These are not domain-section outputs; they are mart infrastructure.
79
+
80
+ ## 6. Contract versioning and deprecation (per spec §2.1)
81
+
82
+ Each section exposes a `contract_version` string following semver. MAJOR bump = field removed/renamed/semantics changed. MINOR = field added. PATCH = doc/computation fix.
83
+
84
+ Them OS pins to MAJOR.MINOR per section. Platform's mart must run prior MAJOR versions in parallel for ≥ 60 days after a new MAJOR ships.
85
+
86
+ ## 7. Freshness SLOs summary
87
+
88
+ | Section | Fastest metric SLO | Slowest metric SLO |
89
+ |---|---|---|
90
+ | Growth | lead volume ≤ 2h | attribution windows ≤ 24h |
91
+ | Sales | first_lock_count ≤ 1h | pipeline snapshots ≤ 15min |
92
+ | Delivery | lesson_completed_count ≤ 1h | cohort curves ≤ 24h |
93
+ | Coaching | capacity/scheduled hours ≤ 2h | roster ≤ 24h |
94
+ | Revenue | revenue_paid ≤ 1h | revenue_earned ≤ 24h |
95
+ | Finance | margin ≤ 4h | LTV cohort ≤ 24h |
96
+ | Portfolio | attention_queue ≤ 30min | initiative_progress ≤ 4h |
97
+
98
+ ## 8. Firewall enforcement summary
99
+
100
+ The mart enforces the firewall at the API layer, not at the warehouse layer. Them OS verifies firewall enforcement via an automated leakage test (attempt cross-section identity joins; test must fail) as part of each section's acceptance criteria.
101
+
102
+ Platform is responsible for the firewall mechanism in the mart shell. Domain section owners are responsible for not including prohibited identifiers in their section's output schema.
103
+
104
+ ## 9. Change log
105
+
106
+ | Version | Date | Notes |
107
+ |---|---|---|
108
+ | v1.0.0 | 2026-05-17 | Initial filing of Them OS requirements. Sections not yet shipped. Platform gaps in §5 are open action items. |
@@ -0,0 +1,106 @@
1
+ # Order Flow
2
+
3
+ **Status:** v1.0.0
4
+ **Date:** 2026-05-14
5
+ **Owner:** revenue (orders module)
6
+ **Consumers:** revenue (internal reconciliation), sales (lead-reactivation close and cadence-stop signals), growth (funnel-close attribution), delivery (FYI-grade order-existence and status visibility), platform-warehouse (analytics ingestion)
7
+ **Related ADRs:** ADR-0001 (tenant_id), ADR-0002 (canonical entity ID template), ADR-0003 (Person canonical), ADR-0005 (event envelope), ADR-0009 (dispatcher transport, producer transactional guarantee)
8
+ **Related contracts:** Event Envelope Contract (`../event-envelope/README.md`), Identity Contract (`../identity/README.md`), Payment Flow Contract (`../payment-flow/README.md`), Refund Flow Contract (`../refund-flow/README.md`), Credit Reservation Lock Contract (`../credit-reservation-lock/README.md`)
9
+ **Sub-specs (authoritative):** `schema/payloads/order.created-v1.json`, `schema/payloads/order.updated-v1.json`
10
+ **Validations:** none yet
11
+
12
+ ## 1. Purpose and scope
13
+
14
+ This contract specifies the producer responsibilities and consumer-visible payload shape for Revenue's two order-flow events: `order.created` and `order.updated`. Both events are produced by Revenue at writeback transaction commit time for canonical Order state changes.
15
+
16
+ In scope: the event names, payload shapes at v1, producer transactional guarantee discipline, canonical-field update rule, consumer responsibilities, and versioning policy.
17
+
18
+ Out of scope: payment settlement details (covered by `payment-flow`), refund settlement details (covered by `refund-flow`), credit reservation lock semantics (covered by `credit-reservation-lock`), order item line-grain events, and internal operator-only metadata edits.
19
+
20
+ ## 2. Normative language
21
+
22
+ The key words MUST, MUST NOT, REQUIRED, SHALL, SHALL NOT, SHOULD, SHOULD NOT, and MAY are to be interpreted per RFC 2119.
23
+
24
+ ## 3. Terminology
25
+
26
+ **Order.** A Revenue-owned commercial record, identified by `ord_<UUID v7 canonical>` per ADR-0002. It is keyed to `person_id` and organization scope through Revenue's canonical store and event envelope.
27
+
28
+ **Order writeback.** The Revenue-internal Prisma transaction that records an Order insert or load-bearing Order field update and publishes the corresponding event through the dispatcher inside the same transaction.
29
+
30
+ **Load-bearing canonical fields.** `status`, `amount_paid_cents`, `amount_total_cents`, and `cancelled_at`. Changes to these fields emit `order.updated`. Free-text notes, operator-only flags, and other internal metadata do not emit `order.updated`.
31
+
32
+ ## 4. Event types
33
+
34
+ ### 4.1 order.created
35
+
36
+ Emitted by Revenue at the writeback transaction commit when an Order row first lands in the canonical Postgres store. The publish lives inside the same `prisma.$transaction` as the Order insert per ADR-0009. Either the Order commits and the event fires, or the transaction rolls back and no event fires.
37
+
38
+ Payload v1: `order_id`, `person_id`, `organization_id`, `status`, `currency`, `amount_total_cents`, `created_via`, `created_at`. See `schema/payloads/order.created-v1.json` for the authoritative shape.
39
+
40
+ Known `created_via` values at v1 are `portal`, `cli`, `webhook_provider_invoice`, `webhook_growth_lead_close`, and `backfill`. Consumers SHALL treat unknown values as safe-to-ignore per the event-envelope additive discipline.
41
+
42
+ ### 4.2 order.updated
43
+
44
+ Emitted by Revenue at the writeback transaction commit when an Order's load-bearing canonical fields change. Typical call sites include paid-state writebacks, cancellation paths, refund-driven status transitions, and amount-total adjustments.
45
+
46
+ Payload v1: `order_id`, `person_id`, `status_from`, `status_to`, `amount_paid_cents_from`, `amount_paid_cents_to`, `amount_total_cents`, `updated_via`, `updated_at`. See `schema/payloads/order.updated-v1.json` for the authoritative shape.
47
+
48
+ Known Order status values at v1 are `DRAFT`, `OPEN`, `PAID`, `PARTIALLY_PAID`, `CANCELLED`, and `REFUNDED`. Known `updated_via` values mirror `created_via` where applicable. Consumers SHALL treat unknown `status`, `created_via`, and `updated_via` values as safe-to-ignore per event-envelope §9.5.
49
+
50
+ ### 4.3 Refund-driven status transitions
51
+
52
+ When a Refund completes against an Order, the Order status may transition to `REFUNDED` or another Revenue-owned status. That status transition emits `order.updated`; the refund itself emits `refund.completed` per the sibling refund-flow contract. Consumers that subscribe to both event families correlate by `order_id`; the two events are not collapsible.
53
+
54
+ ## 5. Producer responsibilities
55
+
56
+ Revenue SHALL emit `order.created` exactly once for each Order insert that commits in the canonical store.
57
+
58
+ Revenue SHALL emit `order.updated` exactly once for each committed update to a load-bearing canonical Order field.
59
+
60
+ Revenue SHALL emit both event types inside the same Prisma transaction as the Order writeback per ADR-0009. The publish call is conventionally the last statement in the transaction, so earlier rollback prevents the event from firing.
61
+
62
+ Revenue SHALL NOT put `tenant_id` in the payload. Tenant scope rides on the Event Envelope per ADR-0001 and the payment-flow precedent.
63
+
64
+ Revenue SHALL NOT emit `order.updated` for free-text or operator-internal metadata-only edits.
65
+
66
+ ## 6. Consumer responsibilities
67
+
68
+ Consumers SHALL deduplicate by `(consumer, event_id)` per the dispatcher SDK and event-envelope contract.
69
+
70
+ Consumers SHALL treat unknown `status`, `created_via`, and `updated_via` values as safe-to-ignore. A new value is an additive change unless the contract removes or narrows an existing value.
71
+
72
+ Sales consumes `order.created` and `order.updated` for lead-reactivation close, early cadence-stop, and status-transition policy signals.
73
+
74
+ Growth consumes `order.created` for funnel-close attribution at the order grain. Growth does not rely on `order.updated` at v1.
75
+
76
+ Delivery consumes both events as FYI-grade visibility for order existence and status transitions. Delivery's lock state machine continues to use credit and refund events for load-bearing state transitions.
77
+
78
+ Platform warehouse ingests both events for order-grain commercial analytics.
79
+
80
+ ## 7. Idempotency and retry
81
+
82
+ Producer-side idempotency is enforced by the Order writeback transaction and the state-transition validation at each call site. Consumer-side retry follows the dispatcher SDK default retry, dedup, and dead-letter behavior.
83
+
84
+ ## 8. Versioning
85
+
86
+ This contract follows the additive-discipline pattern from the Event Envelope Contract. New optional fields and new enum-like values may land as v1.x changes. Removing fields, renaming fields, narrowing accepted values, or changing types requires a major version bump and a deprecation window.
87
+
88
+ Per-event `schema_version` is independent between `order.created` and `order.updated`.
89
+
90
+ ## 9. Producer responsibilities trace
91
+
92
+ Expected Revenue-side call sites:
93
+
94
+ - `order.created` from the orders module creation path as the Postgres create service lands.
95
+ - `order.updated` from paid-state writebacks in `revenue/src/modules/orders/service.charge-pg.ts`.
96
+ - `order.updated` from future cancellation, refund-completion, partial-payment, and amount-adjustment Postgres paths as they land.
97
+
98
+ This trace updates as part of the same contract change if new event-firing sites are added.
99
+
100
+ ## 10. Trigger to revisit
101
+
102
+ Revisit this contract if OrderItem line-grain events become cross-domain, if a consumer needs `order.cancelled` split out from `order.updated`, if status semantics change materially, or if a new payment/refund provider creates Order state transitions that the v1 payload cannot describe additively.
103
+
104
+ ## 11. Change log
105
+
106
+ - **v1.0.0** (2026-05-14): Initial release. Adds `order.created` and `order.updated` with v1 payload schemas and registry entries. Publishes the order-flow sibling contract next to payment-flow and refund-flow. Incorporates Platform's signoff modifications: status is v1.0.0 at publication, `tenant_id` remains envelope-only, Delivery is included after its day-one FYI ack, and enum-like values are documented while remaining open at the consumer boundary.
@@ -0,0 +1,58 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "$id": "https://contracts.sguild/order-flow/schema/payloads/order.created-v1.json",
4
+ "title": "order.created payload v1",
5
+ "description": "Payload for the order.created event_type per contracts/order-flow/README.md §4.1. Emitted by Revenue at the writeback transaction commit when an Order row first lands in the canonical Postgres store. Producer SHALL emit inside the same Prisma transaction as the Order insert per ADR-0009. Tenant scope rides on the Event Envelope, not this payload.",
6
+ "type": "object",
7
+ "additionalProperties": false,
8
+ "required": [
9
+ "order_id",
10
+ "person_id",
11
+ "organization_id",
12
+ "status",
13
+ "currency",
14
+ "amount_total_cents",
15
+ "created_via",
16
+ "created_at"
17
+ ],
18
+ "properties": {
19
+ "order_id": {
20
+ "type": "string",
21
+ "description": "The Revenue-owned Order. ord_<UUID v7 canonical> per ADR-0002.",
22
+ "pattern": "^ord_[0-9a-f]{8}-[0-9a-f]{4}-7[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$"
23
+ },
24
+ "person_id": {
25
+ "type": "string",
26
+ "description": "The canonical Person the Order belongs to. per_<UUID v7 canonical> per ADR-0002.",
27
+ "pattern": "^per_[0-9a-f]{8}-[0-9a-f]{4}-7[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$"
28
+ },
29
+ "organization_id": {
30
+ "type": "string",
31
+ "description": "The organization the Order is scoped to. org_<UUID v7 canonical> per ADR-0002.",
32
+ "pattern": "^org_[0-9a-f]{8}-[0-9a-f]{4}-7[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$"
33
+ },
34
+ "status": {
35
+ "type": "string",
36
+ "description": "Initial Order status. Known values at v1: DRAFT, OPEN, PAID, PARTIALLY_PAID, CANCELLED, REFUNDED. Consumers SHALL treat unknown values as safe-to-ignore per event-envelope §9.5."
37
+ },
38
+ "currency": {
39
+ "type": "string",
40
+ "description": "ISO-4217 currency code. USD at v1 in production.",
41
+ "pattern": "^[A-Z]{3}$"
42
+ },
43
+ "amount_total_cents": {
44
+ "type": "integer",
45
+ "description": "Order total in the smallest currency unit at creation time.",
46
+ "minimum": 0
47
+ },
48
+ "created_via": {
49
+ "type": "string",
50
+ "description": "Creation source. Known values at v1: portal, cli, webhook_provider_invoice, webhook_growth_lead_close, backfill. Consumers SHALL treat unknown values as safe-to-ignore per event-envelope §9.5."
51
+ },
52
+ "created_at": {
53
+ "type": "string",
54
+ "format": "date-time",
55
+ "description": "Wall-clock UTC timestamp at the writeback transaction commit. Producer's clock."
56
+ }
57
+ }
58
+ }
@@ -0,0 +1,63 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "$id": "https://contracts.sguild/order-flow/schema/payloads/order.updated-v1.json",
4
+ "title": "order.updated payload v1",
5
+ "description": "Payload for the order.updated event_type per contracts/order-flow/README.md §4.2. Emitted by Revenue at the writeback transaction commit when an Order's load-bearing canonical fields change. Producer SHALL emit inside the same Prisma transaction as the Order update per ADR-0009. Tenant scope rides on the Event Envelope, not this payload.",
6
+ "type": "object",
7
+ "additionalProperties": false,
8
+ "required": [
9
+ "order_id",
10
+ "person_id",
11
+ "status_from",
12
+ "status_to",
13
+ "amount_paid_cents_from",
14
+ "amount_paid_cents_to",
15
+ "amount_total_cents",
16
+ "updated_via",
17
+ "updated_at"
18
+ ],
19
+ "properties": {
20
+ "order_id": {
21
+ "type": "string",
22
+ "description": "The Revenue-owned Order. ord_<UUID v7 canonical> per ADR-0002.",
23
+ "pattern": "^ord_[0-9a-f]{8}-[0-9a-f]{4}-7[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$"
24
+ },
25
+ "person_id": {
26
+ "type": "string",
27
+ "description": "The canonical Person the Order belongs to. per_<UUID v7 canonical> per ADR-0002.",
28
+ "pattern": "^per_[0-9a-f]{8}-[0-9a-f]{4}-7[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$"
29
+ },
30
+ "status_from": {
31
+ "type": "string",
32
+ "description": "Prior Order status. Known values at v1: DRAFT, OPEN, PAID, PARTIALLY_PAID, CANCELLED, REFUNDED. Consumers SHALL treat unknown values as safe-to-ignore per event-envelope §9.5."
33
+ },
34
+ "status_to": {
35
+ "type": "string",
36
+ "description": "New Order status. Known values at v1: DRAFT, OPEN, PAID, PARTIALLY_PAID, CANCELLED, REFUNDED. Consumers SHALL treat unknown values as safe-to-ignore per event-envelope §9.5."
37
+ },
38
+ "amount_paid_cents_from": {
39
+ "type": ["integer", "null"],
40
+ "description": "Previous amount paid in the smallest currency unit. Null when the source row did not carry a prior value.",
41
+ "minimum": 0
42
+ },
43
+ "amount_paid_cents_to": {
44
+ "type": "integer",
45
+ "description": "New amount paid in the smallest currency unit.",
46
+ "minimum": 0
47
+ },
48
+ "amount_total_cents": {
49
+ "type": "integer",
50
+ "description": "Current Order total in the smallest currency unit after the update.",
51
+ "minimum": 0
52
+ },
53
+ "updated_via": {
54
+ "type": "string",
55
+ "description": "Update source. Known values at v1 mirror created_via where applicable: portal, cli, webhook_provider_invoice, webhook_growth_lead_close, backfill. Consumers SHALL treat unknown values as safe-to-ignore per event-envelope §9.5."
56
+ },
57
+ "updated_at": {
58
+ "type": "string",
59
+ "format": "date-time",
60
+ "description": "Wall-clock UTC timestamp at the writeback transaction commit. Producer's clock."
61
+ }
62
+ }
63
+ }
@@ -0,0 +1,157 @@
1
+ # Payment Flow
2
+
3
+ **Status:** v1.0.0
4
+ **Date:** 2026-05-02
5
+ **Owner:** revenue (payment-adapters, orders)
6
+ **Consumers:** sales (Lead reactivation close on payment success per the lead-pipeline coupling), growth (funnel-close attribution), delivery (awareness of credit availability for the credit-reservation-lock state machine), platform-warehouse (analytics ingestion). Coaching does not subscribe at v1; the coach-availability projection is independent of payment timing per `coach-availability` v1.0.1 §4.3.
7
+ **Related ADRs:** ADR-0001 (tenant_id), ADR-0002 (canonical entity ID template; `ord_` and `per_` prefixes), ADR-0003 (Person canonical, Revenue does not own Person), ADR-0005 (event envelope), ADR-0009 (dispatcher transport, producer transactional guarantee), ADR-0010 (provider externals at Platform; Square is the v1 provider, Quo when SMS lands)
8
+ **Related contracts:** Event Envelope Contract (`../event-envelope/README.md`), Identity Contract (`../identity/README.md`), Credit Reservation Lock Contract (`../credit-reservation-lock/README.md`), Refund Flow Contract (`../refund-flow/README.md`)
9
+ **Sub-specs (authoritative):** `schema/payloads/payment.received-v1.json`, `schema/payloads/payment.failed-v1.json`
10
+ **Validations:** none yet
11
+
12
+ ## 1. Purpose and scope
13
+
14
+ This contract specifies the producer responsibilities and consumer-visible payload shape for Revenue's two payment-flow events: `payment.received` and `payment.failed`. Both events are produced by Revenue at the writeback transaction commit for a payment outcome from Sguild's payment provider (Square at v1, with provider-agnostic shape so future Quo or alternative-provider integrations land additive). Both fire under the producer-transactional-guarantee shape from ADR-0009 (dispatcher.publish inside the same Prisma transaction as the order-status update and the external-actions row that records the provider outcome).
15
+
16
+ In scope: the two event names, the payload shapes at v1, the producer's transactional-guarantee discipline, the consumer-visible enum partitions on `payment_method`, `payment_path`, and `failure_code`, idempotency and dedup expectations, and the versioning policy under the additive-discipline rule.
17
+
18
+ Out of scope: the Square adapter's HTTP wire-format and webhook handling (lives in `revenue/src/lib/providers/square/` and the upstream external-actions module); the Refund flow (covered by the sibling `refund-flow` contract); the credit-reservation-lock state machine's funding sub-state transitions (covered by `credit-reservation-lock` §9.3 with `credit.funded` and the reservation.* family pending the cross-domain decision in `2026-05-02-revenue-emit-wiring-registry-additions`); revenue recognition (Revenue-internal, see `domains/revenue.md`); refund initiation against payments (covered by `refund-flow`).
19
+
20
+ ## 2. Normative language
21
+
22
+ The key words MUST, MUST NOT, REQUIRED, SHALL, SHALL NOT, SHOULD, SHOULD NOT, and MAY are to be interpreted per RFC 2119.
23
+
24
+ ## 3. Terminology
25
+
26
+ **Payment writeback.** The Revenue-internal transaction that records a payment outcome: the order-status update (e.g. OPEN to PAID), the amount-paid update on the order, the external-actions row that captures the provider outcome and the reference id, and the dispatcher.publish for the corresponding event. Distinct from the **provider call**, which is the HTTP request to Square (or future provider) that initiates or settles the payment.
27
+
28
+ **Provider call.** The outbound HTTP interaction with the payment provider. Either a Revenue-initiated charge (`runChargeOrder` in `revenue/src/modules/orders/service.ts`), an inbound webhook event the adapter receives (Square invoice payment notification routed through external-actions), or a sync-status check. The provider call's outcome is one fact; the writeback's outcome is a separate fact, per the writeback-separation rule in `domains/revenue.md`.
29
+
30
+ **Active charge.** Revenue-initiated charge against the customer's payment-method-on-file. Synchronous from Revenue's perspective.
31
+
32
+ **Invoice paid.** Customer-initiated payment against an invoice Revenue sent. Asynchronous from Revenue's perspective; Revenue learns of the outcome through a webhook event the provider posts back.
33
+
34
+ **Provider reference.** The provider-side identifier for the payment (Square payment id at v1). Required per the funding-state external-reference rule in `domains/revenue.md` so the audit trail and reconciliation surface have a stable handle from Revenue's records back to the provider's records.
35
+
36
+ ## 4. Event types
37
+
38
+ ### 4.1 payment.received
39
+
40
+ Emitted by Revenue at the writeback transaction commit for a successful payment. The provider call has succeeded (Square reports `COMPLETED` for an invoice payment, or `CAPTURED` for an active charge), the order has been flipped to PAID (or PARTIALLY_PAID for partial payments per the upstream order-amount logic), the amount-paid value has been updated on the order, and the external-actions row has recorded the provider outcome with the provider reference. The publish lives inside the same `prisma.$transaction` as the order update per ADR-0009; either the writeback commits and the event fires, or the writeback rolls back and no event fires (in which case the reconciliation surface running against the Square audit log catches the divergence per the writeback-separation rule).
41
+
42
+ Payload v1: `order_id`, `person_id`, `payment_method`, `payment_path`, `amount_cents`, `currency`, `provider_ref`, `received_at`. See `schema/payloads/payment.received-v1.json` for the authoritative shape and field constraints.
43
+
44
+ The `payment_path` field disambiguates the two writeback paths: `invoice_paid` for the asynchronous webhook-driven path through `runApplyInvoicePayment`, and `active_charge` for the synchronous path through `runChargeOrder`. Consumers that need path-specific behavior route on this field rather than on separate event_types.
45
+
46
+ The `payment_method` field is partitioned per §4.3.
47
+
48
+ ### 4.2 payment.failed
49
+
50
+ Emitted by Revenue at the writeback transaction commit when the provider has reported a payment failure and Revenue has recorded the failure as an external-actions row. The order does NOT flip to PAID on this event; the order status is preserved at its prior value (typically OPEN). The publish lives inside the same `prisma.$transaction` as the external-actions row insert per ADR-0009.
51
+
52
+ Payload v1: `order_id`, `person_id`, `payment_method`, `payment_path`, `amount_cents`, `currency`, `provider_ref`, `failed_at`, `failure_code`, `failure_reason`. See `schema/payloads/payment.failed-v1.json` for the authoritative shape.
53
+
54
+ The `failure_code` field is partitioned per §4.4 and is the consumer-routable signal; `failure_reason` is the free-form provider-side message and SHALL NOT be parsed by consumers (the message format is provider-specific and may change without contract bump).
55
+
56
+ A provider-side failure on retry of an idempotent attempt that previously succeeded is a reconciliation case, not a `payment.failed` case; the original `payment.received` event from the first successful attempt remains the authoritative signal. Revenue's adapter SHALL NOT emit `payment.failed` for retry-of-success failure modes.
57
+
58
+ ### 4.3 payment_method partition
59
+
60
+ The `payment_method` enum at v1 covers the methods Revenue's Square adapter supports today plus the methods the credit-balance flow uses internally. Consumers SHALL treat unknown values as safe-to-ignore per the additive-discipline in §8:
61
+
62
+ - `card`. Credit or debit card payment via Square. The dominant path in practice.
63
+ - `ach`. ACH bank transfer via Square. Lower volume; longer settlement window.
64
+ - `cash`. Cash payment recorded in person at a Sguild facility. The provider call is a no-op (no Square-side capture); the external-actions row records the operator who handled the payment.
65
+ - `check`. Check payment recorded by an operator. Same provider-call shape as `cash` (no Square-side capture); the external-actions row records the check number and the operator.
66
+ - `credit_balance`. Internal payment via the customer's existing credit-account balance (no provider call). Used when an order's total is satisfied by previously-purchased credits without a provider transaction. The external-actions row records the credit-account id and the credit-ledger entries that satisfied the order.
67
+
68
+ ### 4.4 failure_code partition
69
+
70
+ The `failure_code` enum at v1 carries values consumers can reasonably route on (Sales' Lead-reactivation logic differs by failure cause, and Growth's funnel-close attribution treats provider-down failures differently from declined-card failures). Consumers SHALL treat unknown values as safe-to-ignore per the additive-discipline:
71
+
72
+ - `card_declined`. The provider declined the card. Most common failure cause; consumers typically retry on the customer's next interaction rather than auto-retrying.
73
+ - `insufficient_funds`. Specific sub-type of card_declined the provider distinguishes; included separately because Sales' Lead-reactivation routes differently for it.
74
+ - `expired_card`. The card on file is past its expiry date.
75
+ - `network_error`. Transient network failure between Revenue and the provider. Auto-retryable per Revenue's external-actions retry policy.
76
+ - `provider_unavailable`. The provider returned 5xx or timed out. Auto-retryable.
77
+ - `other`. Catchall for failure causes not yet partitioned. Phase-2 narrowing may surface additional values; the additive-discipline lets Revenue add values without consumer breakage.
78
+
79
+ ## 5. Producer responsibilities
80
+
81
+ Revenue SHALL emit `payment.received` exactly once per successful payment. Idempotency is enforced at the Prisma transaction level: the writeback transaction is keyed by the provider reference and the attempt number, and a duplicate writeback attempt against the same `(provider_ref, order_id)` no-ops at the upstream `runApplyInvoicePayment` or `runChargeOrder` validation rather than re-emitting.
82
+
83
+ Revenue SHALL emit `payment.received` and `payment.failed` inside the same Prisma transaction as the corresponding writeback per ADR-0009's producer-transactional-guarantee shape. The publish call is the last statement in the transaction by convention, so any earlier rollback prevents the event from firing.
84
+
85
+ Revenue SHALL NOT emit `payment.received` if the provider call succeeded but the writeback transaction rolled back. The divergence is detectable by the reconciliation surface running against the Square audit log; it is not the producer's job to emit a "writeback failed" event for it. The reconciliation surface is Revenue-internal and lives outside this contract's scope.
86
+
87
+ Revenue SHALL populate `provider_ref` on every emit. The funding-state external-reference rule in `domains/revenue.md` requires this: a payment-flow event without a provider reference is a bug. For `payment_method` values that have no provider call (`cash`, `check`, `credit_balance`), the `provider_ref` SHALL be the external-actions row id, prefixed with `ext_` to distinguish from provider-issued ids.
88
+
89
+ Revenue SHALL emit `payment.failed` only for the first failed attempt; subsequent retries against the same `(provider_ref, order_id)` that also fail SHALL NOT re-emit. Consumers see one `payment.failed` per order's failed-payment attempt, not one per retry.
90
+
91
+ Revenue MAY emit `payment.received` for an order that previously emitted `payment.failed`; this is the recovery case (the customer fixed the underlying issue and the next attempt succeeded). Consumers SHOULD treat the latest event as the authoritative state.
92
+
93
+ ## 6. Consumer responsibilities
94
+
95
+ Consumers SHALL handle `payment.received` and `payment.failed` independently; neither presupposes the other. An order's first payment attempt may emit `payment.received` directly without a prior `payment.failed`.
96
+
97
+ Consumers SHALL treat unknown values for the `payment_method` and `failure_code` enums as safe-to-ignore per the additive-discipline in §8. The closed-set guarantee is at v1 publish time; future v1.x versions may add values without a consumer cutover.
98
+
99
+ Consumers SHALL NOT parse `failure_reason`. The field is provider-specific free-form text and may change format. Use `failure_code` for routing.
100
+
101
+ Consumers SHALL handle dispatcher redelivery. Per the event-envelope §9.2, Revenue's idempotency at the writeback level does not preclude the dispatcher redelivering the same `event_id` to a consumer that crashed mid-handler; consumers maintain per-(consumer, event_id) dedup tables per the SDK's at-most-once invocation guarantee.
102
+
103
+ Sales SHALL NOT independently compute "did this payment close a Lead"; the `customer.handoff` event from `credit-reservation-lock` §5 is the authoritative Lead-close signal for purchase paths that involve credit reservations. The `payment.received` event is the input to Sales' separate Lead-reactivation flow (a customer paying off an unpaid invoice while the Lead is in reactivation cadence).
104
+
105
+ Growth's funnel-close attribution SHOULD route on `payment_path`: `invoice_paid` events tie to a different funnel stage than `active_charge` events. The semantics are Growth-internal and not part of this contract.
106
+
107
+ Delivery's awareness of credit availability is downstream of the credit-reservation-lock contract's `credit.purchased` and `credit.funded` events; `payment.received` is informational for Delivery and does not trigger Delivery-side state changes directly.
108
+
109
+ ## 7. Idempotency and retry
110
+
111
+ Per ADR-0009, the dispatcher SDK guarantees at-most-once handler invocation per `(consumer, event_id)` via the per-(consumer, event_id) dedup table. Consumer handlers SHOULD be idempotent on their own work even with this guarantee, because handler failures mid-write can leave partial state that retries against.
112
+
113
+ Revenue's producer-side idempotency is enforced by the upstream order-amount logic: a duplicate `runApplyInvoicePayment` or `runChargeOrder` against the same `(provider_ref, order_id)` is a no-op at the validation layer and never reaches the writeback transaction or the dispatcher.publish.
114
+
115
+ Dispatcher delivery retry follows the SDK's default: exponential backoff with jitter, default 3 retries, then dead-letter to the platform-managed DLQ table per ADR-0009 action item 6.
116
+
117
+ ## 8. Versioning
118
+
119
+ This contract follows the additive-discipline pattern from `event-envelope` §11. New fields MUST default to null or a backwards-compatible value. New enum values MUST be appended; consumers MUST treat unknown enum values as safe-to-ignore. Removal, type change, or narrowing of existing enum values requires a major version bump and a producer cutover with a deprecation window per the event-envelope contract.
120
+
121
+ Patch versions (1.0.0 to 1.0.1) cover editorial clarifications. Minor versions (1.0.x to 1.1.0) cover additive changes. Major versions (1.x.y to 2.0.0) cover breaking changes; none are anticipated under v1's planning horizon.
122
+
123
+ The producer cutover for any minor version follows the event-envelope §11 pattern: producers continue emitting at the prior schema_version until the cutover date (publish date plus 14 days by default), then flip to the new schema_version. Consumers MAY rely on new fields from the cutover date.
124
+
125
+ ## 9. Producer responsibilities trace
126
+
127
+ The producer-side sites where these events fire (per `revenue/docs/dispatcher-emit-wiring.md`):
128
+
129
+ - `payment.received` from `revenue/src/modules/orders/service.ts:runApplyInvoicePayment` (path: `invoice_paid`).
130
+ - `payment.received` from `revenue/src/modules/orders/service.ts:runChargeOrder` (path: `active_charge`).
131
+ - `payment.failed` from the failure branches of the same two functions, when the provider reports failure or the upstream validation determines the writeback should not flip the order to PAID.
132
+ - `payment.received` from a future internal-credit-balance settlement path (`payment_method: credit_balance`); not yet built, lands when the orders module's full Postgres migration completes per `2026-05-09-revenue-q2-airtable-sunset-scoping` commitment 0.
133
+
134
+ This trace is the live producer-side reference; if a future revision adds an event-firing site outside the orders module (Pay Runs, scheduled retries, manual operator-initiated charges), this section updates as part of the same contract change.
135
+
136
+ ## 10. Action items
137
+
138
+ 1. [ ] Revenue: register `payment.received` and `payment.failed` in `coordination/contracts/event-types-registry.json` with the consumer mappings named in this contract. Owner: Revenue. Due: at v1.0.0 publish (this filing).
139
+ 2. [ ] Revenue: wire the producer-side dispatcher.publish calls at the three named sites in §9 inside Prisma transactions per ADR-0009, alongside the per-module Postgres migration in `2026-05-09-revenue-q2-airtable-sunset-scoping` commitment 1. Owner: Revenue. Due: 2026-06-22 per the Q2 directive.
140
+ 3. [ ] Sales: scaffold a subscriber for `payment.received` in the lead-pipeline reactivation flow once Phase 2 SDK ships (2026-06-22). Owner: Sales. Due: per Sales' own roadmap.
141
+ 4. [ ] Growth: scaffold a subscriber for `payment.received` and `payment.failed` for funnel-close attribution. Owner: Growth. Due: per Growth's own roadmap.
142
+ 5. [ ] Delivery: subscribe to `payment.received` if the awareness signal informs scheduling logic (optional; the credit-reservation-lock events are the primary Delivery feed). Owner: Delivery. Due: per Delivery's own roadmap.
143
+ 6. [ ] Platform: ack the registry edits and the contract publication. Owner: Platform. Due: per the response window on `2026-05-02-revenue-emit-wiring-registry-additions`.
144
+
145
+ ## 11. Trigger to revisit
146
+
147
+ Any of the following reopens this contract:
148
+
149
+ - A new payment provider lands (Quo, alternative card processor, alternative ACH provider). The provider-agnostic shape SHOULD absorb additive without bumping; if the new provider's behavior differs in ways the v1 enums do not capture (e.g. a new failure_code value), the change lands as a v1.x patch.
150
+ - The `credit_balance` payment method's internal-credit-balance settlement path lands and surfaces fields the v1 payload does not carry.
151
+ - A consumer surfaces a routing requirement that the v1 enum partitions do not satisfy. Phase-2 narrowing of the enums lands as v1.x patches per the additive-discipline.
152
+ - The funding-sub-state event family decision (collapse vs keep separate per `2026-05-02-revenue-emit-wiring-registry-additions`) introduces an overlap with payment-flow that warrants reorganization.
153
+ - A multi-tenant or multi-region scope expansion changes the envelope's `tenant_id` semantics in a way that affects the payload field set.
154
+
155
+ ## Change log
156
+
157
+ - **v1.0.0** (2026-05-02). Initial release. Two events (`payment.received`, `payment.failed`) with v1 payloads. Five-value `payment_method` enum (`card`, `ach`, `cash`, `check`, `credit_balance`). Two-value `payment_path` enum (`invoice_paid`, `active_charge`). Six-value `failure_code` enum (`card_declined`, `insufficient_funds`, `expired_card`, `network_error`, `provider_unavailable`, `other`). Producer-transactional-guarantee shape from ADR-0009 codified at §5. Sibling to refund-flow contract published the same day. Registered alongside the `2026-05-02-revenue-emit-wiring-registry-additions` memo's commitment 0.