Repository navigation
Commit 7f955e5
* fix(sharing): revoke every share on a deleted record, not just rule grants (#5103)
A `sys_record_share` row says "principal P has level L on (object O, record
R)". Delete R and the row describes nothing — yet it stayed in the table
forever.
#4779 (PR #5102) bound an `afterDelete` for this, but inside the sharing-RULE
package, where two conditions fenced it in: it revokes only `source: 'rule'`
rows, and `bindRuleHooks` binds only on objects that appear in
`sys_sharing_rule`. So an object using nothing but MANUAL shares had no delete
hook at all, and manual share + record delete = a permanent orphan.
Harm is bounded today only because record ids are never reused — an assumption
no gate enforces. A custom primary key, an import preserving ids, or any future
recycling turns those rows into real escalation: a new record on a recycled id
inherits the dead record's recipients.
Maintainer ruling (2026-08-04, on the issue): option A. Option B (a platform
polymorphic weak-reference cascade) is a separate engine-lane design card
(#5180); when it lands these hooks collapse into it.
- `record-share-cascade.ts` binds ONE global `beforeDelete`/`afterDelete` pair
and judges the object's sharing posture from `sharingModel` metadata PER
DELETE. Nothing is enumerated at boot, so nothing goes stale — an object that
gains sharing at runtime is covered on its next delete with no rebind, which
is a stronger answer to the ruling's hot-update requirement than a metadata
subscription would have been. Bounded row sets are revoked synchronously and
set-based; an unbounded delete queues an object-scoped orphan sweep rather
than the rule path's revoke-then-regrant, which is unavailable here because
nothing can re-create a manual share. System-context deletes cascade too.
- `SharingService.sweepOrphanedRecordShares` is the record-existence twin of
`sweepOrphanedRuleGrants` (#4433) — that one asks whether the RULE row still
exists and therefore can never see a manual share. Runs on
`kernel:bootstrapped`, keyset-paged with one batched existence probe per
object per page and a scan cap that reports itself. An object whose probe
FAILS keeps its rows: "could not ask" is not "the record is gone".
- The `beforeDelete` row-set stash moves from `rule-hooks.ts` into
`bulk-recompute.ts` beside its resolver, so both hook packages share one
answer per write instead of resolving the same predicate twice.
Rule recompute still never touches a manual share (#5102's pin, re-asserted in
this branch's tests). Only the record's DELETION revokes it, and only because
there is no longer anything to have access to.
Fixes #5103
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015W6nhsDrz6zWQc8je12a1t
* fix(sharing): the deferred-sweep breadcrumb is info, not warn (#5103)
The rule path warns on its unbounded branch because recipients visibly lose
access to records they still qualify for until the re-grant lands. Nothing
equivalent happens here: the sweep only removes rows whose record is gone, so
a deferred reclaim takes nothing from a surviving record and has no
user-visible consequence. A warn on every predicate delete would only erode
the level (AGENTS.md's own caution against over-applying it). The sweep still
warns when it actually revokes rows.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015W6nhsDrz6zWQc8je12a1t
* docs(sharing): record why referential cascades are already covered (#5103)
`cascadeDeleteRelations` removes a `deleteBehavior: 'cascade'` child through
the public `delete()` rather than the driver, so a detail record swept away
with its master reaches this hook like any other delete. That is the fact
behind treating `controlled_by_parent` as sharing-capable: manual grants are
refused there, but the rule evaluator can still materialise rows under system
context, and this is the path that reclaims them.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015W6nhsDrz6zWQc8je12a1t
---------
Co-authored-by: Claude <noreply@anthropic.com>
1 parent e96ad55 commit 7f955e5
8 files changed
Lines changed: 1408 additions & 30 deletions
File tree
- .changeset
- packages/plugins/plugin-sharing/src
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
| 48 | + | |
| 49 | + | |
| 50 | + | |
| 51 | + | |
| 52 | + | |
| 53 | + | |
| 54 | + | |
| 55 | + | |
| 56 | + | |
| 57 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
194 | 194 | | |
195 | 195 | | |
196 | 196 | | |
| 197 | + | |
| 198 | + | |
| 199 | + | |
| 200 | + | |
| 201 | + | |
| 202 | + | |
| 203 | + | |
| 204 | + | |
| 205 | + | |
| 206 | + | |
| 207 | + | |
| 208 | + | |
| 209 | + | |
| 210 | + | |
| 211 | + | |
| 212 | + | |
| 213 | + | |
| 214 | + | |
| 215 | + | |
| 216 | + | |
| 217 | + | |
| 218 | + | |
| 219 | + | |
| 220 | + | |
| 221 | + | |
| 222 | + | |
| 223 | + | |
| 224 | + | |
| 225 | + | |
| 226 | + | |
| 227 | + | |
| 228 | + | |
| 229 | + | |
| 230 | + | |
| 231 | + | |
| 232 | + | |
| 233 | + | |
| 234 | + | |
| 235 | + | |
| 236 | + | |
| 237 | + | |
| 238 | + | |
| 239 | + | |
| 240 | + | |
| 241 | + | |
| 242 | + | |
| 243 | + | |
| 244 | + | |
| 245 | + | |
| 246 | + | |
| 247 | + | |
| 248 | + | |
| 249 | + | |
| 250 | + | |
| 251 | + | |
| 252 | + | |
| 253 | + | |
| 254 | + | |
| 255 | + | |
| 256 | + | |
| 257 | + | |
| 258 | + | |
| 259 | + | |
| 260 | + | |
197 | 261 | | |
198 | 262 | | |
199 | 263 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
13 | 13 | | |
14 | 14 | | |
15 | 15 | | |
| 16 | + | |
16 | 17 | | |
17 | 18 | | |
| 19 | + | |
| 20 | + | |
18 | 21 | | |
19 | 22 | | |
20 | 23 | | |
| |||
43 | 46 | | |
44 | 47 | | |
45 | 48 | | |
| 49 | + | |
| 50 | + | |
| 51 | + | |
46 | 52 | | |
47 | 53 | | |
48 | 54 | | |
49 | 55 | | |
| 56 | + | |
| 57 | + | |
| 58 | + | |
| 59 | + | |
| 60 | + | |
| 61 | + | |
| 62 | + | |
| 63 | + | |
50 | 64 | | |
51 | 65 | | |
52 | 66 | | |
| |||
0 commit comments