Skip to content

Commit 8a48156

Browse files
committed
fix(plugin-sharing): defer rule backfill to kernel:listening so seed rows materialize (#2926 ③)
The boot backfill ran inside a kernel:ready handler, but SeedLoader also seeds on kernel:ready (raced against a budget, in a different AppPlugin handler). Since kernel:ready handlers fire sequentially in registration order, the backfill could run before the seed records exist and materialize nothing. Move the reconcile to kernel:listening (Phase 4), which the kernel fires only after every kernel:ready handler has settled — so the seeded rows are present. Verified end-to-end: a seeded red project now yields a sys_record_share to the exec recipient at boot, no runtime touch required. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017bJWtKmZ2mFpRFAmmmoeqe
1 parent f81c00c commit 8a48156

2 files changed

Lines changed: 24 additions & 2 deletions

File tree

.changeset/sharing-2926-boot-backfill.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,4 @@
22
'@objectstack/plugin-sharing': patch
33
---
44

5-
fix(plugin-sharing): reconcile every active sharing rule once at boot (#2926 ③). Rule grants are materialized by write hooks, which deliberately skip `isSystem` writes — so seed-loader records never produced `sys_record_share` rows and demo data shipping with matching sharing rules was broken out of the box until each record was touched at runtime. The new boot backfill runs after the rule hooks bind, is idempotent (diff-based reconcile), and is best-effort per rule so one broken rule cannot block startup.
5+
fix(plugin-sharing): reconcile every active sharing rule once at boot (#2926 ③). Rule grants are materialized by write hooks, which deliberately skip `isSystem` writes — so seed-loader records never produced `sys_record_share` rows and demo data shipping with matching sharing rules was broken out of the box until each record was touched at runtime. The boot backfill runs on `kernel:listening`the phase the kernel fires only after every `kernel:ready` handler has settled, including the AppPlugin seed loader — so the reconcile sees the seeded rows rather than racing them. It is idempotent (diff-based reconcile) and best-effort per rule so one broken rule cannot block startup.

packages/plugins/plugin-sharing/src/sharing-plugin.ts

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -307,7 +307,12 @@ export class SharingServicePlugin implements Plugin {
307307
unbindRuleProvenanceStamp(engine);
308308
bindRuleProvenanceStamp(engine, ctx.logger as any);
309309

310-
await backfillRuleGrants(this.ruleService, rules, ctx.logger as any);
310+
// [#2926 ③] Reconciling existing rows against every rule is
311+
// deferred to `kernel:listening` (below): seed data is loaded on
312+
// `kernel:ready` (raced against a budget, and the AppPlugin's seed
313+
// hook is a *different* kernel:ready handler), so a backfill here
314+
// would race the very records it must materialize. `kernel:listening`
315+
// fires only after every kernel:ready handler has settled.
311316
} else {
312317
ctx.logger.warn('SharingServicePlugin: engine has no hook API — sharing rule auto-evaluation disabled');
313318
}
@@ -388,6 +393,23 @@ export class SharingServicePlugin implements Plugin {
388393
ctx.logger.warn('SharingServicePlugin: share-link subsystem not started', { error: err?.message });
389394
}
390395
});
396+
397+
// [#2926 ③] Materialize sharing grants for rows already present at boot —
398+
// notably SeedLoader-inserted seed records, whose write goes through the
399+
// isSystem short-circuit in the rule hooks and therefore never produces a
400+
// `sys_record_share`. Runs on `kernel:listening` (Phase 4), after every
401+
// `kernel:ready` handler — including the AppPlugin seed loader — has
402+
// completed, so the reconcile sees the seeded rows. Idempotent: a runtime
403+
// write that already materialized a grant is reconciled to the same state.
404+
ctx.hook('kernel:listening', async () => {
405+
if (!this.ruleService) return;
406+
try {
407+
const rules = await this.ruleService.listRules({ activeOnly: true }, { isSystem: true } as any);
408+
await backfillRuleGrants(this.ruleService, rules, ctx.logger as any);
409+
} catch (err: any) {
410+
ctx.logger.warn('SharingServicePlugin: boot rule backfill (kernel:listening) failed', { error: err?.message });
411+
}
412+
});
391413
}
392414
}
393415

0 commit comments

Comments
 (0)