You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
`IHttpOutbox.ack()` takes an optional third argument, the claim credential, and `HttpDispatcher` now always passes it (#17634). A late ack from a claim the visibility-timeout reap had taken back — a send that outran `claimTtlMs` while another dispatcher re-claimed the row — used to write its outcome by row id over that dispatcher's live attempt: a delivery still in progress could be marked `dead`, or one attempt's outcome overwrite another's. Handed the credential, `SqlHttpOutbox` and `MemoryHttpOutbox` perform the compare-and-set `INotificationOutbox.ack()` has performed since #11859: the outcome is written only while the row is still `in_flight` under the same (`claimedBy`, `claimedAt`) pair `claim()` stamped on it. A lost claim writes nothing and throws the new `HttpAckError` (`DELIVERY_NOT_ELIGIBLE`, the code this package already raises for a delivery row in the wrong state); the dispatcher logs `http-dispatcher: ack refused, claim no longer held`, carries on with the rest of its batch, and whoever holds the row re-drives the delivery.
6
+
7
+
Nothing written against the two-argument `ack(id, result)` has to change. An `IHttpOutbox` implementation that does not read the third argument compiles and works as before, and a caller that does not pass it gets the by-id write it always got — that arity is deprecated, because it checks no ownership. New exports: `HttpClaimCredential` and `HttpAckError`. A subclass that overrides a built-in store's `ack()` should forward the third argument to `super.ack()`, or its dispatcher acks keep the old unchecked write.
Copy file name to clipboardExpand all lines: content/docs/automation/webhooks.mdx
+1Lines changed: 1 addition & 0 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -622,6 +622,7 @@ A precise table of what the runtime promises and what it does not.
622
622
| Producer node crashes mid-emit |**Not durable today.** The realtime bus (`InMemoryRealtimeAdapter`) is an unpersisted, in-process pub/sub — an event lost before Stage 3's INSERT is gone, not redelivered (see §4.1). |
623
623
| Subscriber node crashes after persist | Row exists in `sys_http_delivery`, another node picks it up. |
624
624
| Dispatcher node crashes mid-HTTP | Row stays `in_flight` with `claimed_by`; the first dispatcher tick after the claim TTL reverts it to `pending` and it is re-posted. The TTL derives from the dispatcher tick (`intervalMs`, default 500ms): `lockTtlMs = 5 × intervalMs`, `claimTtlMs = 2 × lockTtlMs` (so ~5s at defaults), all configurable via `HttpDispatcherOptions`. An idle surviving dispatcher ticks at least every `maxIdleIntervalMs` (default 30s), so recovery takes at most `claimTtlMs + maxIdleIntervalMs` (~35s at defaults). |
625
+
| A send outlasts the claim TTL and another dispatcher re-claims the row | The re-claiming dispatcher posts the delivery again (at-least-once — the receiver can see it twice; see the last row). The first dispatcher's late outcome is **not** written over the live attempt: its ack carries the claim credential (`claimed_by`, `claimed_at`) its claim stamped, the conditional update matches nothing, and it logs `http-dispatcher: ack refused, claim no longer held`. The row records the outcome of the attempt that still holds it. |
625
626
| Receiver returns 5xx | Retry per backoff schedule until the fixed 8-attempt budget is exhausted (§4.5). |
626
627
| Receiver returns 4xx | Treated as terminal — no retry, status `dead` immediately. Exception: 408 / 429 are retried. |
627
628
| Receiver returns 2xx |`status = success`, no more attempts. |
Copy file name to clipboardExpand all lines: content/docs/permissions/tenant-audit-census.mdx
+16-16Lines changed: 16 additions & 16 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -98,7 +98,7 @@ are reported as `undecidable` rather than assumed either way.
98
98
99
99
The same holds twice over for the context. An options argument spelled as a
100
100
literal can be read; one spelled `options`, `{ ...opts }`, or handed through a
101
-
forwarding shim cannot, and **66 of the 222 sites are spelled that way**. A
101
+
forwarding shim cannot, and **67 of the 223 sites are spelled that way**. A
102
102
context resolved from an inline literal or a local `const` can be tested for
103
103
`isSystem`; one arriving from a helper call cannot.
104
104
@@ -126,8 +126,8 @@ now **0**: nothing on this surface threads a context that provably lacks the fla
126
126
127
127
**"No tenant context" counted sites it had not read.** An options argument the
128
128
walker could not parse was folded into the same bucket as one it had read and
129
-
found empty. That published **83 sites "carrying no tenant context at all"**
130
-
when 17 said so and 66 were simply unread — an over-claim in the *alarming*
129
+
found empty. That published **84 sites "carrying no tenant context at all"**
130
+
when 17 said so and 67 were simply unread — an over-claim in the *alarming*
131
131
direction, on the very figure this page tells other cards to cite. `carries` is
132
132
now three-valued, and an unreadable argument can never contribute to the
133
133
provable count.
@@ -147,10 +147,10 @@ reproduce them. Where it disagrees, it disagrees on the page:
147
147
148
148
| carried figure | where it survives | this census |
149
149
| :--- | :--- | ---: |
150
-
| 175 write call sites | quoted in the merged changeset |**222**|
150
+
| 175 write call sites | quoted in the merged changeset |**223**|
151
151
| 24 carrying no tenant context | quoted in the merged changeset |**9** provable and tenancy-enabled; **32** more whose options argument is unreadable |
152
-
| 127 of 175 statically decidable, 48 runtime-parameter-name sites | restated on the `isSystem`-scoping card |**149 of 222** decidable, **73** undecidable |
153
-
| 135 (77%) silenced by the `isSystem` guard before the posture gate | the lost issue body — **no surviving corroboration**|**not reproduced**: 105 decidably elevated, 0 decidably not, 100 undecidable |
152
+
| 127 of 175 statically decidable, 48 runtime-parameter-name sites | restated on the `isSystem`-scoping card |**149 of 223** decidable, **74** undecidable |
153
+
| 135 (77%) silenced by the `isSystem` guard before the posture gate | the lost issue body — **no surviving corroboration**|**not reproduced**: 105 decidably elevated, 0 decidably not, 101 undecidable |
154
154
| 141 and 132, two independent re-derivations | the card that filed this work | — |
155
155
156
156
**The differences are not reconciled, and deliberately so.** The old census's
@@ -167,11 +167,11 @@ would report a smaller number and would not say so.
167
167
168
168
The fourth row is the one worth flagging to anyone citing it. **The 135 / 77%
169
169
figure has no surviving corroboration anywhere in the tree.** This census reads
170
-
105 of 222 (47%) as decidably elevated, with 100 more whose elevation is a
170
+
105 of 223 (47%) as decidably elevated, with 101 more whose elevation is a
171
171
run-time fact — so the claim is neither confirmed nor refuted, and the honest
172
172
answer is that a static reading cannot settle it.
173
173
174
-
⇒ **Cite `9 / 222`, and say what it is**: the sites whose options argument was
174
+
⇒ **Cite `9 / 223`, and say what it is**: the sites whose options argument was
175
175
READ and holds no tenant context, against a decidably tenancy-enabled object.
176
176
That is the control's provable yield surface. ⛔ Do not cite it as "the sites
177
177
without tenant context" — **32 further sites** have an options argument this
@@ -183,31 +183,31 @@ cannot read, and they are neither in nor out.
183
183
184
184
| what | count |
185
185
| :--- | ---: |
186
-
| write call sites on the application surface |**222**|
186
+
| write call sites on the application surface |**223**|
187
187
| …whose object name is statically decidable | 149 |
188
-
| …whose object name is chosen at run time |73|
188
+
| …whose object name is chosen at run time |74|
189
189
| …against an object with tenancy ENABLED | 149 |
190
190
| …against an object that declares tenancy off | 0 |
191
191
| threading a tenant context | 139 |
192
192
| PROVABLY carrying none (options read, no context key) |**17**|
193
193
| …of those, against a decidably tenancy-enabled object |**9**|
194
-
| options argument UNREADABLE — may or may not carry one |66|
194
+
| options argument UNREADABLE — may or may not carry one |67|
195
195
| …of those, against a decidably tenancy-enabled object | 32 |
196
196
| threading a decidably ELEVATED (`isSystem`) context | 105 |
197
197
| threading a context that is decidably NOT elevated | 0 |
198
-
| threading a context whose elevation is a run-time fact |100|
198
+
| threading a context whose elevation is a run-time fact |101|
199
199
200
200
| how the instrument reached the site | count |
201
201
| :--- | ---: |
202
-
| receiver carried a readable engine type |177|
202
+
| receiver carried a readable engine type |178|
203
203
| receiver erased, placed by the object NAME | 19 |
204
204
| receiver erased, placed by an `object: string` PARAMETER | 15 |
205
205
| receiver erased, placed by an `UNTYPED_RECEIVERS` row | 11 |
206
206
207
207
| object name spelled inline | 109 |
208
208
| object name spelled through a `const`| 40 |
209
209
| object name is an `object: string` parameter | 19 |
210
-
| object name is some other run-time expression |54|
210
+
| object name is some other run-time expression |55|
211
211
212
212
The corpus walked is every tracked non-test source under `packages/services/`
213
213
and `packages/plugins/`; calls to a same-named method on something that is not
@@ -224,11 +224,11 @@ holds still. They are required to be HERE and to say WHEN they were true;
224
224
their values are not compared. The reasoning, and the measurement behind it,
225
225
are in `scripts/check-tenant-audit-census.mjs`.
226
226
227
-
Measured on 2026-09-11 at `f368fd0aa`.
227
+
Measured on 2026-09-11 at `06215b074`.
228
228
229
229
| corpus scale (not enforced) | count |
230
230
| :--- | ---: |
231
-
| tracked non-test sources scanned |563|
231
+
| tracked non-test sources scanned |564|
232
232
| engine-shaped types recognised | 59 |
233
233
| declared objects in the registry | 300 |
234
234
| same-named calls subtracted as non-engine | 137 |
/** Every `options` bag that reached `SqlDriver.updateMany` — `redeliver`'s op since #11009, and the notification `ack`'s since #11453. */
92
+
/** Every `options` bag that reached `SqlDriver.updateMany` — `redeliver`'s op since #11009, the notification `ack`'s since #11453, and the HTTP `ack`'s since #17634. */
0 commit comments