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
{{ message }}
Repository navigation
Commit 76af143
Browse filesBrowse the repository at this point in the historyBrowse files
docs: correct before* bulk dispatch to the per-row model on two customer pages
Both pages still described the batch-dispatch model ADR-0058 Addendum II
retired: `before*` hooks dispatch once per matched row and `previous` is
bound per row, the premise the #16074 ruling was written on.
Five sites, not the two quoted on the card — the neighbouring paragraphs
carried the same retirement:
- formulas.mdx scope table: `previous` was documented as unbound in
`before*`.
- formulas.mdx "Write it once": described the per-row model as an `after*`
property only.
- formulas.mdx ceiling paragraph: attributed the 10 000-row refusal to
objects with `after*` hooks; one ceiling covers both phases and is
checked before the first dispatch.
- formulas.mdx warn Callout: stated `beforeUpdate`/`beforeDelete` fire once
for the whole batch, that `previous` is unbound there, and that a
`previous`-reading `before*` condition fails the write with a diagnostic
naming the batch — that diagnosis is retired and has had no producer
since the per-row dispatch landed. Replaced with the rule that is now
load-bearing: the payload stays batch-scoped, so only a
row-invariant-in-effect, in-place rewrite is in contract, backed by the
engine's key-divergence refusal.
- data-flow.mdx hook-order prose: "The `after*` events instead dispatch
once per matched row" implied `before*` does not.
Docs-only; `content/` is outside every package's `files[]`, so nothing
published moves.
Claude-Session: https://claude.ai/code/session_017ef78bLdybu3AffehKkhfk
Co-authored-by: Claude <noreply@anthropic.com>
Copy file name to clipboardExpand all lines: content/docs/api/data-flow.mdx
+1-1Lines changed: 1 addition & 1 deletion
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -308,7 +308,7 @@ flowchart TD
308
308
end
309
309
```
310
310
311
-
The lifecycle events are defined by the `HookEvent` enum (8 events). Reads fire `beforeFind`/`afterFind` — for **both**`find` and `findOne`, so one subscription covers every read shape. Bulk writes (`multi: true`) fire the **same**`beforeUpdate`/`beforeDelete`/`afterUpdate`/`afterDelete` events as single-id writes. A bulk write hands hooks **no** row-scoping predicate: it lives on the engine-internal `OperationContext.ast`, so the RLS / sharing filters composed onto it bind the driver call itself, where no handler can widen them — scope a batch through `options.where` at the caller. The `after*` events instead dispatch **once per matched row**, each on a single-record-shaped context whose `input.id` names that row. There are deliberately no per-method (`findOne`/`count`/`aggregate`) or `*Many` events: read authorization and row filtering are RLS/permission-rule concerns, and field masking is field-level metadata.
311
+
The lifecycle events are defined by the `HookEvent` enum (8 events). Reads fire `beforeFind`/`afterFind` — for **both** `find` and `findOne`, so one subscription covers every read shape. Bulk writes (`multi: true`) fire the **same** `beforeUpdate`/`beforeDelete`/`afterUpdate`/`afterDelete` events as single-id writes. A bulk write hands hooks **no** row-scoping predicate: it lives on the engine-internal `OperationContext.ast`, so the RLS / sharing filters composed onto it bind the driver call itself, where no handler can widen them — scope a batch through `options.where` at the caller. Both phases dispatch **once per matched row**, each on a single-record-shaped context whose `input.id` names that row and whose `previous` is that row's own pre-image — `after*` since #5038, `before*` since #5574 (ADR-0058 Addendum II). Zero matched rows is zero dispatches. One difference survives: the `after*` result is copied per row, while every per-row `before*` context carries **the** one batch payload, so a rewrite made on any row's dispatch applies to all of them — see [Transitions on bulk writes](/docs/data-modeling/formulas#transitions-on-bulk-writes). There are deliberately no per-method (`findOne`/`count`/`aggregate`) or `*Many` events: read authorization and row filtering are RLS/permission-rule concerns, and field masking is field-level metadata.
Copy file name to clipboardExpand all lines: content/docs/data-modeling/formulas.mdx
+33-18Lines changed: 33 additions & 18 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -202,7 +202,7 @@ Keep them pure, dependency-free, and AI-readable.
202
202
| Binding | Source | Available in |
203
203
|:---|:---|:---|
204
204
|`record`| the row being evaluated | formulas, validation, sharing, visibility |
205
-
|`previous`| row before update — on a `multi: true` write, that row's own pre-write state in `after*` hooks / record-change triggers (per row); unbound in `before*` hooks, which fire once for the batch| hooks, validation on update |
205
+
|`previous`| row before update — on a `multi: true` write, that row's own pre-write state, in `before*` and `after*` hooks alike and in record-change triggers: since ADR-0058 Addendum II both phases dispatch once per matched row| hooks, validation on update |
206
206
|`input`| hook payload | hooks |
207
207
|`current_user`| the authenticated subject — the canonical binding (ADR-0068). `user`, `ctx.user` and `os.user` are aliases of the **same** object | predicates with identity |
208
208
|`os.user`| alias of `current_user`| seed, predicates with identity |
@@ -371,12 +371,13 @@ The condition above is a **transition** — `record.status == 'escalated'` alone
371
371
would be true on every update of an already-escalated case, so "just became" is
372
372
only expressible by comparing against `previous`.
373
373
374
-
Write it once. A predicate (`multi: true`) write is N record changes, so
375
-
`after*` hooks — and the record-change flow triggers that ride them — are
376
-
evaluated and fired **once per matched row**, with `previous` bound to that
377
-
row's own pre-write state and `record` holding that row's real state rather than
378
-
the write's payload. The same condition therefore means the same thing whether
379
-
the write targets one id or matches a thousand rows:
374
+
Write it once. A predicate (`multi: true`) write is N record changes, so hooks
375
+
in **both** phases — `before*` and `after*` alike, and the record-change flow
376
+
triggers that ride the latter — are evaluated and fired **once per matched
377
+
row**, with `previous` bound to that row's own pre-write state and `record`
378
+
holding that row's own state (stored ⊕ payload) rather than the bare payload.
379
+
The same condition therefore means the same thing whether the write targets one
0 commit comments