@greatwait/contracts 0.1.0-alpha.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 (241) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +77 -0
  3. package/dist/authority.d.ts +217 -0
  4. package/dist/authority.js +261 -0
  5. package/dist/callback-ownership.d.ts +58 -0
  6. package/dist/callback-ownership.js +71 -0
  7. package/dist/callback-safety.d.ts +38 -0
  8. package/dist/callback-safety.js +403 -0
  9. package/dist/compatibility.d.ts +82 -0
  10. package/dist/compatibility.js +115 -0
  11. package/dist/delivery.d.ts +401 -0
  12. package/dist/delivery.js +311 -0
  13. package/dist/endpoints.d.ts +305 -0
  14. package/dist/endpoints.js +179 -0
  15. package/dist/events.d.ts +62 -0
  16. package/dist/events.js +38 -0
  17. package/dist/first-value-journey.d.ts +305 -0
  18. package/dist/first-value-journey.js +466 -0
  19. package/dist/forecast.d.ts +443 -0
  20. package/dist/forecast.js +729 -0
  21. package/dist/identifiers.d.ts +109 -0
  22. package/dist/identifiers.js +131 -0
  23. package/dist/identity.d.ts +196 -0
  24. package/dist/identity.js +182 -0
  25. package/dist/index.d.ts +27 -0
  26. package/dist/index.js +27 -0
  27. package/dist/mcp.d.ts +51 -0
  28. package/dist/mcp.js +56 -0
  29. package/dist/oauth.d.ts +796 -0
  30. package/dist/oauth.js +708 -0
  31. package/dist/operations.d.ts +3985 -0
  32. package/dist/operations.js +1837 -0
  33. package/dist/partition.d.ts +771 -0
  34. package/dist/partition.js +206 -0
  35. package/dist/policy.d.ts +93 -0
  36. package/dist/policy.js +180 -0
  37. package/dist/problems.d.ts +158 -0
  38. package/dist/problems.js +104 -0
  39. package/dist/protocol.d.ts +308 -0
  40. package/dist/protocol.js +515 -0
  41. package/dist/scheduling-records.d.ts +395 -0
  42. package/dist/scheduling-records.js +284 -0
  43. package/dist/scheduling.d.ts +1098 -0
  44. package/dist/scheduling.js +1116 -0
  45. package/dist/secrets.d.ts +383 -0
  46. package/dist/secrets.js +272 -0
  47. package/dist/signatures.d.ts +704 -0
  48. package/dist/signatures.js +867 -0
  49. package/dist/tenancy.d.ts +178 -0
  50. package/dist/tenancy.js +145 -0
  51. package/dist/test-sink.d.ts +986 -0
  52. package/dist/test-sink.js +755 -0
  53. package/dist/time.d.ts +45 -0
  54. package/dist/time.js +128 -0
  55. package/dist/usage.d.ts +121 -0
  56. package/dist/usage.js +200 -0
  57. package/dist/webhooks.d.ts +116 -0
  58. package/dist/webhooks.js +161 -0
  59. package/dist/workspace.d.ts +92 -0
  60. package/dist/workspace.js +59 -0
  61. package/generated/v1/callback-delivery-signature.json +467 -0
  62. package/generated/v1/callback-endpoint-verification.json +59 -0
  63. package/generated/v1/callback-receiver.json +241 -0
  64. package/generated/v1/examples/attempts.list.response.json +24 -0
  65. package/generated/v1/examples/attempts.read.response.json +20 -0
  66. package/generated/v1/examples/credentials.create.request.json +5 -0
  67. package/generated/v1/examples/credentials.create.response.json +10 -0
  68. package/generated/v1/examples/credentials.list.response.json +17 -0
  69. package/generated/v1/examples/credentials.revoke.response.json +11 -0
  70. package/generated/v1/examples/credentials.rotate.request.json +3 -0
  71. package/generated/v1/examples/credentials.rotate.response.json +22 -0
  72. package/generated/v1/examples/endpoints.create.request.json +3 -0
  73. package/generated/v1/examples/endpoints.create.response.json +16 -0
  74. package/generated/v1/examples/endpoints.disable.response.json +15 -0
  75. package/generated/v1/examples/endpoints.list.response.json +21 -0
  76. package/generated/v1/examples/endpoints.read.response.json +14 -0
  77. package/generated/v1/examples/endpoints.rotateSigningKey.request.json +3 -0
  78. package/generated/v1/examples/endpoints.rotateSigningKey.response.json +23 -0
  79. package/generated/v1/examples/endpoints.update.request.json +3 -0
  80. package/generated/v1/examples/endpoints.update.response.json +13 -0
  81. package/generated/v1/examples/endpoints.verify.response.json +15 -0
  82. package/generated/v1/examples/forecast.read.response.json +28 -0
  83. package/generated/v1/examples/forecast.windows.response.json +21 -0
  84. package/generated/v1/examples/grants.list.response.json +17 -0
  85. package/generated/v1/examples/grants.revoke.response.json +16 -0
  86. package/generated/v1/examples/meta.scaffold.response.json +5 -0
  87. package/generated/v1/examples/meta.version.response.json +5 -0
  88. package/generated/v1/examples/occurrences.list.response.json +17 -0
  89. package/generated/v1/examples/occurrences.read.response.json +15 -0
  90. package/generated/v1/examples/occurrences.redeliver.response.json +16 -0
  91. package/generated/v1/examples/projects.create.request.json +3 -0
  92. package/generated/v1/examples/projects.create.response.json +5 -0
  93. package/generated/v1/examples/projects.list.response.json +9 -0
  94. package/generated/v1/examples/schedules.archive.response.json +25 -0
  95. package/generated/v1/examples/schedules.cancel.response.json +24 -0
  96. package/generated/v1/examples/schedules.create.request.json +9 -0
  97. package/generated/v1/examples/schedules.create.response.json +25 -0
  98. package/generated/v1/examples/schedules.list.response.json +23 -0
  99. package/generated/v1/examples/schedules.pause.response.json +23 -0
  100. package/generated/v1/examples/schedules.read.response.json +25 -0
  101. package/generated/v1/examples/schedules.replace.request.json +8 -0
  102. package/generated/v1/examples/schedules.replace.response.json +25 -0
  103. package/generated/v1/examples/schedules.resume.response.json +23 -0
  104. package/generated/v1/examples/sinkdeliveries.list.response.json +19 -0
  105. package/generated/v1/examples/sinkdeliveries.read.response.json +16 -0
  106. package/generated/v1/examples/testsinks.configure.request.json +3 -0
  107. package/generated/v1/examples/testsinks.configure.response.json +13 -0
  108. package/generated/v1/examples/testsinks.create.request.json +8 -0
  109. package/generated/v1/examples/testsinks.create.response.json +13 -0
  110. package/generated/v1/examples/testsinks.delete.response.json +13 -0
  111. package/generated/v1/examples/testsinks.list.response.json +20 -0
  112. package/generated/v1/examples/testsinks.read.response.json +14 -0
  113. package/generated/v1/examples/usage.read.response.json +20 -0
  114. package/generated/v1/first-value-journey.json +436 -0
  115. package/generated/v1/manifest.json +1037 -0
  116. package/generated/v1/openapi.json +23665 -0
  117. package/generated/v1/policy-matrix.json +305 -0
  118. package/generated/v1/schemas/attempts.list.query.json +16 -0
  119. package/generated/v1/schemas/attempts.list.response.json +175 -0
  120. package/generated/v1/schemas/attempts.read.response.json +168 -0
  121. package/generated/v1/schemas/credentials.create.request.json +39 -0
  122. package/generated/v1/schemas/credentials.create.response.json +88 -0
  123. package/generated/v1/schemas/credentials.list.query.json +16 -0
  124. package/generated/v1/schemas/credentials.list.response.json +124 -0
  125. package/generated/v1/schemas/credentials.revoke.response.json +84 -0
  126. package/generated/v1/schemas/credentials.rotate.request.json +11 -0
  127. package/generated/v1/schemas/credentials.rotate.response.json +171 -0
  128. package/generated/v1/schemas/endpoints.create.request.json +12 -0
  129. package/generated/v1/schemas/endpoints.create.response.json +115 -0
  130. package/generated/v1/schemas/endpoints.disable.response-headers.json +11 -0
  131. package/generated/v1/schemas/endpoints.disable.response.json +100 -0
  132. package/generated/v1/schemas/endpoints.list.query.json +16 -0
  133. package/generated/v1/schemas/endpoints.list.response.json +146 -0
  134. package/generated/v1/schemas/endpoints.read.response-headers.json +11 -0
  135. package/generated/v1/schemas/endpoints.read.response.json +100 -0
  136. package/generated/v1/schemas/endpoints.rotateSigningKey.request-headers.json +10 -0
  137. package/generated/v1/schemas/endpoints.rotateSigningKey.request.json +11 -0
  138. package/generated/v1/schemas/endpoints.rotateSigningKey.response-headers.json +11 -0
  139. package/generated/v1/schemas/endpoints.rotateSigningKey.response.json +118 -0
  140. package/generated/v1/schemas/endpoints.update.request-headers.json +10 -0
  141. package/generated/v1/schemas/endpoints.update.request.json +12 -0
  142. package/generated/v1/schemas/endpoints.update.response-headers.json +11 -0
  143. package/generated/v1/schemas/endpoints.update.response.json +100 -0
  144. package/generated/v1/schemas/endpoints.verify.request-headers.json +10 -0
  145. package/generated/v1/schemas/endpoints.verify.response-headers.json +11 -0
  146. package/generated/v1/schemas/endpoints.verify.response.json +100 -0
  147. package/generated/v1/schemas/forecast.read.query.json +26 -0
  148. package/generated/v1/schemas/forecast.read.response.json +100 -0
  149. package/generated/v1/schemas/forecast.windows.query.json +26 -0
  150. package/generated/v1/schemas/forecast.windows.response.json +94 -0
  151. package/generated/v1/schemas/grants.list.response.json +98 -0
  152. package/generated/v1/schemas/grants.revoke.response.json +95 -0
  153. package/generated/v1/schemas/meta.scaffold.response.json +36 -0
  154. package/generated/v1/schemas/meta.version.response.json +19 -0
  155. package/generated/v1/schemas/occurrences.list.query.json +16 -0
  156. package/generated/v1/schemas/occurrences.list.response.json +83 -0
  157. package/generated/v1/schemas/occurrences.read.response.json +76 -0
  158. package/generated/v1/schemas/occurrences.redeliver.request-headers.json +13 -0
  159. package/generated/v1/schemas/occurrences.redeliver.response-headers.json +10 -0
  160. package/generated/v1/schemas/occurrences.redeliver.response.json +168 -0
  161. package/generated/v1/schemas/projects.create.request.json +12 -0
  162. package/generated/v1/schemas/projects.create.response.json +25 -0
  163. package/generated/v1/schemas/projects.list.response.json +35 -0
  164. package/generated/v1/schemas/schedules.archive.request-headers.json +10 -0
  165. package/generated/v1/schemas/schedules.archive.response-headers.json +11 -0
  166. package/generated/v1/schemas/schedules.archive.response.json +189 -0
  167. package/generated/v1/schemas/schedules.cancel.request-headers.json +10 -0
  168. package/generated/v1/schemas/schedules.cancel.response-headers.json +11 -0
  169. package/generated/v1/schemas/schedules.cancel.response.json +189 -0
  170. package/generated/v1/schemas/schedules.create.request-headers.json +12 -0
  171. package/generated/v1/schemas/schedules.create.request.json +99 -0
  172. package/generated/v1/schemas/schedules.create.response-headers.json +15 -0
  173. package/generated/v1/schemas/schedules.create.response.json +189 -0
  174. package/generated/v1/schemas/schedules.list.query.json +16 -0
  175. package/generated/v1/schemas/schedules.list.response.json +170 -0
  176. package/generated/v1/schemas/schedules.pause.request-headers.json +10 -0
  177. package/generated/v1/schemas/schedules.pause.response-headers.json +11 -0
  178. package/generated/v1/schemas/schedules.pause.response.json +189 -0
  179. package/generated/v1/schemas/schedules.read.response-headers.json +11 -0
  180. package/generated/v1/schemas/schedules.read.response.json +189 -0
  181. package/generated/v1/schemas/schedules.replace.request-headers.json +10 -0
  182. package/generated/v1/schemas/schedules.replace.request.json +99 -0
  183. package/generated/v1/schemas/schedules.replace.response-headers.json +11 -0
  184. package/generated/v1/schemas/schedules.replace.response.json +189 -0
  185. package/generated/v1/schemas/schedules.resume.request-headers.json +10 -0
  186. package/generated/v1/schemas/schedules.resume.response-headers.json +11 -0
  187. package/generated/v1/schemas/schedules.resume.response.json +189 -0
  188. package/generated/v1/schemas/sinkdeliveries.list.query.json +16 -0
  189. package/generated/v1/schemas/sinkdeliveries.list.response.json +126 -0
  190. package/generated/v1/schemas/sinkdeliveries.read.response.json +98 -0
  191. package/generated/v1/schemas/testsinks.configure.request.json +66 -0
  192. package/generated/v1/schemas/testsinks.configure.response.json +115 -0
  193. package/generated/v1/schemas/testsinks.create.request.json +76 -0
  194. package/generated/v1/schemas/testsinks.create.response.json +115 -0
  195. package/generated/v1/schemas/testsinks.delete.response.json +115 -0
  196. package/generated/v1/schemas/testsinks.list.query.json +16 -0
  197. package/generated/v1/schemas/testsinks.list.response.json +161 -0
  198. package/generated/v1/schemas/testsinks.read.response.json +115 -0
  199. package/generated/v1/schemas/usage.read.query.json +15 -0
  200. package/generated/v1/schemas/usage.read.response.json +71 -0
  201. package/generated/v1/snippets/attempts.list.json +5 -0
  202. package/generated/v1/snippets/attempts.read.json +5 -0
  203. package/generated/v1/snippets/credentials.create.json +5 -0
  204. package/generated/v1/snippets/credentials.list.json +5 -0
  205. package/generated/v1/snippets/credentials.revoke.json +5 -0
  206. package/generated/v1/snippets/credentials.rotate.json +5 -0
  207. package/generated/v1/snippets/endpoints.create.json +5 -0
  208. package/generated/v1/snippets/endpoints.disable.json +5 -0
  209. package/generated/v1/snippets/endpoints.list.json +5 -0
  210. package/generated/v1/snippets/endpoints.read.json +5 -0
  211. package/generated/v1/snippets/endpoints.rotateSigningKey.json +5 -0
  212. package/generated/v1/snippets/endpoints.update.json +5 -0
  213. package/generated/v1/snippets/endpoints.verify.json +5 -0
  214. package/generated/v1/snippets/forecast.read.json +5 -0
  215. package/generated/v1/snippets/forecast.windows.json +5 -0
  216. package/generated/v1/snippets/grants.list.json +5 -0
  217. package/generated/v1/snippets/grants.revoke.json +5 -0
  218. package/generated/v1/snippets/meta.scaffold.json +5 -0
  219. package/generated/v1/snippets/meta.version.json +5 -0
  220. package/generated/v1/snippets/occurrences.list.json +5 -0
  221. package/generated/v1/snippets/occurrences.read.json +5 -0
  222. package/generated/v1/snippets/occurrences.redeliver.json +5 -0
  223. package/generated/v1/snippets/projects.create.json +5 -0
  224. package/generated/v1/snippets/projects.list.json +5 -0
  225. package/generated/v1/snippets/schedules.archive.json +5 -0
  226. package/generated/v1/snippets/schedules.cancel.json +5 -0
  227. package/generated/v1/snippets/schedules.create.json +5 -0
  228. package/generated/v1/snippets/schedules.list.json +5 -0
  229. package/generated/v1/snippets/schedules.pause.json +5 -0
  230. package/generated/v1/snippets/schedules.read.json +5 -0
  231. package/generated/v1/snippets/schedules.replace.json +5 -0
  232. package/generated/v1/snippets/schedules.resume.json +5 -0
  233. package/generated/v1/snippets/sinkdeliveries.list.json +5 -0
  234. package/generated/v1/snippets/sinkdeliveries.read.json +5 -0
  235. package/generated/v1/snippets/testsinks.configure.json +5 -0
  236. package/generated/v1/snippets/testsinks.create.json +5 -0
  237. package/generated/v1/snippets/testsinks.delete.json +5 -0
  238. package/generated/v1/snippets/testsinks.list.json +5 -0
  239. package/generated/v1/snippets/testsinks.read.json +5 -0
  240. package/generated/v1/snippets/usage.read.json +5 -0
  241. package/package.json +153 -0
