diff --git a/.changeset/20596-trigger-schedule-provenance-anchors.md b/.changeset/20596-trigger-schedule-provenance-anchors.md new file mode 100644 index 00000000000..7d25f06447f --- /dev/null +++ b/.changeset/20596-trigger-schedule-provenance-anchors.md @@ -0,0 +1,10 @@ +--- +'@objectstack/trigger-schedule': patch +--- + +Provenance comments in `trigger-schedule` were re-anchored + +Comment and docblock lines under `src/` that cited tracker numbers which no +longer resolve on GitHub now cite the commit in this repository's history that +decided the matter, and say in their own words what was decided. Comments +only: no type, schema, export, log or refusal text, or runtime behaviour changes. diff --git a/packages/triggers/trigger-schedule/src/schedule-dispatch-claim.test.ts b/packages/triggers/trigger-schedule/src/schedule-dispatch-claim.test.ts index 66285b3c679..7591406001a 100644 --- a/packages/triggers/trigger-schedule/src/schedule-dispatch-claim.test.ts +++ b/packages/triggers/trigger-schedule/src/schedule-dispatch-claim.test.ts @@ -43,7 +43,7 @@ const JOB = `flow-schedule:${FLOW}`; const CRON: FlowTriggerBinding = { flowName: FLOW, schedule: { type: 'cron', expression: '0 1 * * *', timezone: 'UTC' }, - // [#16659] the acting organization every tick of this flow runs as. + // [commit ecdfc9411] the acting organization every tick of this flow runs as. organization: 'org_2mtx1w9d0k4bqf7v', }; diff --git a/packages/triggers/trigger-schedule/src/schedule-runas-e2e.test.ts b/packages/triggers/trigger-schedule/src/schedule-runas-e2e.test.ts index c12ae7bbf70..a8e3513169b 100644 --- a/packages/triggers/trigger-schedule/src/schedule-runas-e2e.test.ts +++ b/packages/triggers/trigger-schedule/src/schedule-runas-e2e.test.ts @@ -59,7 +59,7 @@ function scheduledDataFlow(name: string, runAs?: 'system' | 'user') { type: 'schedule', ...(runAs ? { runAs } : {}), nodes: [ - // [#16659] The acting organization a time-triggered flow declares. The + // [commit ecdfc9411] The acting organization a time-triggered flow declares. The // engine lifts it onto the binding and the trigger threads it onto the // run as `tenantId`; a flow without it is refused at bind. { id: 'start', type: 'start', label: 'Start', config: { schedule: { type: 'interval', intervalMs: 1000 }, organization: 'org_2mtx1w9d0k4bqf7v' } }, diff --git a/packages/triggers/trigger-schedule/src/schedule-trigger.test.ts b/packages/triggers/trigger-schedule/src/schedule-trigger.test.ts index af7373481e0..78a5dea4a46 100644 --- a/packages/triggers/trigger-schedule/src/schedule-trigger.test.ts +++ b/packages/triggers/trigger-schedule/src/schedule-trigger.test.ts @@ -66,7 +66,7 @@ function binding(overrides: Partial = {}): FlowTriggerBindin return { flowName: 'nightly_health_sweep', schedule: { type: 'cron', expression: '0 1 * * *', timezone: 'UTC' }, - // [#16659] A time-triggered binding carries its acting organization; a + // [commit ecdfc9411] A time-triggered binding carries its acting organization; a // binding without one is refused — see // `ScheduleTrigger — the acting-organization refusal (#16659)` below. organization: 'org_2mtx1w9d0k4bqf7v', @@ -324,7 +324,7 @@ describe('ScheduleTriggerPlugin', () => { }); }); -// ─── The acting-organization refusal (#16659) ─────────────────────── +// ─── The acting-organization refusal (commit ecdfc9411) ───────────── // // The unit half of the card's consequence (3): a time-triggered flow that // declares no acting organization is REFUSED at bind, and the refusal reaches @@ -509,7 +509,7 @@ describe('resolveBindingOrganization (#16659)', () => { // // Three states, three suites, and each one asserts what BINDS rather than only // what is logged: a refusal that logs correctly and arms the job anyway is the -// exact defect #16659's own refusal was shaped to avoid. +// exact defect commit ecdfc9411's own refusal was shaped to avoid. describe('ScheduleTrigger — the deployment switch is OFF (#17396)', () => { withScheduledWorkOff(); @@ -585,7 +585,7 @@ describe('ScheduleTrigger — switched ON under `single` (#17396)', () => { const trigger = new ScheduleTrigger(() => job.service, silentLogger()); // ⭐ The widening the whole card turns on: this exact binding is - // REFUSED under a wall (the #16659 suite above) and armed here. + // REFUSED under a wall (commit ecdfc9411's refusal suite above) and armed here. trigger.start(orgLess(), async () => {}); expect(job.jobs.size).toBe(1); }); diff --git a/packages/triggers/trigger-schedule/src/schedule-trigger.ts b/packages/triggers/trigger-schedule/src/schedule-trigger.ts index 5d67f8394de..65117048385 100644 --- a/packages/triggers/trigger-schedule/src/schedule-trigger.ts +++ b/packages/triggers/trigger-schedule/src/schedule-trigger.ts @@ -29,7 +29,7 @@ export interface FlowTriggerBinding { readonly condition?: string | { dialect?: string; source?: string; ast?: unknown }; readonly schedule?: unknown; /** - * [#16659] The ACTING ORGANIZATION a time-triggered flow declares on its + * [commit ecdfc9411] The ACTING ORGANIZATION a time-triggered flow declares on its * start node (`config.organization`), lifted onto the binding by the * engine's `resolveTriggerBinding` the same way `schedule` is. * @@ -248,7 +248,7 @@ export interface TriggerLogger { const JOB_PREFIX = 'flow-schedule'; /** - * Resolve the acting organization of a time-triggered binding (#16659), or + * Resolve the acting organization of a time-triggered binding (commit ecdfc9411), or * `null` when the flow declared none. * * Reads the binding's lifted `organization` first and the raw start-node @@ -369,7 +369,7 @@ export function refuseScheduledWorkDisabled( /** * Refuse to bind a time-triggered flow that declares no acting organization - * (#16659): say why at `error`, then THROW so the engine records the refusal. + * (commit ecdfc9411): say why at `error`, then THROW so the engine records the refusal. * * ## When this fires, after #17396 and #18378 * @@ -700,7 +700,7 @@ export class ScheduleTrigger implements FlowTrigger { return; } - // [#16659] The acting organization is part of the BINDING, so it is + // [commit ecdfc9411] The acting organization is part of the BINDING, so it is // checked before the job service is even resolved: a flow that cannot // legally run must not be reported as "not scheduled because the job // service is missing", which is a different defect with a different @@ -774,7 +774,7 @@ export class ScheduleTrigger implements FlowTrigger { try { const ctx: AutomationContext = { event: 'schedule', - // [#16659] When the flow declares one, the run executes AS + // [commit ecdfc9411] When the flow declares one, the run executes AS // that organization: `tenantId` is the acting run's // organization, and every consumer already reads it — // `notify-node.ts` threads it onto the notification it diff --git a/packages/triggers/trigger-schedule/src/time-relative-trigger.test.ts b/packages/triggers/trigger-schedule/src/time-relative-trigger.test.ts index 712fb53fcf1..655747ac91c 100644 --- a/packages/triggers/trigger-schedule/src/time-relative-trigger.test.ts +++ b/packages/triggers/trigger-schedule/src/time-relative-trigger.test.ts @@ -77,7 +77,7 @@ function fakeDataEngine(rows: Row[], knownObjects: string[] = ['contracts']) { } /** - * [#16659] A fake ObjectQL surface that HONOURS `context.tenantId`, so the + * [commit ecdfc9411] A fake ObjectQL surface that HONOURS `context.tenantId`, so the * differential control can put matching rows in two organizations and observe * which ones come back. * @@ -134,7 +134,7 @@ function silentLogger(): TriggerLogger { const NOW = () => new Date('2026-07-18T12:00:00.000Z'); /** - * [#16659] The organization every fixture binding declares. Named rather than + * [commit ecdfc9411] The organization every fixture binding declares. Named rather than * inlined because it is now asserted from two directions — the sweep's query * scope and the launched run's identity — and a literal repeated at both ends * of that pair can drift into agreeing with itself. @@ -146,7 +146,7 @@ function binding(timeRelative: unknown, overrides: Partial = flowName: 'renewal_alert', object: 'contracts', config: { timeRelative }, - // [#16659] see the schedule trigger's fixture note. + // [commit ecdfc9411] see the schedule trigger's fixture note. organization: TEST_ORG, ...overrides, }; @@ -275,7 +275,7 @@ describe('TimeRelativeTrigger', () => { expect(seen[0]).toMatchObject({ object: 'contracts', event: 'time_relative' }); expect(seen[0].record).toBe(seen[0].params); // The sweep queries as a system op (sees all rows, RLS-bypassing) AND - // inside its declared organization. [#16659] This assertion used to + // inside its declared organization. [commit ecdfc9411] This assertion used to // read `{ isSystem: true }` and it was pinning the defect: `isSystem` // is AUTHORIZATION and `tenantId` is TENANCY, and a sweep carrying only // the first selects across every tenant while its runs act as one. @@ -791,7 +791,7 @@ describe('TimeRelativeTriggerPlugin', () => { }); }); -// ─── The acting-organization refusal (#16659) ─────────────────────── +// ─── The acting-organization refusal (commit ecdfc9411) ───────────── // // The time-relative sweep is NOT the weaker case for carrying an organization, // it is the stronger one: it runs ELEVATED on purpose (`isSystem` — a @@ -859,7 +859,7 @@ describe('TimeRelativeTrigger — the acting-organization refusal (#16659)', () expect(job.jobs.size).toBe(0); }); - // ── the SELECTION half (#16659, F2) ─────────────────────────────────── + // ── the SELECTION half (commit ecdfc9411, F2) ───────────────────────── // // Declaring an organization bounded the RUN and left the QUERY unbounded, // so a sweep declared for A matched rows in every tenant and launched runs @@ -985,7 +985,7 @@ describe('TimeRelativeTrigger — the acting-organization refusal (#16659)', () }); it('a store that CANNOT honour the scope is reported at `error`, never answered unscoped', async () => { - // `driver-memory` refuses any call handed a tenant scope (#16589). A + // `driver-memory` refuses any call handed a tenant scope (commit 555a89cbd). A // sweep required to stay inside one organization, talking to a store // that cannot keep it there, must be LOUD — "selected nothing this // tick" and "cannot select at all" are different facts. @@ -1368,7 +1368,7 @@ describe('TimeRelativeTrigger — switched ON under `group` (#18378)', () => { it('a DECLARED organization still outranks the record — declaring narrows, it does not widen', async () => { // A declaration bounds SELECTION as well as identity, so honouring the // record over it would silently widen a flow the author scoped — the - // #16659 defect. Declaration wins, and the sweep sees one plant only. + // defect commit ecdfc9411 fixed. Declaration wins, and the sweep sees one plant only. const job = fakeJobService(); const base = tenantScopedDataEngine(twoPlants()); const engine = { diff --git a/packages/triggers/trigger-schedule/src/time-relative-trigger.ts b/packages/triggers/trigger-schedule/src/time-relative-trigger.ts index 714341b2f17..c85aadbd168 100644 --- a/packages/triggers/trigger-schedule/src/time-relative-trigger.ts +++ b/packages/triggers/trigger-schedule/src/time-relative-trigger.ts @@ -47,7 +47,7 @@ export interface TimeRelativeDataEngine { limit?: number; /** * The sweep's execution context. Two INDEPENDENT axes, and this - * sweep sets both (#16659): + * sweep sets both (commit ecdfc9411): * * - `isSystem` is AUTHORIZATION — a background sweep must see * every row the organization holds, not the RLS-scoped subset @@ -215,7 +215,7 @@ export function buildWindowWhere(desc: TimeRelativeDescriptor, window: DateWindo } /** - * [#16659] Why the engine will DROP this sweep's tenant scope for `schema`, or + * [commit ecdfc9411] Why the engine will DROP this sweep's tenant scope for `schema`, or * `null` when it will apply it. * * `Engine.buildDriverOptions` scopes a read by `context.tenantId` unless the @@ -343,7 +343,7 @@ export class TimeRelativeTrigger implements FlowTrigger { } const desc = parsed.data; - // [#16659] A time-relative sweep launches from a clock, exactly as a + // [commit ecdfc9411] A time-relative sweep launches from a clock, exactly as a // plain schedule flow does, so it owes the same declaration and takes // the same refusal. It is NOT the weaker case for carrying an // organization, it is the stronger one: the sweep runs ELEVATED @@ -362,7 +362,7 @@ export class TimeRelativeTrigger implements FlowTrigger { // elevation argument above is why the `single` case is still safe: an // unscoped `isSystem` read on a one-organization install selects that // organization's rows and the platform's NULL-tenant rows, which is - // exactly what it selected before #16659 and what the #8844 guard + // exactly what it selected before commit ecdfc9411 and what the #8844 guard // resolves beneath it. const organization = resolveBindingOrganization(binding); if (policy.requiresActingOrganization && organization === null) { @@ -415,7 +415,7 @@ export class TimeRelativeTrigger implements FlowTrigger { // author never made. const inertBecause = organization !== null ? organizationScopeIsInertFor(known) : null; if (inertBecause) { - // [#16659] ⛔ A DISCLOSURE, never a narrowing. The sweep + // [commit ecdfc9411] ⛔ A DISCLOSURE, never a narrowing. The sweep // passes `context.tenantId` unconditionally and the ENGINE // decides whether it applies; this branch re-reads the two // declarations the engine documents as its exemptions @@ -460,7 +460,7 @@ export class TimeRelativeTrigger implements FlowTrigger { // Error isolation: a sweep failure must not crash the job // runner / ticker. Log and swallow. // - // [#16659] At `error` when the logger has one, for the reason + // [commit ecdfc9411] At `error` when the logger has one, for the reason // {@link TriggerLogger.error} already states: the CLI's // boot-quiet window swallows stdout, so a `warn` here can be // the whole of what a broken sweep says and still be invisible. @@ -484,7 +484,7 @@ export class TimeRelativeTrigger implements FlowTrigger { const mode = desc.offsetDays ? `offsets [${desc.offsetDays.join(', ')}]d` : `within ${desc.withinDays}d`; - // [#16659] The organization is on the BIND line, not only in + // [commit ecdfc9411] The organization is on the BIND line, not only in // the refusal: it is now the sweep's selection scope as well as // the run's identity, so "which rows can this flow ever see" is // answerable from the boot log instead of from the metadata. @@ -524,7 +524,7 @@ export class TimeRelativeTrigger implements FlowTrigger { desc: TimeRelativeDescriptor, maxRecords: number, /** - * [#16659] The declared organization, or `null`. + * [commit ecdfc9411] The declared organization, or `null`. * * When declared it bounds this sweep TWICE, and both halves are * load-bearing: @@ -540,7 +540,7 @@ export class TimeRelativeTrigger implements FlowTrigger { * second organization to cross to there (plugin-auth's org-create * posture gate refuses one), so an * unscoped sweep is not the cross-organization task the ruling forbids - * — it is the shape a single-organization install had before #16659. + * — it is the shape a single-organization install had before commit ecdfc9411. * Under `isolated` `start()` still refuses an undeclared binding, and * with the switch off nothing binds, so `null` cannot arrive from * either. @@ -558,7 +558,7 @@ export class TimeRelativeTrigger implements FlowTrigger { * Only `'per-record'` changes behaviour here, and only while * `organization === null`: an explicit declaration outranks it, because * a declaration bounds SELECTION as well as identity and silently - * widening a flow the author scoped would be the #16659 defect again. + * widening a flow the author scoped would be the defect commit ecdfc9411 fixed, again. */ ownership: ScheduledRunOwnership, callback: (ctx: AutomationContext) => Promise, @@ -582,7 +582,7 @@ export class TimeRelativeTrigger implements FlowTrigger { (await engine.find(desc.object, { where, limit: maxRecords, - // [#16659] SELECTION is scoped to the declared organization, + // [commit ecdfc9411] SELECTION is scoped to the declared organization, // not just the run that follows it. // // `isSystem` alone was the whole context here, and it made @@ -612,7 +612,7 @@ export class TimeRelativeTrigger implements FlowTrigger { // (`tenancy.enabled: false`, ADR-0066; federated, ADR-0015), // and every driver that CAN isolate then scopes, while // `driver-memory` — which cannot — refuses the call by name - // (#16589). Refusal is the correct answer for a sweep that + // (commit 555a89cbd). Refusal is the correct answer for a sweep that // is required to stay inside one organization and is talking // to a store that cannot keep it there, and it arrives as a // logged sweep failure rather than as silence. @@ -699,7 +699,7 @@ export class TimeRelativeTrigger implements FlowTrigger { record, object: desc.object, event: 'time_relative', - // [#16659] The acting organization — the same key a + // [commit ecdfc9411] The acting organization — the same key a // record-change run inherits from its triggering session, // and the one `notify-node.ts` and the run-history writer // already read.