Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions .changeset/20596-trigger-schedule-provenance-anchors.md
Original file line number Diff line number Diff line change
@@ -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.
Original file line number Diff line number Diff line change
Expand Up @@ -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',
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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' } },
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ function binding(overrides: Partial<FlowTriggerBinding> = {}): 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',
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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();

Expand Down Expand Up @@ -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);
});
Expand Down
10 changes: 5 additions & 5 deletions packages/triggers/trigger-schedule/src/schedule-trigger.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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.
*
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
*
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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.
*
Expand Down Expand Up @@ -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.
Expand All @@ -146,7 +146,7 @@ function binding(timeRelative: unknown, overrides: Partial<FlowTriggerBinding> =
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,
};
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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 = {
Expand Down
26 changes: 13 additions & 13 deletions packages/triggers/trigger-schedule/src/time-relative-trigger.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand All @@ -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) {
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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.
Expand All @@ -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.
Expand Down Expand Up @@ -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:
Expand All @@ -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.
Expand All @@ -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<void>,
Expand All @@ -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
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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.
Expand Down
Loading