@objectstack/plugin-webhooks 17.1.0 → 17.3.0

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.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,820 @@
1
1
  # @objectstack/plugin-webhooks
2
2
 
3
+ ## 17.3.0
4
+
5
+ ### Patch Changes
6
+
7
+ - e7191ce: fix(build): give each `exports` condition its own `types` target in the 28 dual-build packages (#13112)
8
+
9
+ **Published-surface change, zero runtime change.** No emitted byte moves; what
10
+ moves is which declaration file a resolver READS. Maintainer ruling 2026-08-29
11
+ (decision batch #3, verbatim 「同意」) chose declaring the files over deleting
12
+ them.
13
+
14
+ ## What was wrong
15
+
16
+ These 28 packages are `"type": "module"` and dual-built, and each spelled one
17
+ `types` condition as a **sibling** of `import`/`require`:
18
+
19
+ ```json
20
+ "exports": { ".": {
21
+ "types": "./dist/index.d.ts", "import": "./dist/index.js", "require": "./dist/index.cjs"
22
+ } }
23
+ ```
24
+
25
+ A sibling `types` answers for **both** conditions, so a CommonJS consumer was
26
+ handed `dist/index.d.ts` — an ES-module declaration, because the package is
27
+ `"type": "module"` — for an entry point it reaches with `require`. Measured with
28
+ `tsc --traceResolution` on a `"type": "commonjs"` fixture at `moduleResolution:
29
+ node16`:
30
+
31
+ ```
32
+ error TS1479: The current file is a CommonJS module whose imports will produce
33
+ 'require' calls; however, the referenced file is an ECMAScript module and cannot
34
+ be imported with 'require'.
35
+ ```
36
+
37
+ The JavaScript at `dist/index.cjs` loads perfectly (`check:dual-build-cjs-loads`
38
+ has asserted that for months). It is the **types** that told the consumer the
39
+ supported `require` entry point could not be required. The `dist/index.d.cts`
40
+ twin tsup emits beside it — 36 files, 5,517,701 B on this build — was named by
41
+ no condition at all and shipped in every tarball unreachable.
42
+
43
+ ## What changed
44
+
45
+ Each condition now names its own declaration, the shape TypeScript documents:
46
+
47
+ ```json
48
+ "exports": { ".": {
49
+ "import": { "types": "./dist/index.d.ts", "default": "./dist/index.js" },
50
+ "require": { "types": "./dist/index.d.cts", "default": "./dist/index.cjs" }
51
+ } }
52
+ ```
53
+
54
+ 33 entry points across 27 packages, subpaths included. The root `types` field is
55
+ untouched, so `node10` resolvers are unaffected; the `import` condition resolves
56
+ exactly what it resolved before, measured as an unchanged control in the same
57
+ run.
58
+
59
+ ## `@objectstack/core` is deliberately NOT changed
60
+
61
+ Splitting a declaration in two makes TypeScript compare it nominally, and
62
+ `ObjectKernel` carries a `private plugins` member that reaches every plugin
63
+ through `PluginContext.getKernel()`. With core split, whole-repo `pnpm build`
64
+ fails in `@objectstack/verify` with 5 × TS2345 ("Types have separate
65
+ declarations of a private property 'plugins'"); with core held back and the
66
+ other 27 split, 71/71 tasks pass. So core keeps the sibling-`types` shape and
67
+ its two `.d.cts` files (220,854 B) stay unreachable, declared as such in
68
+ `check:dual-build-cjs-loads`. Splitting it needs a decision about core's public
69
+ types, not about an exports map.
70
+
71
+ ## For consumers
72
+
73
+ - **ESM consumers: nothing changes.** Same declaration file, byte for byte.
74
+ - **CJS consumers under `node16`/`nodenext`: TS1479 goes away** and the
75
+ declarations they get are the ones built for CommonJS.
76
+ - **`node10` / `moduleResolution: node` consumers: nothing changes** — they never
77
+ read `exports`.
78
+ - Nothing is removed: every path that resolved before still resolves.
79
+
80
+ Packages that are CJS-first (`require` → `./dist/index.js`, no `"type": "module"`)
81
+ were already correct and are untouched — their `dist/index.d.ts` really is the
82
+ CommonJS declaration. Their ESM mirror (an unreachable `.d.mts` under the
83
+ `import` condition) is a separate, larger population and is filed separately per
84
+ the ruling, not fixed here.
85
+
86
+ `check:dual-build-cjs-loads` grew a fourth invariant (TYPED) that reds on the old
87
+ shape, so the drift cannot return silently.
88
+ - 99d23b1: fix(service-messaging,service-automation,plugin-webhooks): stamp `organization_id` on `sys_http_delivery` rows so the cross-organization wall on `redeliver()` actually excludes other tenants' rows (#13546)
89
+
90
+ `sys_http_delivery` is tenant-scoped and `redeliver()` — the one
91
+ request-reachable door on it — deliberately scopes by the caller's
92
+ organization (#10740). But the enqueue door never stamped the
93
+ `organization_id` column, and the SQL driver's tenant term is
94
+ `(organization_id = :tenantId OR organization_id IS NULL)` — a deliberate
95
+ global-row fail-open — so 100% of delivery rows landed in the NULL arm:
96
+ visible to, and replayable by, every organization on a walled deployment.
97
+
98
+ The repair mirrors the notification outbox's existing convention
99
+ (`EnqueueDeliveryInput.organizationId`), end to end:
100
+
101
+ - `EnqueueHttpInput` gains an **optional** `organizationId` member (inherited
102
+ by `UndeliverableHttpInput`, so parked rows are tenant-stamped too), and
103
+ `HttpDelivery` surfaces it on read-back. `SqlHttpOutbox.insert` writes
104
+ `organization_id: input.organizationId ?? null` exactly like
105
+ `SqlOutbox.enqueue`; `MemoryHttpOutbox` stores the same field and — now
106
+ that its rows carry a tenant — applies `RedeliverOptions.tenantId` in
107
+ `redeliver()` with the driver's exact semantics (another organization's row
108
+ is invisible/`RESOURCE_NOT_FOUND`; an org-less row stays a global row; a
109
+ tenant-less caller stays unscoped).
110
+ - The flow `http` node (durable mode) threads its run's acting organization
111
+ (`AutomationContext.tenantId` — the same source as the `notify` node's
112
+ #11303 repair) and warns loudly when a multi-org run has none to thread.
113
+ - The webhook auto-enqueuer stamps each delivery with its subscription's own
114
+ organization (`sys_webhook.organization_id`); org-less subscriptions
115
+ enqueue org-less, unchanged.
116
+
117
+ Forward-stamping only: existing NULL rows are untouched (their disposition is
118
+ a separate decision). Producers with genuinely no organization — a
119
+ `single`-posture deployment, a stack before its first organization — keep
120
+ working unchanged; their rows land NULL, which is the honest global-row shape.
121
+ - 30928a6: fix(i18n): read the provenance companion at serving time, not only record it (#12642)
122
+
123
+ Maintainer ruling #12069 Option A (#11671) landed translation provenance as
124
+ **two** halves: `os i18n extract --source-hashes` RECORDS which source revision
125
+ a generated leaf is still a byte copy of, and `withSourceFallback` READS those
126
+ records at serving time and substitutes the current source for a leaf whose
127
+ source has moved underneath it. The recording half was then rolled out to every
128
+ bundle set. The reading half was not — measured on `main`: provenance
129
+ **recorded in 9 of 9** bundle sets and **read at serving time in 1**.
130
+
131
+ The other eight assembled their `TranslationBundle` straight from the raw
132
+ generated modules and never consulted the companion sitting beside them, so
133
+ they recorded the drift and went on serving the superseded draft. Nothing said
134
+ so: `check:i18n` compares key sets and they still matched, `check:i18n-coverage`
135
+ counts a present leaf as translated, and `check:i18n-stale-fill`'s cross-locale
136
+ rule needs a SECOND locale holding the same stale bytes before it can testify.
137
+ The measured case had one locale and no second witness.
138
+
139
+ All eight are wired here, in the shape `@objectstack/platform-objects`'s own
140
+ `metadata-translations/index.ts` uses — the committed
141
+ `<locale>.source-hashes.generated.ts` passed as the fourth argument, the third
142
+ left `undefined` because these sets have no hand-authored sections. Provenance
143
+ is now recorded in 9 of 9 sets and served in 9 of 9.
144
+
145
+ `@objectstack/plugin-webhooks` was the last of them and is the only one whose
146
+ manifest changed: `withSourceFallback` lives in `@objectstack/platform-objects`,
147
+ which that package did not declare. It was **already in that package's install
148
+ closure** through `@objectstack/service-messaging`, so the edge declares a
149
+ resolution that already resolved rather than adding a package to the graph —
150
+ and relying on it undeclared would have been a phantom dependency under this
151
+ repo's strict package manager.
152
+
153
+ `check:i18n-stale-fill` gains a second verdict, **UNSERVED PROVENANCE**, so this
154
+ cannot silently come apart again: a bundle set that commits a companion and
155
+ does not consult it at serving time now fails the build, including a tenth set
156
+ that lands tomorrow.
157
+
158
+ **Graded `patch`, and the grade is the interesting part.** No API changes, no
159
+ new exported surface, and no key set moves — substitution was chosen over
160
+ deletion precisely so key-set claims stay put (ruling #8765 Option B). What
161
+ changes is which STRING a stale leaf serves. On this tree that is **zero
162
+ leaves**: a record is only ever written for a leaf that IS a byte copy of the
163
+ current source, so the companions arrive 0-stale by construction. The change is
164
+ in what happens the next time a source string moves — the reader sees the
165
+ English source rather than a superseded draft of it, which is the same
166
+ degradation an untranslated key already produces and not a new state.
167
+ - de47336: chore(i18n): roll the generated-leaf provenance companion out to the remaining bundle sets (#12559)
168
+
169
+ `os i18n extract --source-hashes` (#11671, maintainer ruling #12069 Option A)
170
+ records, per generated translation leaf, the digest of the source revision that
171
+ leaf is **still a byte copy of** — the one signal that tells a stale fill from a
172
+ real translation once the source has moved and the two stopped being
173
+ distinguishable by value. It shipped opt-in, and exactly one of the nine i18n
174
+ bundle sets opted in. A landed detector, a changeset announcing it and a green
175
+ gate read together as *"generated translation staleness is now caught"*; for
176
+ eight of nine sets it was not, and the thing making it not caught was a single
177
+ absent flag in an extract config — invisible from all three of those surfaces.
178
+
179
+ **All eight remaining sets now opt in** — `plugin-approvals`, `plugin-audit`,
180
+ `plugin-security`, `plugin-sharing`, `plugin-webhooks`, `service-messaging`,
181
+ `service-realtime`, `service-storage`. Each documents `source-hashes` in its
182
+ extract config and commits three `<locale>.source-hashes.generated.ts`
183
+ companions, produced by the same extract run as the bundles they sit beside
184
+ (`check:i18n` compares them byte-for-byte, so they cannot be written by hand).
185
+ `check:i18n` now reports 7 bundles per set where it reported 4, and 11 for
186
+ `platform-objects` where it reported 8.
187
+
188
+ **Records count what is currently RECORDABLE, never what is covered.** A record
189
+ is written only for a leaf that *is* right now a byte copy of the current
190
+ source, so a fully translated locale starts with an empty table — which is the
191
+ instrument armed, not an instrument that measures nothing: the entry appears by
192
+ itself on the first extract after a leaf becomes a fill. Measured at this
193
+ commit, per set over its three translated locales: `service-messaging` 289,
194
+ `plugin-approvals` 61, `plugin-security` 33, `plugin-webhooks` 20,
195
+ `plugin-audit` 8, `service-storage` 7, `plugin-sharing` 1 (es-ES only; zh-CN and
196
+ ja-JP are fully translated and start empty), `service-realtime` 0 (all three
197
+ locales fully translated). **419 records written across the eight sets, 0
198
+ stale.**
199
+
200
+ **One extractor fix the rollout forced.** `--source-hashes` had one user, and
201
+ that user commits both generated sections, so the interaction with
202
+ `--no-metadata-forms` had never been exercised. The provenance table is computed
203
+ over every generated section the extractor builds; the eight sets here commit no
204
+ metadata-forms bundle, and their `metadataForms` subtree — absent from their
205
+ merge baseline — arrives as a fresh `--fill=default` copy of `en`, so every leaf
206
+ of it was recordable. First measured on `plugin-audit`: **763 records, of which
207
+ 2 were its own objects and 761 were digests of the Studio metadata-form baseline
208
+ `@objectstack/platform-objects` owns.** Those records are unreadable in the
209
+ package holding them and would have rewritten all 24 companions on any unrelated
210
+ `*.form.ts` change in `packages/spec` — the cross-package coupling ADR-0029 D8
211
+ and every `bundle-ownership.test.ts` keep out of committed bundles. The
212
+ companion now covers exactly the sections a run commits, decided by the same two
213
+ predicates that decide the bundle files. `platform-objects` commits both, so its
214
+ three committed companions are byte-for-byte unchanged.
215
+
216
+ **Grade: `patch`, and behaviour on the day it lands is unchanged for every
217
+ leaf.** A record is written only where a leaf is currently a byte copy of the
218
+ **current** source, so every record written equals the current digest and none
219
+ of them can be stale; the mechanism cannot arrive red. No committed translation
220
+ bundle changed a byte, no public API moved, and no leaf's rendered text changed.
221
+ `narrowToCommittedSections` is new but internal to `@objectstack/cli` — the
222
+ package's entrypoint does not re-export the extractor utils.
223
+
224
+ **What this does not do**, stated so the boundary is not inferred wrongly a
225
+ second time: these eight sets now *record* provenance. Reading it at serving
226
+ time is `withSourceFallback`, and that is still wired in
227
+ `@objectstack/platform-objects` alone — so a stale fill in one of the eight is
228
+ now recorded and reportable, but not yet substituted at runtime. Tracked
229
+ separately.
230
+ - 3dafd8c: Fixed the `sys_webhook` object's `ja-JP` display name (`label`) to ウェブフック, matching the already-Japanese `pluralLabel` and the bundle's own help prose (`object_name.help`, `triggers.help`), which both use the same term. Previously `label` was a stale `Webhook` fill left over from a prior source revision, so the object rendered its own name two different ways within the same locale in the Setup/admin UI. `description` (which uses "Webhook" inside a longer Japanese sentence) and the `Webhook ID` field label are unchanged — this only renames the object itself. `zh-CN` is deliberately untouched: it uses `Webhook` for both slots on purpose, as there is no established Chinese rendering of the term in this codebase.
231
+ - ad54eb3: feat(tooling): onboard all 14 `packages/plugins/**` packages into `check:test-typecheck` (#14062)
232
+
233
+ Every plugin package now has a `tsconfig.test.json` compiled by the shared
234
+ `check:test-typecheck` gate, and its `typecheck` script names it. Before this,
235
+ the shrink-only `test-typecheck-debt.json` ratchet said **nothing** about a
236
+ third of the repo's runtime surface: 14 packages, 1 `tsconfig.test.json`
237
+ (`plugin-security`, wired directly to `tsc` rather than to the instrument), and
238
+ 0 `check:test-typecheck` scripts.
239
+
240
+ Onboarded as a family by the director ruling of 2026-09-01 on #14062
241
+ (maintainer verbatim: 「同意」), which also carries the #5286 maintainer
242
+ authority the starting ledgers need. The smaller branch triage recommended —
243
+ declare the instrument's scope and re-site the two compile-time pins — was
244
+ recorded as considered and not taken: an instrument silent over a third of the
245
+ runtime surface is a hole readers generalise across, and that costs more than
246
+ fourteen tsconfigs.
247
+
248
+ **Measured, not assumed** (at `e80889095`, workspace closure built first). Four
249
+ packages carry residue and therefore a starting ledger — plugin-approvals 324
250
+ over 8 files, plugin-auth 94 over 10, plugin-sharing 3 over 2,
251
+ knowledge-ragflow 3 over 1. The other ten measure **zero** and deliberately get
252
+ no ledger file at all: the gate reads a missing ledger as `{ entries: {} }`, so
253
+ any error there is red immediately with no entry to be added to — strictly
254
+ stronger than a ledger holding nothing, and the call `plugin-security` had
255
+ already recorded for itself.
256
+
257
+ ⛔ **This does not repair 345 type errors.** Per ruling item 3 it makes the
258
+ ratchet able to *see* them; paydown follows the ratchet's own shrink-only
259
+ discipline on its own cards. No test file is edited here.
260
+
261
+ Two corrections to the finding's own prose, both measured: the exclusion is
262
+ narrower than "no plugin package compiles its tests" — 9 of the 14 already
263
+ compiled their tests inside the `typecheck`-invoked build config, at zero
264
+ errors — and `exec-context-annotation.pin.ts` is a `.pin.ts`, which
265
+ `**/*.test.ts` never excluded, so its directives were already live. The pin
266
+ this change genuinely makes real is
267
+ `plugin-approvals/src/manager-org-screen-parity.contract.test.ts`, which no tsc
268
+ program had ever read.
269
+ - 598b7ec: fix(i18n): re-translate the five leaves that served a superseded source revision (#12065)
270
+
271
+ `os i18n extract` merges gaps only, so a revised source string leaves the previous
272
+ revision standing in every translated locale — in sync by key, green under
273
+ `check:i18n` and counted as translated by `check:i18n-coverage`. The five leaves
274
+ `check:i18n-stale-fill` froze in its baseline are re-translated here from the
275
+ **current** `en` source, and the baseline is ratcheted to empty in the same change.
276
+
277
+ User-visible admin/Setup help text changes in `es-ES`, `ja-JP` and `zh-CN`:
278
+
279
+ - `dataset.fields.measures.helpText` (metadata forms) — all three locales promised a
280
+ `"certified"` governance flag that was removed from the declaration in 16.0.
281
+ - `sys_webhook.fields.method.help` — all three locales served the pre-revision method
282
+ enumeration after the source became a prose description.
283
+ - `sys_webhook.pluralLabel` — `ja-JP` was an untranslated Latin fill and is now
284
+ Japanese; `zh-CN` keeps `Webhook`, which is the term this bundle's own Chinese prose
285
+ uses and which carries no plural inflection.
286
+ - `sys_http_delivery.fields.attempts.help` — `es-ES` / `ja-JP` held an English fill and
287
+ `zh-CN` a translation of the same superseded source; all three now carry the
288
+ PARKED / terminal-row clause the source documents.
289
+ - `sys_notification_subscription.fields.principal.help` — the selector list was missing
290
+ the `owner_of:object:id` and bare-email forms in all three locales.
291
+
292
+ No schema, export or runtime behaviour changes: translated-locale leaf values only,
293
+ plus the shrink-only ratchet baseline.
294
+ - Updated dependencies [809d417]
295
+ - Updated dependencies [387e231]
296
+ - Updated dependencies [f794e4e]
297
+ - Updated dependencies [cae2169]
298
+ - Updated dependencies [b812a54]
299
+ - Updated dependencies [2d4fa75]
300
+ - Updated dependencies [0e4e51b]
301
+ - Updated dependencies [e84bbf6]
302
+ - Updated dependencies [effae80]
303
+ - Updated dependencies [efb3513]
304
+ - Updated dependencies [d62f990]
305
+ - Updated dependencies [c45d8e6]
306
+ - Updated dependencies [2e3e8c7]
307
+ - Updated dependencies [e621291]
308
+ - Updated dependencies [655b106]
309
+ - Updated dependencies [40a93b5]
310
+ - Updated dependencies [d5b330d]
311
+ - Updated dependencies [dda969c]
312
+ - Updated dependencies [1f45690]
313
+ - Updated dependencies [277948f]
314
+ - Updated dependencies [8bdd955]
315
+ - Updated dependencies [f3bbbef]
316
+ - Updated dependencies [4f24e9d]
317
+ - Updated dependencies [e27583e]
318
+ - Updated dependencies [4bd6faa]
319
+ - Updated dependencies [86cbe37]
320
+ - Updated dependencies [6a180e4]
321
+ - Updated dependencies [474242f]
322
+ - Updated dependencies [63cd487]
323
+ - Updated dependencies [bd4aa4e]
324
+ - Updated dependencies [803eaab]
325
+ - Updated dependencies [f8e8f03]
326
+ - Updated dependencies [983edf1]
327
+ - Updated dependencies [eae824e]
328
+ - Updated dependencies [f6fa22c]
329
+ - Updated dependencies [8a483b3]
330
+ - Updated dependencies [3bc2e38]
331
+ - Updated dependencies [97bcd99]
332
+ - Updated dependencies [df59de0]
333
+ - Updated dependencies [96e25a8]
334
+ - Updated dependencies [f75a38a]
335
+ - Updated dependencies [7a25e7d]
336
+ - Updated dependencies [1fa05a6]
337
+ - Updated dependencies [c85a265]
338
+ - Updated dependencies [dcb10a5]
339
+ - Updated dependencies [773a999]
340
+ - Updated dependencies [35dffea]
341
+ - Updated dependencies [d8024f0]
342
+ - Updated dependencies [8120808]
343
+ - Updated dependencies [776a098]
344
+ - Updated dependencies [5060877]
345
+ - Updated dependencies [4f6325d]
346
+ - Updated dependencies [52954c0]
347
+ - Updated dependencies [2aa8456]
348
+ - Updated dependencies [93809a3]
349
+ - Updated dependencies [7c0d0c3]
350
+ - Updated dependencies [daae7aa]
351
+ - Updated dependencies [8dc22d6]
352
+ - Updated dependencies [a392dbf]
353
+ - Updated dependencies [279431e]
354
+ - Updated dependencies [948dd6b]
355
+ - Updated dependencies [3b4c56c]
356
+ - Updated dependencies [ae8edd2]
357
+ - Updated dependencies [e25403c]
358
+ - Updated dependencies [64baa68]
359
+ - Updated dependencies [9fa70d7]
360
+ - Updated dependencies [09db64a]
361
+ - Updated dependencies [92916e7]
362
+ - Updated dependencies [a84f3ea]
363
+ - Updated dependencies [f2eaae8]
364
+ - Updated dependencies [c09451b]
365
+ - Updated dependencies [ba64877]
366
+ - Updated dependencies [e7191ce]
367
+ - Updated dependencies [7345308]
368
+ - Updated dependencies [79b6a22]
369
+ - Updated dependencies [30d96ab]
370
+ - Updated dependencies [f658793]
371
+ - Updated dependencies [c95ad19]
372
+ - Updated dependencies [e58ea8b]
373
+ - Updated dependencies [4a17645]
374
+ - Updated dependencies [3795c5f]
375
+ - Updated dependencies [8ab926b]
376
+ - Updated dependencies [7317cf2]
377
+ - Updated dependencies [e25e839]
378
+ - Updated dependencies [5997207]
379
+ - Updated dependencies [8b13cc8]
380
+ - Updated dependencies [4a4a35d]
381
+ - Updated dependencies [86e765a]
382
+ - Updated dependencies [1d7e76a]
383
+ - Updated dependencies [53dc739]
384
+ - Updated dependencies [fd289be]
385
+ - Updated dependencies [03bf7b1]
386
+ - Updated dependencies [f90e820]
387
+ - Updated dependencies [18d816a]
388
+ - Updated dependencies [e8bd715]
389
+ - Updated dependencies [b91c351]
390
+ - Updated dependencies [a28a3c0]
391
+ - Updated dependencies [daeaaf9]
392
+ - Updated dependencies [c459da6]
393
+ - Updated dependencies [e914733]
394
+ - Updated dependencies [f887e52]
395
+ - Updated dependencies [881f8d8]
396
+ - Updated dependencies [3bfa1e6]
397
+ - Updated dependencies [0a8ebf3]
398
+ - Updated dependencies [901355c]
399
+ - Updated dependencies [34ce8e7]
400
+ - Updated dependencies [33681ea]
401
+ - Updated dependencies [4635f3e]
402
+ - Updated dependencies [fd289be]
403
+ - Updated dependencies [ee3595c]
404
+ - Updated dependencies [99d23b1]
405
+ - Updated dependencies [09b4f4e]
406
+ - Updated dependencies [30928a6]
407
+ - Updated dependencies [de47336]
408
+ - Updated dependencies [b2eab95]
409
+ - Updated dependencies [93940d4]
410
+ - Updated dependencies [3a04b01]
411
+ - Updated dependencies [45b9051]
412
+ - Updated dependencies [3954fb7]
413
+ - Updated dependencies [4805b56]
414
+ - Updated dependencies [b9e9227]
415
+ - Updated dependencies [d395692]
416
+ - Updated dependencies [5894d30]
417
+ - Updated dependencies [a3765f6]
418
+ - Updated dependencies [2d5cee3]
419
+ - Updated dependencies [e22158f]
420
+ - Updated dependencies [7404925]
421
+ - Updated dependencies [0c2334f]
422
+ - Updated dependencies [778c59f]
423
+ - Updated dependencies [d2619fd]
424
+ - Updated dependencies [af56546]
425
+ - Updated dependencies [6acb11a]
426
+ - Updated dependencies [33c5fd3]
427
+ - Updated dependencies [20b0fdb]
428
+ - Updated dependencies [905019b]
429
+ - Updated dependencies [a286411]
430
+ - Updated dependencies [98c0d33]
431
+ - Updated dependencies [368a82e]
432
+ - Updated dependencies [a3d5724]
433
+ - Updated dependencies [93ea19b]
434
+ - Updated dependencies [9ee2dcf]
435
+ - Updated dependencies [8cb96ec]
436
+ - Updated dependencies [8f10a79]
437
+ - Updated dependencies [6269a55]
438
+ - Updated dependencies [a17da05]
439
+ - Updated dependencies [a8c00e2]
440
+ - Updated dependencies [0fb8760]
441
+ - Updated dependencies [e5ce2ed]
442
+ - Updated dependencies [be21955]
443
+ - Updated dependencies [bc56e18]
444
+ - Updated dependencies [be21955]
445
+ - Updated dependencies [a9ee989]
446
+ - Updated dependencies [4d0d944]
447
+ - Updated dependencies [15d58db]
448
+ - Updated dependencies [d63b014]
449
+ - Updated dependencies [9abe4e4]
450
+ - Updated dependencies [2cc7122]
451
+ - Updated dependencies [b6d9432]
452
+ - Updated dependencies [3b5f036]
453
+ - Updated dependencies [50d6c92]
454
+ - Updated dependencies [9e0ba21]
455
+ - Updated dependencies [311433f]
456
+ - Updated dependencies [3e5ad08]
457
+ - Updated dependencies [9abe4e4]
458
+ - Updated dependencies [b7131f3]
459
+ - Updated dependencies [e5812fa]
460
+ - Updated dependencies [7085f90]
461
+ - Updated dependencies [dee4dd4]
462
+ - Updated dependencies [ce7e497]
463
+ - Updated dependencies [51ecb2f]
464
+ - Updated dependencies [9086761]
465
+ - Updated dependencies [f6344e7]
466
+ - Updated dependencies [42a117b]
467
+ - Updated dependencies [e4902d2]
468
+ - Updated dependencies [1401ae7]
469
+ - Updated dependencies [e577445]
470
+ - Updated dependencies [4297fe7]
471
+ - Updated dependencies [e398863]
472
+ - Updated dependencies [d16df74]
473
+ - Updated dependencies [d79c602]
474
+ - Updated dependencies [f11fc61]
475
+ - Updated dependencies [e808890]
476
+ - Updated dependencies [8f79379]
477
+ - Updated dependencies [e6ca40e]
478
+ - Updated dependencies [0c77ea4]
479
+ - Updated dependencies [52954c0]
480
+ - Updated dependencies [89eb997]
481
+ - Updated dependencies [7131f12]
482
+ - Updated dependencies [aa5994e]
483
+ - Updated dependencies [be93457]
484
+ - Updated dependencies [a65db76]
485
+ - Updated dependencies [15eb2c9]
486
+ - Updated dependencies [5691b07]
487
+ - Updated dependencies [d9cf78e]
488
+ - Updated dependencies [1a47a53]
489
+ - Updated dependencies [2a6122b]
490
+ - Updated dependencies [225e769]
491
+ - Updated dependencies [8af88dd]
492
+ - Updated dependencies [fb5fbb8]
493
+ - Updated dependencies [d7b3963]
494
+ - Updated dependencies [33184fd]
495
+ - Updated dependencies [7c41693]
496
+ - Updated dependencies [b72db01]
497
+ - Updated dependencies [dce5cd4]
498
+ - Updated dependencies [9688f58]
499
+ - Updated dependencies [556ebc1]
500
+ - Updated dependencies [177ebdc]
501
+ - Updated dependencies [8d237b4]
502
+ - Updated dependencies [2d2e6f0]
503
+ - Updated dependencies [2d8dd8d]
504
+ - Updated dependencies [22d573e]
505
+ - Updated dependencies [b5a2398]
506
+ - Updated dependencies [348860c]
507
+ - Updated dependencies [5383fa6]
508
+ - Updated dependencies [5b3ff63]
509
+ - Updated dependencies [1a6a19c]
510
+ - Updated dependencies [064d484]
511
+ - Updated dependencies [527e050]
512
+ - Updated dependencies [dd33bf9]
513
+ - Updated dependencies [4cb2a90]
514
+ - Updated dependencies [74a7804]
515
+ - Updated dependencies [53d3689]
516
+ - Updated dependencies [b3a63d3]
517
+ - Updated dependencies [49f0dcf]
518
+ - Updated dependencies [033a34c]
519
+ - Updated dependencies [4d25d22]
520
+ - Updated dependencies [1ffee51]
521
+ - Updated dependencies [5ae4303]
522
+ - Updated dependencies [ece4dad]
523
+ - Updated dependencies [e9b377e]
524
+ - Updated dependencies [146f448]
525
+ - Updated dependencies [735f5c7]
526
+ - Updated dependencies [a7e18de]
527
+ - Updated dependencies [366f895]
528
+ - Updated dependencies [dc75ba8]
529
+ - Updated dependencies [cce0aa9]
530
+ - Updated dependencies [e764507]
531
+ - Updated dependencies [cff17af]
532
+ - Updated dependencies [39404f3]
533
+ - Updated dependencies [ca1965f]
534
+ - Updated dependencies [8619f95]
535
+ - Updated dependencies [b706af9]
536
+ - Updated dependencies [add4360]
537
+ - Updated dependencies [e0abc38]
538
+ - Updated dependencies [fc9ba76]
539
+ - Updated dependencies [0f94cc7]
540
+ - Updated dependencies [a11c1a5]
541
+ - Updated dependencies [71f9cd1]
542
+ - Updated dependencies [ee17d86]
543
+ - Updated dependencies [cdbd920]
544
+ - Updated dependencies [18c432e]
545
+ - Updated dependencies [3c418c4]
546
+ - Updated dependencies [fa8715a]
547
+ - Updated dependencies [a933ed7]
548
+ - Updated dependencies [b3ca463]
549
+ - Updated dependencies [a933ed7]
550
+ - Updated dependencies [0d4a6a8]
551
+ - Updated dependencies [518d5e5]
552
+ - Updated dependencies [6643ba1]
553
+ - Updated dependencies [eeba2ef]
554
+ - Updated dependencies [ec4c4d2]
555
+ - Updated dependencies [424f73c]
556
+ - Updated dependencies [cccbe51]
557
+ - Updated dependencies [a8d6b1d]
558
+ - Updated dependencies [e4a7695]
559
+ - Updated dependencies [87075b1]
560
+ - Updated dependencies [fc58a99]
561
+ - Updated dependencies [14cfc00]
562
+ - Updated dependencies [1c6f7b4]
563
+ - Updated dependencies [e854a53]
564
+ - Updated dependencies [dfebfc8]
565
+ - Updated dependencies [598b7ec]
566
+ - Updated dependencies [ffbb7a1]
567
+ - Updated dependencies [d028b37]
568
+ - Updated dependencies [f7b25c5]
569
+ - Updated dependencies [122ef38]
570
+ - Updated dependencies [4a37870]
571
+ - Updated dependencies [428f9b2]
572
+ - Updated dependencies [aa7ff56]
573
+ - Updated dependencies [811a3c2]
574
+ - Updated dependencies [1401ae7]
575
+ - Updated dependencies [2fd3f1c]
576
+ - Updated dependencies [c41b42e]
577
+ - Updated dependencies [c4db311]
578
+ - Updated dependencies [750fff5]
579
+ - Updated dependencies [c19035e]
580
+ - Updated dependencies [ececf7a]
581
+ - Updated dependencies [d173125]
582
+ - Updated dependencies [8eeca27]
583
+ - Updated dependencies [8425c17]
584
+ - Updated dependencies [a5ef1d8]
585
+ - Updated dependencies [772d5de]
586
+ - Updated dependencies [ce80ec2]
587
+ - Updated dependencies [b372318]
588
+ - Updated dependencies [97a2263]
589
+ - Updated dependencies [29d0676]
590
+ - Updated dependencies [0169d49]
591
+ - Updated dependencies [6bd3231]
592
+ - Updated dependencies [d2b5ba8]
593
+ - Updated dependencies [b799ac5]
594
+ - Updated dependencies [8f74307]
595
+ - Updated dependencies [d23dc08]
596
+ - Updated dependencies [644ad50]
597
+ - Updated dependencies [0da7cd2]
598
+ - Updated dependencies [28a5c3e]
599
+ - Updated dependencies [4bc18e5]
600
+ - Updated dependencies [9f57f1e]
601
+ - @objectstack/spec@17.3.0
602
+ - @objectstack/platform-objects@17.3.0
603
+ - @objectstack/core@17.3.0
604
+ - @objectstack/service-messaging@17.3.0
605
+
606
+ ## 17.2.0
607
+
608
+ ### Minor Changes
609
+
610
+ - cdaa72f: fix(service-messaging,plugin-webhooks): the `update`-op tenant-audit surface on the delivery outboxes is classified — `ack` is a dispatcher sweep, `redeliver` threads the caller's tenant (#10740)
611
+
612
+ **BREAKING** signature change on `IHttpOutbox.redeliver` and
613
+ `MessagingService.redeliverHttp`, shipped as `minor` under the repo's
614
+ launch-window convention for breaking changes.
615
+
616
+ `sys_http_delivery` and `sys_notification_delivery` carry three single-record
617
+ (`multi: false`) writes that the SQL driver audits under the **`update`** op —
618
+ a different op, and a different throttle key, from the `updateMany` half
619
+ classified previously. Their correct classifications are **opposite**, and
620
+ treating them as one sweep is the dangerous reading:
621
+
622
+ | site | reachable from | classification |
623
+ | --- | --- | --- |
624
+ | `SqlNotificationOutbox.ack` | dispatcher tick only | global sweep |
625
+ | `SqlHttpOutbox.ack` | dispatcher tick only | global sweep |
626
+ | `SqlHttpOutbox.redeliver` | `POST /api/v1/webhooks/redeliver` | request-contextual |
627
+
628
+ **The two `ack` sites** are declared global sweeps through a new
629
+ `dispatcherAckOptions()` helper, sibling to `dispatcherSweepOptions()` and
630
+ deliberately not the same function — that one returns `& { multi: true }`, so a
631
+ `multi: false` site cannot borrow it by accident. The warrant was re-derived
632
+ against the current tree rather than inherited: `ack` has exactly two callers,
633
+ both inside `runPartition()` on a `setInterval` tick holding a per-partition
634
+ cluster lock, so no request context exists to thread; and the row being acked
635
+ was claimed by a sweep that crosses organizations by construction
636
+ (`hash(refId | notificationId | digestKey) mod N` is a load-spreading key, and
637
+ one outbox per environment drains the whole queue). Passing the claimed row's
638
+ own `organization_id` is documented at the helper as the tempting wrong answer:
639
+ a predicate read off the row you are about to write matches exactly that row,
640
+ adds no isolation, and silences the audit anyway — the appearance of scoping
641
+ without the substance.
642
+
643
+ **`redeliver` is not that**, and it is the reason this shipped separately. The
644
+ route in front of it is served to any authenticated user, so on a walled
645
+ deployment (`OS_TENANCY_POSTURE=isolated|group`) an unscoped replay is an
646
+ authenticated user writing another organization's delivery row — the case the
647
+ tenant audit exists to catch. It now carries the caller's tenant, applied to
648
+ the rows it reads as well as the row it writes, and it must never be given
649
+ `bypassTenantAudit`: a scoped write and a bypassed write produce the same
650
+ silence in the log, so the flag would convert a detectable hole into an
651
+ undetectable one. The webhook route resolves the session's
652
+ `activeOrganizationId` and threads it.
653
+
654
+ Behaviour change at the endpoint: a delivery row outside the caller's
655
+ organization is now **not found** (`RESOURCE_NOT_FOUND`, HTTP 404) rather than
656
+ replayed. It is deliberately invisible rather than forbidden, so the endpoint
657
+ is not an existence oracle for other tenants' delivery ids. An in-tenant
658
+ redelivery is unchanged.
659
+
660
+ Migrating a caller: `redeliver(id, guard?)` becomes
661
+ `redeliver(id, { tenantId, guard? })`, and `redeliverHttp(id)` becomes
662
+ `redeliverHttp(id, { tenantId })`. `tenantId` is a **required** property typed
663
+ `string | undefined`, so omitting it does not compile — a caller with no tenant
664
+ has to write `tenantId: undefined` and mean it. That is the point of the shape:
665
+ an optional property would let the dangerous case, a request path that simply
666
+ forgot, type-check in silence. Passing `undefined` leaves the write unscoped
667
+ and the audit line still fires, which is the intended reporting behaviour on a
668
+ deployment that cannot resolve an organization for the caller.
669
+
670
+ <!-- adr-0087: not-required (runtime-interface-only packages/services/service-messaging/src/http-outbox.ts#IHttpOutbox, packages/services/service-messaging/src/http-outbox.ts#RedeliverOptions) The surface that changed shape is `IHttpOutbox.redeliver`, plus the new `RedeliverOptions` argument type beside it. Both are TypeScript declarations in a service package with no `packages/spec` schema behind them: no metadata author writes a `redeliver` key, there is no authorable spelling and no `retiredKey()` tombstone, so `os migrate meta` has no stack source to rewrite. The change is a required second argument on an in-process method — a compile error at every call site, which is the notification channel, not a silent runtime gap. `MessagingService.redeliverHttp` moves with it and is deliberately NOT in the list above: this gate refuses that symbol as unresolvable, because `packages/spec/src/api/protocol.zod.ts` mentions the class name in a prose comment about `MessagingService.listInbox` while neither declaring nor importing it. The claim would be true and the gate cannot check it, so it is stated here for a reviewer instead of asserted where it would read as verified. It is a thin delegate to `IHttpOutbox.redeliver` in the same package and carries no schema of its own either. -->
671
+ - e222a53: **BREAKING** (compile-time only): twelve logger sink types that declared an
672
+ optional `error` now declare a **non-optional** `warn`, so a durability report
673
+ always has somewhere to land (#9754, #10556).
674
+
675
+ `minor`, not `major`: during the launch window this stack ships breaking changes
676
+ as `minor` — every publishable package versions in lockstep, so a `major` would
677
+ promote the whole release. `patch` would be wrong in the other direction, because
678
+ this *can* break a consumer's build.
679
+
680
+ `error` stays optional on every one of these types — hosts legitimately inject
681
+ reduced sinks, and requiring `error` was measured and rejected as #9754 option C.
682
+ What changes is that its *absence* now has a declared, guaranteed destination.
683
+ Call sites keep the `logger?.warn?.(…)` spelling as the backstop for hosts the
684
+ type cannot reach, so **no runtime behaviour changes**: nothing that printed
685
+ before stops printing, and nothing silent starts printing.
686
+
687
+ ### Who has to change, and what to do
688
+
689
+ Only a caller that hands one of these sinks an object with **no `warn` method** —
690
+ for example `{ info }` or `{ error }` alone. Add a `warn` member; there is no
691
+ rename, no removal, and no stored value or metadata key to rewrite. Every
692
+ construction site inside this repo already supplied one, so the in-repo cost was
693
+ zero; the compile error is reserved for the callers that were silently discarding
694
+ these reports.
695
+
696
+ The affected types, by package:
697
+
698
+ - `@objectstack/cloud-connection` — the internal `PluginContext['logger']`
699
+ - `@objectstack/metadata-protocol` — `IndexMigrationLogger`
700
+ - `@objectstack/plugin-approvals` — the internal `MinimalLogger` of `lifecycle-hooks`
701
+ - `@objectstack/plugin-audit` — `AuthEventAuditLogger`, `ReadAuditLogger`
702
+ - `@objectstack/plugin-auth` — `ReconcileMembershipDeps['logger']`, the internal
703
+ `LoggerLike` of `member-role-canonical`, and `AuthManagerOptions['logger']`
704
+ - `@objectstack/plugin-email` — `ReclaimLogger`, via `ReclaimAttachmentContentOptions`
705
+ - `@objectstack/plugin-reports` — `ReportServiceOptions['logger']`
706
+ - `@objectstack/plugin-sharing` — the internal `MinimalLogger` of `bulk-recompute`,
707
+ `rule-hooks` and `record-share-cascade`
708
+ - `@objectstack/plugin-webhooks` — `OptionalLogger`, via `AutoEnqueuerOptions`
709
+ - `@objectstack/service-knowledge` — `KnowledgeLogger`
710
+
711
+ `AuthManagerOptions['logger']` is the one most likely to be reached from outside:
712
+ `AuthManager` is public surface, its `logger` option stays optional, and a logger
713
+ that *is* supplied must now carry `warn`. The only non-test construction site in
714
+ this repo passes the kernel `Logger`, whose `warn` is already required.
715
+
716
+ `ReportService` and `AutoEnqueuer` additionally stopped defaulting their logger
717
+ field to `{}`. The field is now honestly optional rather than holding an empty
718
+ object that declared it could report and discarded everything. Behaviour is
719
+ unchanged in both directions.
720
+
721
+ <!-- adr-0087: not-required (runtime-interface-only packages/plugins/plugin-auth/src/auth-manager.ts#AuthManagerOptions, packages/plugins/plugin-auth/src/reconcile-membership.ts#ReconcileMembershipDeps, packages/metadata-protocol/src/migrations/partial-index-probe.ts#IndexMigrationLogger, packages/plugins/plugin-audit/src/auth-event-audit.ts#AuthEventAuditLogger, packages/plugins/plugin-audit/src/read-audit.ts#ReadAuditLogger, packages/plugins/plugin-reports/src/report-service.ts#ReportServiceOptions, packages/services/service-knowledge/src/knowledge-service.ts#KnowledgeLogger) every tightened type is a plain TypeScript logger interface -- no Zod projection, no metadata surface, and none is referenced by one -- so `objectstack migrate meta` has nothing to rewrite. Nothing is removed or renamed and no stored value moves; the only consumer action is adding a `warn` member at a construction site the compiler names. -->
722
+
723
+ ### Patch Changes
724
+
725
+ - 047ac86: Five `Plugin` implementations now release their resources from `destroy()`, the
726
+ only teardown hook the kernel calls (#10772).
727
+
728
+ `Plugin` (`@objectstack/core`'s `types.ts`) declares `init()`, `start?(ctx)` and
729
+ `destroy?()`. `ObjectKernel.performShutdown()` and `LiteKernel.destroy()` walk
730
+ the plugins in reverse calling `plugin.destroy()` — and nothing anywhere calls
731
+ `stop()`, `dispose()`, `close()` or `shutdown()` on a plugin. Each of these five
732
+ spelled its teardown with one of those names instead, so what it released was
733
+ still held after `await kernel.shutdown()` had **resolved**:
734
+
735
+ | package | class | was spelled | what outlived shutdown |
736
+ |:--|:--|:--|:--|
737
+ | `@objectstack/metadata` | `MetadataPlugin` | `stop` (arrow property) | artifact watcher, `manager.dispose()`, repository handle |
738
+ | `@objectstack/runtime` | `AppPlugin` | `stop` (arrow property) | the `app:unregistered` catalog event, never emitted |
739
+ | `@objectstack/runtime` | `ExternalValidationPlugin` | `stop` (arrow property) | every armed drift-check `setInterval` |
740
+ | `@objectstack/plugin-email` | `EmailServicePlugin` | `dispose` | two metadata subscriptions, the SMTP transport, an engine binding |
741
+ | `@objectstack/plugin-webhooks` | `WebhookOutboxPlugin` | `dispose` | the auto-enqueuer (2 realtime subscriptions + a refresh interval) and two engine hooks |
742
+
743
+ `ExternalValidationPlugin` is the one with teeth: it is one of only two `Plugin`
744
+ implementations in the tree that own `setInterval` directly, it is mounted on
745
+ the real `os serve` path, and its `stop()`'s only caller anywhere was the class
746
+ itself re-arming. Measured against a real kernel, its drift checker performed
747
+ five further reads in the five intervals after a resolved shutdown — the #9371
748
+ mechanism verbatim. `WebhookOutboxPlugin.dispose()` had **zero** callers in the
749
+ entire repo, so its teardown had never run in any process at all.
750
+
751
+ **Nothing is removed and no signature narrows.** Each old name is retained as a
752
+ delegating alias, because it is public API of an exported class and an embedder
753
+ may have learned to call it directly precisely BECAUSE the kernel never did.
754
+ `stop` stays an arrow property where it was one (so a detached
755
+ `const { stop } = plugin` keeps working) and stays synchronous on
756
+ `ExternalValidationPlugin` (so a non-awaiting call site is unaffected). The two
757
+ `stop(ctx)` aliases widen their parameter to optional.
758
+
759
+ One behavioural note for direct callers, since `destroy()` takes no context:
760
+ `MetadataPlugin.stop(ctx)` and `AppPlugin.stop(ctx)` now use the context
761
+ captured in `init()` and ignore the argument. In a real composition these are
762
+ the same object. The visible difference is confined to a plugin whose `init()`
763
+ never ran — for `MetadataPlugin` a dropped `warn` line, for `AppPlugin` a
764
+ catalog event that is no longer emitted for an app that was never registered.
765
+ - Updated dependencies [6936d07]
766
+ - Updated dependencies [59eb04d]
767
+ - Updated dependencies [9f05b7d]
768
+ - Updated dependencies [3b2af5e]
769
+ - Updated dependencies [7d2d112]
770
+ - Updated dependencies [5fa0d72]
771
+ - Updated dependencies [8cc8401]
772
+ - Updated dependencies [02b3b07]
773
+ - Updated dependencies [8163a1c]
774
+ - Updated dependencies [cdaa72f]
775
+ - Updated dependencies [914c413]
776
+ - Updated dependencies [55809a0]
777
+ - Updated dependencies [ee2ff45]
778
+ - Updated dependencies [47cd3ec]
779
+ - Updated dependencies [52db1d1]
780
+ - Updated dependencies [5649efb]
781
+ - Updated dependencies [9d7d2de]
782
+ - Updated dependencies [c815c50]
783
+ - Updated dependencies [795ea05]
784
+ - Updated dependencies [900e489]
785
+ - Updated dependencies [2306a76]
786
+ - Updated dependencies [e5ea701]
787
+ - Updated dependencies [a40dcc1]
788
+ - Updated dependencies [def0d3e]
789
+ - Updated dependencies [8d0bb79]
790
+ - Updated dependencies [5acb58d]
791
+ - Updated dependencies [2e3cf95]
792
+ - Updated dependencies [4c93387]
793
+ - Updated dependencies [504c8d5]
794
+ - Updated dependencies [a037f7c]
795
+ - Updated dependencies [3ee8ddf]
796
+ - Updated dependencies [16cef97]
797
+ - Updated dependencies [a79bd35]
798
+ - Updated dependencies [6ceaa4b]
799
+ - Updated dependencies [15ea214]
800
+ - Updated dependencies [de19489]
801
+ - Updated dependencies [c684d00]
802
+ - Updated dependencies [923c424]
803
+ - Updated dependencies [1ec36b7]
804
+ - Updated dependencies [5f2e54c]
805
+ - Updated dependencies [189373b]
806
+ - Updated dependencies [35ad101]
807
+ - Updated dependencies [ceb33a9]
808
+ - Updated dependencies [73d9795]
809
+ - Updated dependencies [8012960]
810
+ - Updated dependencies [f34f56b]
811
+ - Updated dependencies [f399618]
812
+ - Updated dependencies [75e9301]
813
+ - Updated dependencies [2810695]
814
+ - @objectstack/spec@17.2.0
815
+ - @objectstack/core@17.2.0
816
+ - @objectstack/service-messaging@17.2.0
817
+
3
818
  ## 17.1.0
4
819
 
5
820
  ### Patch Changes