@@ -0,0 +1,704 @@
1
+ import { z } from "zod";
2
+ import { type OccurrenceId, type ScheduleId } from "./identifiers.js";
3
+ /**
4
+ * Callback authenticity.
5
+ *
6
+ * The signature covers the timestamp, the delivery id, and the exact raw
7
+ * request-body bytes. A receiver verifies before parsing, in constant time.
8
+ *
9
+ * This is the most consequential break from the legacy scheduler, which signed
10
+ * `scheduleId.owner.tenant.timestampMs` and never touched the body: a signature
11
+ * that does not cover the body authenticates only the envelope, so an attacker
12
+ * who can replay a captured header set may substitute any payload. W6-03
13
+ * migrates the autoscaler onto this contract through a separate v2 endpoint so
14
+ * the old and new verifiers never weaken each other.
15
+ *
16
+ * The `Callback-*` header namespace is intentionally independent of the display
17
+ * brand. Renaming the product must not force receivers to change verification
18
+ * code.
19
+ */
20
+ export declare const CALLBACK_HEADERS: {
21
+ readonly deliveryId: "Callback-Delivery-Id";
22
+ readonly keyId: "Callback-Key-Id";
23
+ readonly occurrenceId: "Callback-Occurrence-Id";
24
+ readonly signature: "Callback-Signature";
25
+ readonly timestamp: "Callback-Timestamp";
26
+ };
27
+ /** Only `v1` exists. A new scheme is added alongside, never in place of. */
28
+ export declare const SIGNATURE_SCHEME: "v1";
29
+ /** How far a receiver's clock may differ before a signature is stale. */
30
+ export declare const SIGNATURE_TOLERANCE_SECONDS = 300;
31
+ /** Version of the complete delivery-authentication protocol artifact. */
32
+ export declare const CALLBACK_DELIVERY_PROTOCOL_VERSION = "2026-08-23";
33
+ /** The media type sent on every occurrence delivery. */
34
+ export declare const CALLBACK_CONTENT_TYPE = "application/json";
35
+ export declare const signatureHeaderSchema: z.ZodString;
36
+ /**
37
+ * The exact bytes that are signed: `<timestamp>.<delivery-id>.` followed by the
38
+ * raw body, with no re-serialization anywhere in the path. Re-encoding JSON
39
+ * before signing or verifying is the classic way to break this.
40
+ *
41
+ * Both prefix fields are validated here rather than trusted, and that is a
42
+ * security property, not tidiness. The delimiter `.` also occurs inside every
43
+ * contract timestamp, so without validation the encoding is not injective:
44
+ * `("2026-07-28T15:00:00", "000Z.dlv_…", body)` produces the same bytes as
45
+ * `("2026-07-28T15:00:00.000Z", "dlv_…", body)`, and an attacker holding one
46
+ * legitimate signature could move bytes across the delimiter — shortening the
47
+ * body and lengthening the delivery-id header — and still verify.
48
+ *
49
+ * Validating makes both fields fixed-width, so the prefix is a constant 56
50
+ * bytes and the encoding is injective. A verifier that computes the MAC before
51
+ * validating its headers is the natural implementation and would be
52
+ * vulnerable; making that impossible here means no receiver has to know.
53
+ *
54
+ * **Throws** on a malformed timestamp or delivery id. That is right on the
55
+ * signing side — refusing to sign something we cannot encode unambiguously —
56
+ * but a receiver verifying attacker-controlled headers wants a boolean, not an
57
+ * exception: use `trySigningInput`, or an uncaught throw turns a
58
+ * malformed-header probe into a 500.
59
+ */
60
+ export declare function signingInput(timestamp: string, deliveryId: string, rawBody: Uint8Array): Uint8Array;
61
+ /**
62
+ * `signingInput` for untrusted input: returns `undefined` instead of throwing
63
+ * when the prefix fields are malformed, which is what a receiver verifying
64
+ * headers off the wire needs.
65
+ */
66
+ export declare function trySigningInput(timestamp: string, deliveryId: string, rawBody: Uint8Array): Uint8Array | undefined;
67
+ export declare function formatSignature(hexMac: string): string;
68
+ /**
69
+ * Whether a signed timestamp is close enough to now. Replay protection beyond
70
+ * this window is the receiver's, keyed on the delivery id.
71
+ */
72
+ export declare function isTimestampFresh(timestamp: string, nowMs: number, toleranceSeconds?: number): boolean;
73
+ /**
74
+ * Why this delivery exists.
75
+ *
76
+ * `automatic` is everything the system decided for itself: a first attempt, and
77
+ * every retry after an ambiguous or failed one. Nobody asked for it, it may
78
+ * duplicate work already done, and a receiver must therefore treat it as at
79
+ * most one execution of the occurrence.
80
+ *
81
+ * `redelivery` is a person pressing the button, scoped by
82
+ * `occurrences:redeliver` and named against the retained attempt they chose to
83
+ * follow. It is a *request for the work to happen again*, and a receiver that
84
+ * suppresses it as a duplicate is answering a deliberate instruction with
85
+ * silence.
86
+ *
87
+ * The two were indistinguishable on the wire until this field existed, so the
88
+ * only rule safe against the first was applied to both. That is the defect
89
+ * W3-08 exists to remove, and the reason the field is inside the signed bytes
90
+ * rather than in a header: a receiver that would re-run work on the strength of
91
+ * an unauthenticated hint has handed anyone who can reach its endpoint a
92
+ * duplicate-execution button.
93
+ */
94
+ export declare const callbackTriggerSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
95
+ kind: z.ZodLiteral<"automatic">;
96
+ }, z.core.$strip>, z.ZodObject<{
97
+ kind: z.ZodLiteral<"redelivery">;
98
+ sourceAttemptId: z.ZodString;
99
+ }, z.core.$strip>], "kind">;
100
+ export type CallbackTrigger = z.infer<typeof callbackTriggerSchema>;
101
+ /**
102
+ * The envelope version this service emits.
103
+ *
104
+ * Dated and independent of the contract version. It moved on 2026-08-08 because
105
+ * the envelope gained `trigger`, and again on 2026-08-23 because `completion`
106
+ * stopped being optional: every delivery now carries a completion URL, a
107
+ * per-dispatch credential and an absolute deadline, whatever the schedule asked
108
+ * for. `assess` classifies both moves as `breaking` — see
109
+ * `CALLBACK_ENVELOPE_VERSION_CHANGE`.
110
+ *
111
+ * **A required field is why this is a version rather than an addition.** Under
112
+ * `2026-08-08` an absent `completion` means "this schedule did not ask for
113
+ * one"; under `2026-08-23` it means the envelope is malformed. A receiver that
114
+ * could not tell those apart would have to treat "no completion material" as
115
+ * both a normal delivery and a broken one.
116
+ */
117
+ export declare const CALLBACK_ENVELOPE_VERSION: "2026-08-23";
118
+ /** The first published envelope version, which this service no longer emits. */
119
+ export declare const CALLBACK_ENVELOPE_VERSION_2026_07_01: "2026-07-01";
120
+ /**
121
+ * The second published version, which this service no longer emits.
122
+ *
123
+ * Still accepted inbound, and its known-answer vectors are still published:
124
+ * a receiver deployed before the sender moved keeps verifying deliveries that
125
+ * were signed under it.
126
+ */
127
+ export declare const CALLBACK_ENVELOPE_VERSION_2026_08_08: "2026-08-08";
128
+ /**
129
+ * Every envelope version a receiver may be handed, oldest first.
130
+ *
131
+ * All are accepted on the way in and only the newest is sent, which is what
132
+ * makes a receiver deployable ahead of the sender. A receiver upgraded first
133
+ * keeps verifying the deliveries already in flight; one upgraded afterwards
134
+ * answers `invalid_envelope` to everything, because a version literal it has
135
+ * never heard of is exactly the case the field exists to catch.
136
+ */
137
+ export declare const CALLBACK_ENVELOPE_VERSIONS: readonly ["2026-07-01", "2026-08-08", "2026-08-23"];
138
+ /**
139
+ * Where a `callback` receiver reports that it finished, and with what.
140
+ *
141
+ * **Present only on a delivery whose schedule asked for `completion:
142
+ * "callback"`**, which is what makes adding it to an existing envelope version
143
+ * safe rather than a break. A receiver sees this field for the first time on a
144
+ * delivery its own customer deliberately reconfigured; no receiver that has not
145
+ * opted in can be handed one. That is a different situation from `trigger`,
146
+ * which appeared on *every* delivery and moved the version to `2026-08-08` in
147
+ * consequence.
148
+ *
149
+ * The token authenticates the report and nothing else. It is minted per dispatch
150
+ * and names one attempt at one dispatch epoch, so a report replayed against a
151
+ * later attempt is refused by the same fence that refuses a late executor
152
+ * result.
153
+ */
154
+ export declare const callbackCompletionSchema: z.ZodObject<{
155
+ token: z.ZodString;
156
+ url: z.ZodURL;
157
+ }, z.core.$strip>;
158
+ export type CallbackCompletion = z.infer<typeof callbackCompletionSchema>;
159
+ /**
160
+ * The completion material every `2026-08-23` delivery carries.
161
+ *
162
+ * The same URL and credential, plus the one thing a receiver could not
163
+ * previously know: **when Greatwait stops waiting.** Without it a receiver
164
+ * deciding whether to accept the work has to infer the deadline from the
165
+ * schedule's configured duration, which it cannot see, and a receiver that
166
+ * reports one second late looks identical to one that never reported at all.
167
+ *
168
+ * `deadline` is the schedule's own allowance measured from this attempt, in
169
+ * the wire's one timestamp representation. It is the maximum: a receiver may
170
+ * ask for an earlier instant in its acknowledgement and may not ask for a
171
+ * later one.
172
+ */
173
+ export declare const callbackDeferredCompletionSchema: z.ZodObject<{
174
+ deadline: z.ZodString;
175
+ token: z.ZodString;
176
+ url: z.ZodURL;
177
+ }, z.core.$strip>;
178
+ export type CallbackDeferredCompletion = z.infer<typeof callbackDeferredCompletionSchema>;
179
+ /**
180
+ * The report body, whose vocabulary lives in the Zod-free `delivery` module so
181
+ * the scheduler's completion route can read it without importing Zod.
182
+ */
183
+ export declare const callbackCompletionReportSchema: z.ZodObject<{
184
+ outcome: z.ZodDefault<z.ZodEnum<{
185
+ succeeded: "succeeded";
186
+ failed: "failed";
187
+ }>>;
188
+ }, z.core.$strip>;
189
+ export type CallbackCompletionReport = z.infer<typeof callbackCompletionReportSchema>;
190
+ /**
191
+ * The versioned JSON envelope delivered to a customer endpoint.
192
+ *
193
+ * A union rather than one object with an optional field, and the difference is
194
+ * the whole point of a version: under `2026-07-01` a missing `trigger` means
195
+ * "this sender cannot tell you", and under `2026-08-08` it is a malformed
196
+ * envelope. Collapsing the two would leave a receiver unable to know whether
197
+ * silence about a redelivery is an answer or an absence.
198
+ */
199
+ export declare const callbackEnvelopeSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
200
+ version: z.ZodLiteral<"2026-07-01">;
201
+ attempt: z.ZodNumber;
202
+ data: z.ZodUnknown;
203
+ occurrenceId: z.ZodString;
204
+ plannedFor: z.ZodString;
205
+ scheduleId: z.ZodString;
206
+ type: z.ZodLiteral<"schedule.occurrence">;
207
+ }, z.core.$strip>, z.ZodObject<{
208
+ completion: z.ZodOptional<z.ZodObject<{
209
+ token: z.ZodString;
210
+ url: z.ZodURL;
211
+ }, z.core.$strip>>;
212
+ trigger: z.ZodDiscriminatedUnion<[z.ZodObject<{
213
+ kind: z.ZodLiteral<"automatic">;
214
+ }, z.core.$strip>, z.ZodObject<{
215
+ kind: z.ZodLiteral<"redelivery">;
216
+ sourceAttemptId: z.ZodString;
217
+ }, z.core.$strip>], "kind">;
218
+ version: z.ZodLiteral<"2026-08-08">;
219
+ attempt: z.ZodNumber;
220
+ data: z.ZodUnknown;
221
+ occurrenceId: z.ZodString;
222
+ plannedFor: z.ZodString;
223
+ scheduleId: z.ZodString;
224
+ type: z.ZodLiteral<"schedule.occurrence">;
225
+ }, z.core.$strip>, z.ZodObject<{
226
+ completion: z.ZodObject<{
227
+ deadline: z.ZodString;
228
+ token: z.ZodString;
229
+ url: z.ZodURL;
230
+ }, z.core.$strip>;
231
+ trigger: z.ZodDiscriminatedUnion<[z.ZodObject<{
232
+ kind: z.ZodLiteral<"automatic">;
233
+ }, z.core.$strip>, z.ZodObject<{
234
+ kind: z.ZodLiteral<"redelivery">;
235
+ sourceAttemptId: z.ZodString;
236
+ }, z.core.$strip>], "kind">;
237
+ version: z.ZodLiteral<"2026-08-23">;
238
+ attempt: z.ZodNumber;
239
+ data: z.ZodUnknown;
240
+ occurrenceId: z.ZodString;
241
+ plannedFor: z.ZodString;
242
+ scheduleId: z.ZodString;
243
+ type: z.ZodLiteral<"schedule.occurrence">;
244
+ }, z.core.$strip>], "version">;
245
+ export type CallbackEnvelope = z.infer<typeof callbackEnvelopeSchema>;
246
+ /**
247
+ * What caused a delivery, for an envelope of any published version.
248
+ *
249
+ * A `2026-07-01` envelope carries no trigger and reads as `automatic`, which is
250
+ * the conservative answer rather than a neutral one: it keeps duplicate
251
+ * suppression in force for a sender that cannot say a redelivery was ordered.
252
+ * Every receiver-side decision goes through this function so that default lives
253
+ * in one place instead of being re-decided at each call site.
254
+ */
255
+ export declare function callbackEnvelopeTrigger(envelope: CallbackEnvelope): CallbackTrigger;
256
+ /**
257
+ * The unit of work a receiver performs at most once.
258
+ *
259
+ * Not the occurrence: the occurrence *and what asked for it*. Every automatic
260
+ * delivery of one occurrence is one unit, so a retry can never duplicate work.
261
+ * Each ordered redelivery is its own unit, numbered by `attempt` — the
262
+ * occurrence's own counter, which only ever goes up and which every copy of one
263
+ * redelivery shares.
264
+ *
265
+ * **The number is what makes a repeat recognisable with constant memory, and
266
+ * that is the whole reason it is the identity.** A receiver keeps the highest
267
+ * redelivery it has finished and refuses anything at or below it, so a copy
268
+ * delayed in a proxy, a queue or an abandoned connection is still recognised
269
+ * however long it takes to arrive and however many redeliveries have completed
270
+ * since. External review on 2026-08-08 showed why the alternative fails: keyed
271
+ * on *which attempt the person followed*, a receiver remembering only the last
272
+ * one finished cannot tell a stale copy of an older instruction from a new one,
273
+ * and runs a customer's work twice. Remembering every instruction instead would
274
+ * hold the promise, at the price of a table that grows forever in every
275
+ * customer's receiver, in every language.
276
+ *
277
+ * `sourceAttemptId` stays on the wire because it says which attempt a person
278
+ * chose to follow, which is worth showing them. It is no longer what decides
279
+ * whether the work runs.
280
+ *
281
+ * Published here, and used by both the shipped middleware and the test sink,
282
+ * because a receiver and the reference receiver computing this differently is
283
+ * indistinguishable from a receiver with an idempotency bug.
284
+ */
285
+ export type CallbackRun = {
286
+ readonly kind: "automatic";
287
+ } | {
288
+ readonly kind: "redelivery";
289
+ readonly ordinal: number;
290
+ };
291
+ export declare function callbackRun(envelope: CallbackEnvelope): CallbackRun;
292
+ /**
293
+ * Serialize one callback envelope exactly once in the frozen wire order.
294
+ *
295
+ * The returned bytes are both the bytes HMAC signs and the bytes `fetch`
296
+ * sends. `data` is checked as JSON before stringification so values such as
297
+ * `undefined`, `NaN`, `bigint`, a `Date`, or a cycle cannot be silently omitted
298
+ * or rewritten into a body different from the caller's input.
299
+ */
300
+ export declare function serializeCallbackEnvelope(envelope: CallbackEnvelope): Uint8Array;
301
+ /**
302
+ * Read the two leading envelope identifiers without parsing attacker-controlled
303
+ * JSON. This lets receiver middleware reject a mutated occurrence header after
304
+ * authenticating the exact bytes but before `JSON.parse` or an application
305
+ * handler sees them.
306
+ */
307
+ export declare function extractCanonicalEnvelopeIds(rawBody: Uint8Array): {
308
+ readonly occurrenceId: OccurrenceId;
309
+ readonly scheduleId: ScheduleId;
310
+ } | undefined;
311
+ export declare const callbackHeadersSchema: z.ZodObject<{
312
+ "Callback-Delivery-Id": z.ZodString;
313
+ "Callback-Key-Id": z.ZodString;
314
+ "Callback-Occurrence-Id": z.ZodString;
315
+ "Callback-Signature": z.ZodString;
316
+ "Callback-Timestamp": z.ZodString;
317
+ }, z.core.$strip>;
318
+ /**
319
+ * Language-neutral known-answer contract for one complete delivery request.
320
+ * The secret is synthetic and deliberately not shaped like a credential.
321
+ */
322
+ export declare const CALLBACK_DELIVERY_PROTOCOL: {
323
+ readonly envelope: {
324
+ /**
325
+ * The frozen wire order, and `completion` sits inside it.
326
+ *
327
+ * **This list is what a receiver outside JavaScript implements against**,
328
+ * so a field that reached the wire without reaching this array would make
329
+ * every callback delivery fail signature verification for exactly the
330
+ * integrators who cannot read the serializer. From `2026-08-23` every
331
+ * delivery carries `completion`; under `2026-08-08` it was optional, and
332
+ * the order was fixed anyway, because a position decided at emit time is a
333
+ * position nobody else can reproduce.
334
+ */
335
+ readonly propertyOrder: readonly ["version", "type", "scheduleId", "occurrenceId", "plannedFor", "attempt", "completion", "trigger", "data"];
336
+ /**
337
+ * The order **inside** `completion`, which is frozen for the same reason.
338
+ *
339
+ * `deadline` is appended rather than inserted, so a receiver that already
340
+ * reconstructs the `2026-08-08` object adds a key instead of re-deriving
341
+ * an order. Under `2026-08-08` the same object is the first two entries
342
+ * and nothing else.
343
+ */
344
+ readonly completionPropertyOrder: readonly ["token", "url", "deadline"];
345
+ /**
346
+ * The order under `2026-08-08`, whose `completion` is optional and carries
347
+ * no `deadline`. The top-level list is the same; what differs is inside.
348
+ */
349
+ readonly propertyOrder_2026_08_08: readonly ["version", "type", "scheduleId", "occurrenceId", "plannedFor", "attempt", "completion", "trigger", "data"];
350
+ /** The order under `2026-07-01`, which carries no trigger. */
351
+ readonly propertyOrder_2026_07_01: readonly ["version", "type", "scheduleId", "occurrenceId", "plannedFor", "attempt", "data"];
352
+ readonly serialization: "compact UTF-8 JSON with the stated top-level property order";
353
+ readonly trigger: {
354
+ readonly automatic: "A first attempt or a retry the system chose. Nobody asked for it, it may repeat work already done, and a receiver must run the occurrence at most once.";
355
+ readonly default: "An envelope with no trigger field is automatic. Only 2026-07-01 can omit it.";
356
+ readonly propertyOrder: readonly ["kind", "sourceAttemptId"];
357
+ readonly ordinal: "attempt. It only ever increases within an occurrence and every copy of one redelivery carries the same value, so a receiver recognises a repeat by keeping the highest it has run to completion rather than by keeping them all.";
358
+ readonly redelivery: "A person ordered this delivery under the occurrences:redeliver scope. sourceAttemptId names the retained attempt they chose to follow and is there to show them, not to decide whether the work runs — attempt is what decides that. It is a request for the work to happen again, and a receiver that suppresses it as a duplicate answers a deliberate instruction with silence.";
359
+ readonly requirement: "run the handler again for a redelivery whose attempt is above the highest this receiver has run to completion for the occurrence, keep suppressing every automatic delivery of a completed occurrence, and suppress every redelivery at or below that high-water mark";
360
+ };
361
+ readonly version: "2026-08-23";
362
+ /**
363
+ * Every version a receiver may be handed. A sender emits only the last;
364
+ * accepting the whole list is what lets a receiver deploy first.
365
+ */
366
+ readonly versions: readonly ["2026-07-01", "2026-08-08", "2026-08-23"];
367
+ };
368
+ readonly headers: {
369
+ readonly contentType: "application/json";
370
+ readonly required: readonly ["Content-Type", "Callback-Delivery-Id", "Callback-Occurrence-Id", "Callback-Timestamp", "Callback-Key-Id", "Callback-Signature"];
371
+ };
372
+ readonly replay: {
373
+ readonly identity: "Callback-Delivery-Id";
374
+ readonly requirement: "atomically reject a previously accepted delivery id before parsing";
375
+ };
376
+ readonly signature: {
377
+ readonly algorithm: "HMAC-SHA-256";
378
+ readonly format: "v1=<64 lowercase hexadecimal characters>";
379
+ readonly input: "ASCII <Callback-Timestamp>.<Callback-Delivery-Id>. followed by the exact raw request body bytes";
380
+ readonly scheme: "v1";
381
+ readonly timestampToleranceSeconds: 300;
382
+ };
383
+ /**
384
+ * The retired `2026-07-01` envelope, kept as a known answer.
385
+ *
386
+ * A receiver is still held to verifying it, because a sender mid-rollout may
387
+ * have signed it before the version moved. Its MAC is the one published on
388
+ * 2026-07-28 and is deliberately not recomputed.
389
+ */
390
+ readonly legacyTestVector: {
391
+ readonly envelope: {
392
+ readonly attempt: 1;
393
+ readonly data: {};
394
+ readonly occurrenceId: "occ_0123456789abcdefghjkmnpqrs";
395
+ readonly plannedFor: "2026-07-28T15:00:00.000Z";
396
+ readonly scheduleId: "sch_0123456789abcdefghjkmnpqrs";
397
+ readonly type: "schedule.occurrence";
398
+ readonly version: "2026-07-01";
399
+ };
400
+ readonly exactBodyBase64: "eyJ2ZXJzaW9uIjoiMjAyNi0wNy0wMSIsInR5cGUiOiJzY2hlZHVsZS5vY2N1cnJlbmNlIiwic2NoZWR1bGVJZCI6InNjaF8wMTIzNDU2Nzg5YWJjZGVmZ2hqa21ucHFycyIsIm9jY3VycmVuY2VJZCI6Im9jY18wMTIzNDU2Nzg5YWJjZGVmZ2hqa21ucHFycyIsInBsYW5uZWRGb3IiOiIyMDI2LTA3LTI4VDE1OjAwOjAwLjAwMFoiLCJhdHRlbXB0IjoxLCJkYXRhIjp7fX0=";
401
+ readonly exactBodyUtf8: "{\"version\":\"2026-07-01\",\"type\":\"schedule.occurrence\",\"scheduleId\":\"sch_0123456789abcdefghjkmnpqrs\",\"occurrenceId\":\"occ_0123456789abcdefghjkmnpqrs\",\"plannedFor\":\"2026-07-28T15:00:00.000Z\",\"attempt\":1,\"data\":{}}";
402
+ readonly headers: {
403
+ readonly "Callback-Delivery-Id": "dlv_0123456789abcdefghjkmnpqrs";
404
+ readonly "Callback-Key-Id": "csk_0123456789abcdefghjkmnpqrs";
405
+ readonly "Callback-Occurrence-Id": "occ_0123456789abcdefghjkmnpqrs";
406
+ readonly "Callback-Signature": "v1=11135fdfc0bc5d49ebcc9d3a0d8df1116179097cefe961c6eca281339e5cfab3";
407
+ readonly "Callback-Timestamp": "2026-07-28T15:00:00.000Z";
408
+ readonly "Content-Type": "application/json";
409
+ };
410
+ readonly secret: "greatwait-signature-vector-key-not-a-credential";
411
+ readonly signingInputBase64: "MjAyNi0wNy0yOFQxNTowMDowMC4wMDBaLmRsdl8wMTIzNDU2Nzg5YWJjZGVmZ2hqa21ucHFycy57InZlcnNpb24iOiIyMDI2LTA3LTAxIiwidHlwZSI6InNjaGVkdWxlLm9jY3VycmVuY2UiLCJzY2hlZHVsZUlkIjoic2NoXzAxMjM0NTY3ODlhYmNkZWZnaGprbW5wcXJzIiwib2NjdXJyZW5jZUlkIjoib2NjXzAxMjM0NTY3ODlhYmNkZWZnaGprbW5wcXJzIiwicGxhbm5lZEZvciI6IjIwMjYtMDctMjhUMTU6MDA6MDAuMDAwWiIsImF0dGVtcHQiOjEsImRhdGEiOnt9fQ==";
412
+ readonly signingInputUtf8: "2026-07-28T15:00:00.000Z.dlv_0123456789abcdefghjkmnpqrs.{\"version\":\"2026-07-01\",\"type\":\"schedule.occurrence\",\"scheduleId\":\"sch_0123456789abcdefghjkmnpqrs\",\"occurrenceId\":\"occ_0123456789abcdefghjkmnpqrs\",\"plannedFor\":\"2026-07-28T15:00:00.000Z\",\"attempt\":1,\"data\":{}}";
413
+ };
414
+ /**
415
+ * The retired `2026-08-08` envelope **without** `completion`, kept as a known
416
+ * answer.
417
+ *
418
+ * It is the vector `testVector` was until 2026-08-23, moved here rather than
419
+ * recomputed. Under `2026-08-08` an absent `completion` was the ordinary
420
+ * case, so this is the shape most deliveries signed under that version had,
421
+ * and a receiver still verifying them needs its published answer to stay put.
422
+ */
423
+ readonly triggerTestVector: {
424
+ readonly envelope: {
425
+ readonly attempt: 1;
426
+ readonly data: {};
427
+ readonly occurrenceId: "occ_0123456789abcdefghjkmnpqrs";
428
+ readonly plannedFor: "2026-07-28T15:00:00.000Z";
429
+ readonly scheduleId: "sch_0123456789abcdefghjkmnpqrs";
430
+ readonly trigger: {
431
+ readonly kind: "automatic";
432
+ };
433
+ readonly type: "schedule.occurrence";
434
+ readonly version: "2026-08-08";
435
+ };
436
+ readonly exactBodyBase64: "eyJ2ZXJzaW9uIjoiMjAyNi0wOC0wOCIsInR5cGUiOiJzY2hlZHVsZS5vY2N1cnJlbmNlIiwic2NoZWR1bGVJZCI6InNjaF8wMTIzNDU2Nzg5YWJjZGVmZ2hqa21ucHFycyIsIm9jY3VycmVuY2VJZCI6Im9jY18wMTIzNDU2Nzg5YWJjZGVmZ2hqa21ucHFycyIsInBsYW5uZWRGb3IiOiIyMDI2LTA3LTI4VDE1OjAwOjAwLjAwMFoiLCJhdHRlbXB0IjoxLCJ0cmlnZ2VyIjp7ImtpbmQiOiJhdXRvbWF0aWMifSwiZGF0YSI6e319";
437
+ readonly exactBodyUtf8: "{\"version\":\"2026-08-08\",\"type\":\"schedule.occurrence\",\"scheduleId\":\"sch_0123456789abcdefghjkmnpqrs\",\"occurrenceId\":\"occ_0123456789abcdefghjkmnpqrs\",\"plannedFor\":\"2026-07-28T15:00:00.000Z\",\"attempt\":1,\"trigger\":{\"kind\":\"automatic\"},\"data\":{}}";
438
+ readonly headers: {
439
+ readonly "Callback-Delivery-Id": "dlv_0123456789abcdefghjkmnpqrs";
440
+ readonly "Callback-Key-Id": "csk_0123456789abcdefghjkmnpqrs";
441
+ readonly "Callback-Occurrence-Id": "occ_0123456789abcdefghjkmnpqrs";
442
+ readonly "Callback-Signature": "v1=bd96c6d68a5c0521c18600d92913ce4dcf32036f2c930d0f9db9de55da9280e7";
443
+ readonly "Callback-Timestamp": "2026-07-28T15:00:00.000Z";
444
+ readonly "Content-Type": "application/json";
445
+ };
446
+ readonly secret: "greatwait-signature-vector-key-not-a-credential";
447
+ readonly signingInputBase64: "MjAyNi0wNy0yOFQxNTowMDowMC4wMDBaLmRsdl8wMTIzNDU2Nzg5YWJjZGVmZ2hqa21ucHFycy57InZlcnNpb24iOiIyMDI2LTA4LTA4IiwidHlwZSI6InNjaGVkdWxlLm9jY3VycmVuY2UiLCJzY2hlZHVsZUlkIjoic2NoXzAxMjM0NTY3ODlhYmNkZWZnaGprbW5wcXJzIiwib2NjdXJyZW5jZUlkIjoib2NjXzAxMjM0NTY3ODlhYmNkZWZnaGprbW5wcXJzIiwicGxhbm5lZEZvciI6IjIwMjYtMDctMjhUMTU6MDA6MDAuMDAwWiIsImF0dGVtcHQiOjEsInRyaWdnZXIiOnsia2luZCI6ImF1dG9tYXRpYyJ9LCJkYXRhIjp7fX0=";
448
+ readonly signingInputUtf8: "2026-07-28T15:00:00.000Z.dlv_0123456789abcdefghjkmnpqrs.{\"version\":\"2026-08-08\",\"type\":\"schedule.occurrence\",\"scheduleId\":\"sch_0123456789abcdefghjkmnpqrs\",\"occurrenceId\":\"occ_0123456789abcdefghjkmnpqrs\",\"plannedFor\":\"2026-07-28T15:00:00.000Z\",\"attempt\":1,\"trigger\":{\"kind\":\"automatic\"},\"data\":{}}";
449
+ };
450
+ /**
451
+ * The retired `2026-08-08` envelope **with** `completion`, kept as a known
452
+ * answer.
453
+ *
454
+ * Under that version the field was optional and appeared only on a schedule
455
+ * configured for `callback` completion, and it carried no `deadline`. This is
456
+ * the published answer for that shape, byte for byte and MAC unchanged: a
457
+ * receiver mid-rollout is still handed it, and recomputing this vector is
458
+ * exactly the move that would hide a change to `signingInput`.
459
+ *
460
+ * The token is synthetic and shaped like ours without being one. A real token
461
+ * names an attempt at a dispatch epoch and expires; this is a vector.
462
+ */
463
+ readonly completionTestVector: {
464
+ readonly envelope: {
465
+ readonly attempt: 1;
466
+ readonly completion: {
467
+ readonly token: "v1.example-claim-not-a-real-token.example-mac";
468
+ readonly url: "https://greatwait-scheduler.example/callbacks/completion";
469
+ };
470
+ readonly data: {};
471
+ readonly occurrenceId: "occ_0123456789abcdefghjkmnpqrs";
472
+ readonly plannedFor: "2026-07-28T15:00:00.000Z";
473
+ readonly scheduleId: "sch_0123456789abcdefghjkmnpqrs";
474
+ readonly trigger: {
475
+ readonly kind: "automatic";
476
+ };
477
+ readonly type: "schedule.occurrence";
478
+ readonly version: "2026-08-08";
479
+ };
480
+ readonly exactBodyBase64: "eyJ2ZXJzaW9uIjoiMjAyNi0wOC0wOCIsInR5cGUiOiJzY2hlZHVsZS5vY2N1cnJlbmNlIiwic2NoZWR1bGVJZCI6InNjaF8wMTIzNDU2Nzg5YWJjZGVmZ2hqa21ucHFycyIsIm9jY3VycmVuY2VJZCI6Im9jY18wMTIzNDU2Nzg5YWJjZGVmZ2hqa21ucHFycyIsInBsYW5uZWRGb3IiOiIyMDI2LTA3LTI4VDE1OjAwOjAwLjAwMFoiLCJhdHRlbXB0IjoxLCJjb21wbGV0aW9uIjp7InRva2VuIjoidjEuZXhhbXBsZS1jbGFpbS1ub3QtYS1yZWFsLXRva2VuLmV4YW1wbGUtbWFjIiwidXJsIjoiaHR0cHM6Ly9ncmVhdHdhaXQtc2NoZWR1bGVyLmV4YW1wbGUvY2FsbGJhY2tzL2NvbXBsZXRpb24ifSwidHJpZ2dlciI6eyJraW5kIjoiYXV0b21hdGljIn0sImRhdGEiOnt9fQ==";
481
+ readonly exactBodyUtf8: "{\"version\":\"2026-08-08\",\"type\":\"schedule.occurrence\",\"scheduleId\":\"sch_0123456789abcdefghjkmnpqrs\",\"occurrenceId\":\"occ_0123456789abcdefghjkmnpqrs\",\"plannedFor\":\"2026-07-28T15:00:00.000Z\",\"attempt\":1,\"completion\":{\"token\":\"v1.example-claim-not-a-real-token.example-mac\",\"url\":\"https://greatwait-scheduler.example/callbacks/completion\"},\"trigger\":{\"kind\":\"automatic\"},\"data\":{}}";
482
+ readonly headers: {
483
+ readonly "Callback-Delivery-Id": "dlv_0123456789abcdefghjkmnpqrs";
484
+ readonly "Callback-Key-Id": "csk_0123456789abcdefghjkmnpqrs";
485
+ readonly "Callback-Occurrence-Id": "occ_0123456789abcdefghjkmnpqrs";
486
+ readonly "Callback-Signature": "v1=38c6d854051087ed408a5f6252d90375859f305aeb0bca1c3f3a5bb63135e138";
487
+ readonly "Callback-Timestamp": "2026-07-28T15:00:00.000Z";
488
+ readonly "Content-Type": "application/json";
489
+ };
490
+ readonly secret: "greatwait-signature-vector-key-not-a-credential";
491
+ readonly signingInputBase64: "MjAyNi0wNy0yOFQxNTowMDowMC4wMDBaLmRsdl8wMTIzNDU2Nzg5YWJjZGVmZ2hqa21ucHFycy57InZlcnNpb24iOiIyMDI2LTA4LTA4IiwidHlwZSI6InNjaGVkdWxlLm9jY3VycmVuY2UiLCJzY2hlZHVsZUlkIjoic2NoXzAxMjM0NTY3ODlhYmNkZWZnaGprbW5wcXJzIiwib2NjdXJyZW5jZUlkIjoib2NjXzAxMjM0NTY3ODlhYmNkZWZnaGprbW5wcXJzIiwicGxhbm5lZEZvciI6IjIwMjYtMDctMjhUMTU6MDA6MDAuMDAwWiIsImF0dGVtcHQiOjEsImNvbXBsZXRpb24iOnsidG9rZW4iOiJ2MS5leGFtcGxlLWNsYWltLW5vdC1hLXJlYWwtdG9rZW4uZXhhbXBsZS1tYWMiLCJ1cmwiOiJodHRwczovL2dyZWF0d2FpdC1zY2hlZHVsZXIuZXhhbXBsZS9jYWxsYmFja3MvY29tcGxldGlvbiJ9LCJ0cmlnZ2VyIjp7ImtpbmQiOiJhdXRvbWF0aWMifSwiZGF0YSI6e319";
492
+ readonly signingInputUtf8: "2026-07-28T15:00:00.000Z.dlv_0123456789abcdefghjkmnpqrs.{\"version\":\"2026-08-08\",\"type\":\"schedule.occurrence\",\"scheduleId\":\"sch_0123456789abcdefghjkmnpqrs\",\"occurrenceId\":\"occ_0123456789abcdefghjkmnpqrs\",\"plannedFor\":\"2026-07-28T15:00:00.000Z\",\"attempt\":1,\"completion\":{\"token\":\"v1.example-claim-not-a-real-token.example-mac\",\"url\":\"https://greatwait-scheduler.example/callbacks/completion\"},\"trigger\":{\"kind\":\"automatic\"},\"data\":{}}";
493
+ };
494
+ /**
495
+ * A redelivery, which is the only vector that discriminates this change.
496
+ *
497
+ * The two automatic vectors would still pass an implementation that dropped
498
+ * `trigger` on the floor and hard-coded `{"kind":"automatic"}`; this one
499
+ * cannot, because both the kind and the source attempt are inside the signed
500
+ * bytes.
501
+ */
502
+ readonly redeliveryTestVector: {
503
+ readonly envelope: {
504
+ readonly attempt: 2;
505
+ readonly completion: {
506
+ readonly deadline: "2026-07-28T15:00:05.000Z";
507
+ readonly token: "v2.example-claim-not-a-real-token.example-mac";
508
+ readonly url: "https://greatwait-scheduler.example/callbacks/completion";
509
+ };
510
+ readonly data: {};
511
+ readonly occurrenceId: "occ_0123456789abcdefghjkmnpqrs";
512
+ readonly plannedFor: "2026-07-28T15:00:00.000Z";
513
+ readonly scheduleId: "sch_0123456789abcdefghjkmnpqrs";
514
+ readonly trigger: {
515
+ readonly kind: "redelivery";
516
+ readonly sourceAttemptId: "att_0123456789abcdefghjkmnpqrs";
517
+ };
518
+ readonly type: "schedule.occurrence";
519
+ readonly version: "2026-08-23";
520
+ };
521
+ readonly exactBodyBase64: "eyJ2ZXJzaW9uIjoiMjAyNi0wOC0yMyIsInR5cGUiOiJzY2hlZHVsZS5vY2N1cnJlbmNlIiwic2NoZWR1bGVJZCI6InNjaF8wMTIzNDU2Nzg5YWJjZGVmZ2hqa21ucHFycyIsIm9jY3VycmVuY2VJZCI6Im9jY18wMTIzNDU2Nzg5YWJjZGVmZ2hqa21ucHFycyIsInBsYW5uZWRGb3IiOiIyMDI2LTA3LTI4VDE1OjAwOjAwLjAwMFoiLCJhdHRlbXB0IjoyLCJjb21wbGV0aW9uIjp7InRva2VuIjoidjIuZXhhbXBsZS1jbGFpbS1ub3QtYS1yZWFsLXRva2VuLmV4YW1wbGUtbWFjIiwidXJsIjoiaHR0cHM6Ly9ncmVhdHdhaXQtc2NoZWR1bGVyLmV4YW1wbGUvY2FsbGJhY2tzL2NvbXBsZXRpb24iLCJkZWFkbGluZSI6IjIwMjYtMDctMjhUMTU6MDA6MDUuMDAwWiJ9LCJ0cmlnZ2VyIjp7ImtpbmQiOiJyZWRlbGl2ZXJ5Iiwic291cmNlQXR0ZW1wdElkIjoiYXR0XzAxMjM0NTY3ODlhYmNkZWZnaGprbW5wcXJzIn0sImRhdGEiOnt9fQ==";
522
+ readonly exactBodyUtf8: "{\"version\":\"2026-08-23\",\"type\":\"schedule.occurrence\",\"scheduleId\":\"sch_0123456789abcdefghjkmnpqrs\",\"occurrenceId\":\"occ_0123456789abcdefghjkmnpqrs\",\"plannedFor\":\"2026-07-28T15:00:00.000Z\",\"attempt\":2,\"completion\":{\"token\":\"v2.example-claim-not-a-real-token.example-mac\",\"url\":\"https://greatwait-scheduler.example/callbacks/completion\",\"deadline\":\"2026-07-28T15:00:05.000Z\"},\"trigger\":{\"kind\":\"redelivery\",\"sourceAttemptId\":\"att_0123456789abcdefghjkmnpqrs\"},\"data\":{}}";
523
+ readonly headers: {
524
+ readonly "Callback-Delivery-Id": "dlv_0123456789abcdefghjkmnpqrs";
525
+ readonly "Callback-Key-Id": "csk_0123456789abcdefghjkmnpqrs";
526
+ readonly "Callback-Occurrence-Id": "occ_0123456789abcdefghjkmnpqrs";
527
+ readonly "Callback-Signature": "v1=9d698aed6d05dcd964f00d6e6796aab238f4f50e6f5cfeb61751fd7850778a04";
528
+ readonly "Callback-Timestamp": "2026-07-28T15:00:00.000Z";
529
+ readonly "Content-Type": "application/json";
530
+ };
531
+ readonly secret: "greatwait-signature-vector-key-not-a-credential";
532
+ readonly signingInputBase64: "MjAyNi0wNy0yOFQxNTowMDowMC4wMDBaLmRsdl8wMTIzNDU2Nzg5YWJjZGVmZ2hqa21ucHFycy57InZlcnNpb24iOiIyMDI2LTA4LTIzIiwidHlwZSI6InNjaGVkdWxlLm9jY3VycmVuY2UiLCJzY2hlZHVsZUlkIjoic2NoXzAxMjM0NTY3ODlhYmNkZWZnaGprbW5wcXJzIiwib2NjdXJyZW5jZUlkIjoib2NjXzAxMjM0NTY3ODlhYmNkZWZnaGprbW5wcXJzIiwicGxhbm5lZEZvciI6IjIwMjYtMDctMjhUMTU6MDA6MDAuMDAwWiIsImF0dGVtcHQiOjIsImNvbXBsZXRpb24iOnsidG9rZW4iOiJ2Mi5leGFtcGxlLWNsYWltLW5vdC1hLXJlYWwtdG9rZW4uZXhhbXBsZS1tYWMiLCJ1cmwiOiJodHRwczovL2dyZWF0d2FpdC1zY2hlZHVsZXIuZXhhbXBsZS9jYWxsYmFja3MvY29tcGxldGlvbiIsImRlYWRsaW5lIjoiMjAyNi0wNy0yOFQxNTowMDowNS4wMDBaIn0sInRyaWdnZXIiOnsia2luZCI6InJlZGVsaXZlcnkiLCJzb3VyY2VBdHRlbXB0SWQiOiJhdHRfMDEyMzQ1Njc4OWFiY2RlZmdoamttbnBxcnMifSwiZGF0YSI6e319";
533
+ readonly signingInputUtf8: "2026-07-28T15:00:00.000Z.dlv_0123456789abcdefghjkmnpqrs.{\"version\":\"2026-08-23\",\"type\":\"schedule.occurrence\",\"scheduleId\":\"sch_0123456789abcdefghjkmnpqrs\",\"occurrenceId\":\"occ_0123456789abcdefghjkmnpqrs\",\"plannedFor\":\"2026-07-28T15:00:00.000Z\",\"attempt\":2,\"completion\":{\"token\":\"v2.example-claim-not-a-real-token.example-mac\",\"url\":\"https://greatwait-scheduler.example/callbacks/completion\",\"deadline\":\"2026-07-28T15:00:05.000Z\"},\"trigger\":{\"kind\":\"redelivery\",\"sourceAttemptId\":\"att_0123456789abcdefghjkmnpqrs\"},\"data\":{}}";
534
+ };
535
+ /**
536
+ * The ordinary current-version delivery: automatic, and carrying completion
537
+ * material because every `2026-08-23` delivery does.
538
+ */
539
+ readonly testVector: {
540
+ readonly envelope: {
541
+ readonly attempt: 1;
542
+ readonly completion: {
543
+ readonly deadline: "2026-07-28T15:00:05.000Z";
544
+ readonly token: "v2.example-claim-not-a-real-token.example-mac";
545
+ readonly url: "https://greatwait-scheduler.example/callbacks/completion";
546
+ };
547
+ readonly data: {};
548
+ readonly occurrenceId: "occ_0123456789abcdefghjkmnpqrs";
549
+ readonly plannedFor: "2026-07-28T15:00:00.000Z";
550
+ readonly scheduleId: "sch_0123456789abcdefghjkmnpqrs";
551
+ readonly trigger: {
552
+ readonly kind: "automatic";
553
+ };
554
+ readonly type: "schedule.occurrence";
555
+ readonly version: "2026-08-23";
556
+ };
557
+ readonly exactBodyBase64: "eyJ2ZXJzaW9uIjoiMjAyNi0wOC0yMyIsInR5cGUiOiJzY2hlZHVsZS5vY2N1cnJlbmNlIiwic2NoZWR1bGVJZCI6InNjaF8wMTIzNDU2Nzg5YWJjZGVmZ2hqa21ucHFycyIsIm9jY3VycmVuY2VJZCI6Im9jY18wMTIzNDU2Nzg5YWJjZGVmZ2hqa21ucHFycyIsInBsYW5uZWRGb3IiOiIyMDI2LTA3LTI4VDE1OjAwOjAwLjAwMFoiLCJhdHRlbXB0IjoxLCJjb21wbGV0aW9uIjp7InRva2VuIjoidjIuZXhhbXBsZS1jbGFpbS1ub3QtYS1yZWFsLXRva2VuLmV4YW1wbGUtbWFjIiwidXJsIjoiaHR0cHM6Ly9ncmVhdHdhaXQtc2NoZWR1bGVyLmV4YW1wbGUvY2FsbGJhY2tzL2NvbXBsZXRpb24iLCJkZWFkbGluZSI6IjIwMjYtMDctMjhUMTU6MDA6MDUuMDAwWiJ9LCJ0cmlnZ2VyIjp7ImtpbmQiOiJhdXRvbWF0aWMifSwiZGF0YSI6e319";
558
+ readonly exactBodyUtf8: "{\"version\":\"2026-08-23\",\"type\":\"schedule.occurrence\",\"scheduleId\":\"sch_0123456789abcdefghjkmnpqrs\",\"occurrenceId\":\"occ_0123456789abcdefghjkmnpqrs\",\"plannedFor\":\"2026-07-28T15:00:00.000Z\",\"attempt\":1,\"completion\":{\"token\":\"v2.example-claim-not-a-real-token.example-mac\",\"url\":\"https://greatwait-scheduler.example/callbacks/completion\",\"deadline\":\"2026-07-28T15:00:05.000Z\"},\"trigger\":{\"kind\":\"automatic\"},\"data\":{}}";
559
+ readonly headers: {
560
+ readonly "Callback-Delivery-Id": "dlv_0123456789abcdefghjkmnpqrs";
561
+ readonly "Callback-Key-Id": "csk_0123456789abcdefghjkmnpqrs";
562
+ readonly "Callback-Occurrence-Id": "occ_0123456789abcdefghjkmnpqrs";
563
+ readonly "Callback-Signature": "v1=5bf68b786fbac1afa7e3474ae5039ad27900b0185bca43a3e45185d56315a312";
564
+ readonly "Callback-Timestamp": "2026-07-28T15:00:00.000Z";
565
+ readonly "Content-Type": "application/json";
566
+ };
567
+ readonly secret: "greatwait-signature-vector-key-not-a-credential";
568
+ readonly signingInputBase64: "MjAyNi0wNy0yOFQxNTowMDowMC4wMDBaLmRsdl8wMTIzNDU2Nzg5YWJjZGVmZ2hqa21ucHFycy57InZlcnNpb24iOiIyMDI2LTA4LTIzIiwidHlwZSI6InNjaGVkdWxlLm9jY3VycmVuY2UiLCJzY2hlZHVsZUlkIjoic2NoXzAxMjM0NTY3ODlhYmNkZWZnaGprbW5wcXJzIiwib2NjdXJyZW5jZUlkIjoib2NjXzAxMjM0NTY3ODlhYmNkZWZnaGprbW5wcXJzIiwicGxhbm5lZEZvciI6IjIwMjYtMDctMjhUMTU6MDA6MDAuMDAwWiIsImF0dGVtcHQiOjEsImNvbXBsZXRpb24iOnsidG9rZW4iOiJ2Mi5leGFtcGxlLWNsYWltLW5vdC1hLXJlYWwtdG9rZW4uZXhhbXBsZS1tYWMiLCJ1cmwiOiJodHRwczovL2dyZWF0d2FpdC1zY2hlZHVsZXIuZXhhbXBsZS9jYWxsYmFja3MvY29tcGxldGlvbiIsImRlYWRsaW5lIjoiMjAyNi0wNy0yOFQxNTowMDowNS4wMDBaIn0sInRyaWdnZXIiOnsia2luZCI6ImF1dG9tYXRpYyJ9LCJkYXRhIjp7fX0=";
569
+ readonly signingInputUtf8: "2026-07-28T15:00:00.000Z.dlv_0123456789abcdefghjkmnpqrs.{\"version\":\"2026-08-23\",\"type\":\"schedule.occurrence\",\"scheduleId\":\"sch_0123456789abcdefghjkmnpqrs\",\"occurrenceId\":\"occ_0123456789abcdefghjkmnpqrs\",\"plannedFor\":\"2026-07-28T15:00:00.000Z\",\"attempt\":1,\"completion\":{\"token\":\"v2.example-claim-not-a-real-token.example-mac\",\"url\":\"https://greatwait-scheduler.example/callbacks/completion\",\"deadline\":\"2026-07-28T15:00:05.000Z\"},\"trigger\":{\"kind\":\"automatic\"},\"data\":{}}";
570
+ };
571
+ readonly unicodeTestVector: {
572
+ readonly envelope: {
573
+ readonly attempt: 1;
574
+ readonly completion: {
575
+ readonly deadline: "2026-07-28T15:00:05.000Z";
576
+ readonly token: "v2.example-claim-not-a-real-token.example-mac";
577
+ readonly url: "https://greatwait-scheduler.example/callbacks/completion";
578
+ };
579
+ readonly data: {
580
+ readonly note: "café ☃";
581
+ };
582
+ readonly occurrenceId: "occ_0123456789abcdefghjkmnpqrs";
583
+ readonly plannedFor: "2026-07-28T15:00:00.000Z";
584
+ readonly scheduleId: "sch_0123456789abcdefghjkmnpqrs";
585
+ readonly trigger: {
586
+ readonly kind: "automatic";
587
+ };
588
+ readonly type: "schedule.occurrence";
589
+ readonly version: "2026-08-23";
590
+ };
591
+ readonly exactBodyBase64: "eyJ2ZXJzaW9uIjoiMjAyNi0wOC0yMyIsInR5cGUiOiJzY2hlZHVsZS5vY2N1cnJlbmNlIiwic2NoZWR1bGVJZCI6InNjaF8wMTIzNDU2Nzg5YWJjZGVmZ2hqa21ucHFycyIsIm9jY3VycmVuY2VJZCI6Im9jY18wMTIzNDU2Nzg5YWJjZGVmZ2hqa21ucHFycyIsInBsYW5uZWRGb3IiOiIyMDI2LTA3LTI4VDE1OjAwOjAwLjAwMFoiLCJhdHRlbXB0IjoxLCJjb21wbGV0aW9uIjp7InRva2VuIjoidjIuZXhhbXBsZS1jbGFpbS1ub3QtYS1yZWFsLXRva2VuLmV4YW1wbGUtbWFjIiwidXJsIjoiaHR0cHM6Ly9ncmVhdHdhaXQtc2NoZWR1bGVyLmV4YW1wbGUvY2FsbGJhY2tzL2NvbXBsZXRpb24iLCJkZWFkbGluZSI6IjIwMjYtMDctMjhUMTU6MDA6MDUuMDAwWiJ9LCJ0cmlnZ2VyIjp7ImtpbmQiOiJhdXRvbWF0aWMifSwiZGF0YSI6eyJub3RlIjoiY2Fmw6kg4piDIn19";
592
+ readonly exactBodyUtf8: "{\"version\":\"2026-08-23\",\"type\":\"schedule.occurrence\",\"scheduleId\":\"sch_0123456789abcdefghjkmnpqrs\",\"occurrenceId\":\"occ_0123456789abcdefghjkmnpqrs\",\"plannedFor\":\"2026-07-28T15:00:00.000Z\",\"attempt\":1,\"completion\":{\"token\":\"v2.example-claim-not-a-real-token.example-mac\",\"url\":\"https://greatwait-scheduler.example/callbacks/completion\",\"deadline\":\"2026-07-28T15:00:05.000Z\"},\"trigger\":{\"kind\":\"automatic\"},\"data\":{\"note\":\"café ☃\"}}";
593
+ readonly headers: {
594
+ readonly "Callback-Delivery-Id": "dlv_0123456789abcdefghjkmnpqrs";
595
+ readonly "Callback-Key-Id": "csk_0123456789abcdefghjkmnpqrs";
596
+ readonly "Callback-Occurrence-Id": "occ_0123456789abcdefghjkmnpqrs";
597
+ readonly "Callback-Signature": "v1=18a8d1c50ea54bd0a5ec27abe80d1c98e44a7f358ecf8f06615c174075100e72";
598
+ readonly "Callback-Timestamp": "2026-07-28T15:00:00.000Z";
599
+ readonly "Content-Type": "application/json";
600
+ };
601
+ readonly secret: "greatwait-signature-vector-key-not-a-credential";
602
+ readonly signingInputBase64: "MjAyNi0wNy0yOFQxNTowMDowMC4wMDBaLmRsdl8wMTIzNDU2Nzg5YWJjZGVmZ2hqa21ucHFycy57InZlcnNpb24iOiIyMDI2LTA4LTIzIiwidHlwZSI6InNjaGVkdWxlLm9jY3VycmVuY2UiLCJzY2hlZHVsZUlkIjoic2NoXzAxMjM0NTY3ODlhYmNkZWZnaGprbW5wcXJzIiwib2NjdXJyZW5jZUlkIjoib2NjXzAxMjM0NTY3ODlhYmNkZWZnaGprbW5wcXJzIiwicGxhbm5lZEZvciI6IjIwMjYtMDctMjhUMTU6MDA6MDAuMDAwWiIsImF0dGVtcHQiOjEsImNvbXBsZXRpb24iOnsidG9rZW4iOiJ2Mi5leGFtcGxlLWNsYWltLW5vdC1hLXJlYWwtdG9rZW4uZXhhbXBsZS1tYWMiLCJ1cmwiOiJodHRwczovL2dyZWF0d2FpdC1zY2hlZHVsZXIuZXhhbXBsZS9jYWxsYmFja3MvY29tcGxldGlvbiIsImRlYWRsaW5lIjoiMjAyNi0wNy0yOFQxNTowMDowNS4wMDBaIn0sInRyaWdnZXIiOnsia2luZCI6ImF1dG9tYXRpYyJ9LCJkYXRhIjp7Im5vdGUiOiJjYWbDqSDimIMifX0=";
603
+ readonly signingInputUtf8: "2026-07-28T15:00:00.000Z.dlv_0123456789abcdefghjkmnpqrs.{\"version\":\"2026-08-23\",\"type\":\"schedule.occurrence\",\"scheduleId\":\"sch_0123456789abcdefghjkmnpqrs\",\"occurrenceId\":\"occ_0123456789abcdefghjkmnpqrs\",\"plannedFor\":\"2026-07-28T15:00:00.000Z\",\"attempt\":1,\"completion\":{\"token\":\"v2.example-claim-not-a-real-token.example-mac\",\"url\":\"https://greatwait-scheduler.example/callbacks/completion\",\"deadline\":\"2026-07-28T15:00:05.000Z\"},\"trigger\":{\"kind\":\"automatic\"},\"data\":{\"note\":\"café ☃\"}}";
604
+ };
605
+ readonly version: "2026-08-23";
606
+ };
607
+ /**
608
+ * One published known-answer vector, whatever version it is for.
609
+ *
610
+ * The shape every vector in {@link CALLBACK_DELIVERY_PROTOCOL} has, named so
611
+ * the whole set can be enumerated rather than listed.
612
+ */
613
+ export interface CallbackDeliveryTestVector {
614
+ readonly envelope: CallbackEnvelope;
615
+ readonly exactBodyBase64: string;
616
+ readonly exactBodyUtf8: string;
617
+ /**
618
+ * Every header the delivery carries, named rather than indexed.
619
+ *
620
+ * A `Record<string, string>` would read back as `string | undefined` under
621
+ * `noUncheckedIndexedAccess`, which makes every consumer assert a header the
622
+ * vector is defined to have — and an assertion is exactly what a published
623
+ * known answer should not need.
624
+ */
625
+ readonly headers: {
626
+ readonly "Callback-Delivery-Id": string;
627
+ readonly "Callback-Key-Id": string;
628
+ readonly "Callback-Occurrence-Id": string;
629
+ readonly "Callback-Signature": string;
630
+ readonly "Callback-Timestamp": string;
631
+ readonly "Content-Type": string;
632
+ };
633
+ readonly secret: string;
634
+ readonly signingInputBase64: string;
635
+ readonly signingInputUtf8: string;
636
+ }
637
+ /**
638
+ * Every published vector, derived from the protocol rather than listed.
639
+ *
640
+ * **A hand-written list is the failure this exists to prevent.** The duplicated
641
+ * serializer in `@greatwait/delivery` is held byte-for-byte against these, and
642
+ * the one time it drifted, it drifted on the field whose vector nobody had
643
+ * added to the list yet. Reading the protocol object means a vector added later
644
+ * is driven by every suite that already iterates this, with no second edit.
645
+ *
646
+ * **Matched by shape rather than by name**, which is not fastidiousness: the
647
+ * first version of this matched keys ending in `TestVector` and silently
648
+ * dropped `testVector`, whose lower-case first letter makes that suffix test
649
+ * false. A filter that quietly returns one fewer than it should is the same
650
+ * failure as the hand-written list, arrived at differently.
651
+ */
652
+ export declare function callbackDeliveryTestVectors(): readonly (CallbackDeliveryTestVector & {
653
+ readonly name: string;
654
+ })[];
655
+ /**
656
+ * What moving the envelope to `2026-08-08` is, in the vocabulary the
657
+ * compatibility module classifies.
658
+ *
659
+ * Recorded as data rather than asserted in prose because the verdict is an
660
+ * output: `assess(CALLBACK_ENVELOPE_VERSION_CHANGE)` returns
661
+ * `{ kind: "breaking", requiresNewContractVersion: true }`, and a fixture holds
662
+ * it there. "It only added a field, so it is additive" is the reading this
663
+ * exists to refuse — what a client parses is not the field, it is the version
664
+ * literal, and that literal *changed*.
665
+ *
666
+ * The contract version does not move with it. `CONTRACT_VERSION` versions the
667
+ * REST surface; the envelope carries its own dated version precisely so a break
668
+ * in the callback body does not force every API client to re-pin.
669
+ */
670
+ export declare const CALLBACK_ENVELOPE_VERSION_CHANGE: {
671
+ readonly operation: "changed";
672
+ readonly subject: "response-field";
673
+ };
674
+ /**
675
+ * How a receiver's response is classified. Public and stable: a customer tunes
676
+ * their endpoint against these categories.
677
+ */
678
+ export type ResponseClass = "ambiguous" | "retryable" | "success" | "terminal";
679
+ /**
680
+ * The outcome of one delivery attempt. `no-response` is a first-class case,
681
+ * not an absent status: a timeout or a dropped connection means the receiver
682
+ * may or may not have acted, and collapsing that into a status code is how an
683
+ * occurrence gets silently dropped. The copied engine already produces exactly
684
+ * this case.
685
+ */
686
+ export type AttemptOutcome = {
687
+ readonly kind: "no-response";
688
+ } | {
689
+ readonly kind: "status";
690
+ readonly status: number;
691
+ };
692
+ /**
693
+ * Classifies a status code alone. Use `classifyOutcome` where a request may not
694
+ * complete.
695
+ *
696
+ * **`isCallbackRefusalStatus` is consulted rather than duplicated here.** This
697
+ * function is the published statement of what a receiver's answer means and
698
+ * `performGreatwaitAttempt` is what acts on it; before FXO-1596 the two carried
699
+ * the same status boundary as two independent literals, kept equal by hand.
700
+ * They are now one list, so a status cannot be retried by the executor while
701
+ * this function tells the customer it was terminal.
702
+ */
703
+ export declare function classifyStatus(status: number): ResponseClass;
704
+ export declare function classifyOutcome(outcome: AttemptOutcome): ResponseClass;