From 9b0d34213b3eda2faaaf2b3097052873742032ac Mon Sep 17 00:00:00 2001 From: Claude Date: Wed, 30 Sep 2026 07:22:48 +0000 Subject: [PATCH 1/3] docs(service-automation): re-anchor the dead tracker citations to the commits and ADR that decided them Comment and docblock prose only, under packages/services/service-automation/src. 73 dead sites on 73 lines in 27 files (44 census sites, 24 test-comment sites, and 5 hyphen-joined sites the census grammar cannot see) now cite the commit in this repository's history that decided what the line describes, or ADR-0126 section 7.2 where that ADR records the decision, and say in their own words what was decided. Anchors: c5a7448d5, 9d7f7259f, ae6dcf6a4, ecdfc9411, 7307191db, 18d816a50, 815585513, f90e82024, 02b41232d, 266436a7f, e238c79f0, fa5d137ab, 1408fe385, 7901b2dd2, 8c7cca1ce; and ADR-0126 section 7.2. Three changed lines carry no dead number: two in crud-nodes.ts correct a statement that was stale when it landed (the update door already answered a unique violation with the DUPLICATE_RECORD envelope), and one in flow-activation-ledger.test.ts carries the anchor its reflowed neighbour lost. Every file keeps its line count; no code token moves. Claude-Session: https://claude.ai/code/session_01XY5uCwTjZj7884yYtyur4H Co-authored-by: Claude --- .../builtin/contained-failure-rollup.test.ts | 2 +- .../contained-failure-visibility.test.ts | 4 +- .../create-record-duplicate-code.test.ts | 4 +- .../src/builtin/crud-nodes.ts | 8 ++-- .../src/builtin/notify-node.ts | 2 +- ...ro-delivery-visibility.integration.test.ts | 12 ++--- .../builtin/screen-resume-signal-less.test.ts | 4 +- .../src/builtin/subflow-child-refusal.test.ts | 2 +- .../src/builtin/subflow-node.ts | 2 +- .../src/builtin/template-functions.test.ts | 2 +- .../src/builtin/template.ts | 16 +++---- .../src/builtin/try-catch-node.ts | 8 ++-- .../src/end-node-refused-outcome.test.ts | 2 +- .../service-automation/src/engine.test.ts | 2 +- .../services/service-automation/src/engine.ts | 46 +++++++++---------- .../src/flow-activation-ledger.test.ts | 8 ++-- .../src/flow-activation-store.ts | 4 +- ...field-expression-scale.integration.test.ts | 4 +- .../service-automation/src/flow-precedence.ts | 2 +- .../services/service-automation/src/index.ts | 2 +- .../initial-completion-history-throw.test.ts | 2 +- .../src/initial-failure-history-throw.test.ts | 2 +- .../src/input-schema-retry-parity.test.ts | 2 +- .../src/stale-hot-consumed-suspension.test.ts | 2 +- .../src/suspended-run-store.test.ts | 2 +- .../src/suspended-run-store.ts | 4 +- .../src/sys-automation-run.object.ts | 2 +- 27 files changed, 76 insertions(+), 76 deletions(-) diff --git a/packages/services/service-automation/src/builtin/contained-failure-rollup.test.ts b/packages/services/service-automation/src/builtin/contained-failure-rollup.test.ts index 0cead3d785..46269d4b2c 100644 --- a/packages/services/service-automation/src/builtin/contained-failure-rollup.test.ts +++ b/packages/services/service-automation/src/builtin/contained-failure-rollup.test.ts @@ -7,7 +7,7 @@ // failures that child CONTAINED, and the fold `failed = Σ nodes[].failures` // therefore answers "what did this run cause", subflows included. Before it, a // parent whose child lost a row read `failed: 0` while `acted` had rolled up -// all along — the misreading the run-level count exists to prevent (#13681), +// all along — the misreading the run-level count exists to prevent (commit 18d816a50), // one level up. // // The measured target these tests drive is the card's, from #15617: diff --git a/packages/services/service-automation/src/builtin/contained-failure-visibility.test.ts b/packages/services/service-automation/src/builtin/contained-failure-visibility.test.ts index b34227cd21..ff26bf97ad 100644 --- a/packages/services/service-automation/src/builtin/contained-failure-visibility.test.ts +++ b/packages/services/service-automation/src/builtin/contained-failure-visibility.test.ts @@ -1,12 +1,12 @@ // Copyright (c) 2026 ObjectStack. Licensed under the Apache-2.0 license. // -// #14456 — the visibility half of the ruled containment contract (#13681). +// #14456 — the visibility half of the ruled containment contract (commit 18d816a50). // // `loop { body: [ try_catch { try, catch } ] }` is the containment spelling for // a per-iteration failure that must not end the sweep (maintainer ruling // 2026-08-31, branch B; there is deliberately no `loop.config.onIterationError` // key). Containment already worked. What did not exist was any way to SEE what -// it contained: the measurement these tests reproduce (#13681) found a run that +// it contained: the measurement behind commit 18d816a50, reproduced here, found a run that // lost one row out of five reporting // // status=completed selected=5 acted=9 skipped=0 diff --git a/packages/services/service-automation/src/builtin/create-record-duplicate-code.test.ts b/packages/services/service-automation/src/builtin/create-record-duplicate-code.test.ts index 1f16919cb6..2181fd5328 100644 --- a/packages/services/service-automation/src/builtin/create-record-duplicate-code.test.ts +++ b/packages/services/service-automation/src/builtin/create-record-duplicate-code.test.ts @@ -1,7 +1,7 @@ // Copyright (c) 2026 ObjectStack. Licensed under the Apache-2.0 license. /** - * #14419 — `create_record` used to collapse EVERY `data.insert()` failure into + * Commit c5a7448d5 — `create_record` used to collapse EVERY `data.insert()` failure into * one opaque string (`create_record(OBJECT_NAME) failed: MESSAGE_TEXT`), so a * flow's only two error-handling primitives — `try_catch` and a `fault` edge * — could not tell "the row is already there" (`engine.insert`'s own @@ -257,7 +257,7 @@ describe('#14419 (PR #14948 patch round 1) — a stale $error.code must not leak * The tier contract review's reproduction. `try_catch`'s catch region reads * `code` off the run-wide `$error` (necessarily — see the second describe * block above), so a stale `$error` becomes a store failure misread as a - * duplicate and SWALLOWED — a different door than #14419's original bug, + * duplicate and SWALLOWED — a different door than the bug commit c5a7448d5 fixed, * the exact same failure mode, and the very thing fence 4 of the ruling of * record exists to rule out. * diff --git a/packages/services/service-automation/src/builtin/crud-nodes.ts b/packages/services/service-automation/src/builtin/crud-nodes.ts index a292a5ac91..6c14dd4ce0 100644 --- a/packages/services/service-automation/src/builtin/crud-nodes.ts +++ b/packages/services/service-automation/src/builtin/crud-nodes.ts @@ -412,7 +412,7 @@ export function registerCrudNodes(engine: AutomationEngine, ctx: PluginContext): metrics: { acted: 1 }, }; } catch (err) { - // #14419 — `engine.insert` (#14095) raises `DuplicateRecordError` + // Commit c5a7448d5 — `engine.insert` (#14095) raises `DuplicateRecordError` // for a unique-constraint violation, carrying the ADR-0112 // `code: 'DUPLICATE_RECORD'` this executor used to throw away by // folding every failure into one opaque string. Surfacing it here @@ -436,9 +436,9 @@ export function registerCrudNodes(engine: AutomationEngine, ctx: PluginContext): // dependency here via `@objectstack/spec`). // // Deliberately narrow to THIS verb: `update_record` / `delete_record` - // collapse identically, but `engine.update` still leaks the raw - // driver error (#14390, not yet fixed) — those node results have - // nothing structured to surface yet, so they are untouched here. + // collapse identically; `engine.update` gained the same `DUPLICATE_RECORD` + // envelope for a unique violation (commit 9d7f7259f), but those node results + // are untouched here — this repair was scoped to `create_record` alone. const rawCode = err && typeof err === 'object' && 'code' in err ? (err as { code?: unknown }).code diff --git a/packages/services/service-automation/src/builtin/notify-node.ts b/packages/services/service-automation/src/builtin/notify-node.ts index 2afe90db57..8826903a18 100644 --- a/packages/services/service-automation/src/builtin/notify-node.ts +++ b/packages/services/service-automation/src/builtin/notify-node.ts @@ -446,7 +446,7 @@ export function registerNotifyNode(engine: AutomationEngine, ctx: PluginContext) // Waiting for the real outcome is not on the table: a notify // node must not block a flow on a downstream channel. // - // ── `selected`: what makes a ZERO dispatch readable (#17123) ── + // ── `selected`: what makes a ZERO dispatch readable (commit ae6dcf6a4) ── // // `acted` and `unmeasuredEffect` above answer "what did this // node cause". Neither can answer "this node tried to notify diff --git a/packages/services/service-automation/src/builtin/notify-zero-delivery-visibility.integration.test.ts b/packages/services/service-automation/src/builtin/notify-zero-delivery-visibility.integration.test.ts index 4567732b2a..0290aa5191 100644 --- a/packages/services/service-automation/src/builtin/notify-zero-delivery-visibility.integration.test.ts +++ b/packages/services/service-automation/src/builtin/notify-zero-delivery-visibility.integration.test.ts @@ -1,7 +1,7 @@ // Copyright (c) 2026 ObjectStack. Licensed under the Apache-2.0 license. /** - * #17123 — a `notify` node that reached NOBODY must not read like a run that + * Commit ae6dcf6a4 — a `notify` node that reached NOBODY must not read like a run that * had nobody to reach. * * ## What was measured, and why a green suite proved nothing @@ -63,15 +63,15 @@ * context. A test that invented its own two context shapes could agree with * itself while disagreeing with both doors. * - * ## Why #16659 landing does not close this, stated as a measurement + * ## Why commit ecdfc9411 does not close this, stated as a measurement * - * #16659 makes a scheduled flow carry its organization. That stops ONE cause + * Commit ecdfc9411 makes a scheduled flow carry its organization. That stops ONE cause * of a zero delivery; it does not make a zero delivery visible. The schedule * arm here is therefore driven in BOTH shapes: * * - `cronTickToday()` — no organization, the shape production builds now; * - `cronTickWithOrg()` — carrying the PLATFORM organization, the shape a - * scheduled flow has once #16659 lands. + * scheduled flow takes under commit ecdfc9411. * * On a multi-organization install the platform organization is not where the * recipients live, so the org-scoped `role:` expansion @@ -142,7 +142,7 @@ function cronTickToday(): AutomationContext { } /** - * `type: 'schedule'` as it fires once #16659 lands: the same context, now + * `type: 'schedule'` as it fires under commit ecdfc9411: the same context, now * carrying the organization the scheduled flow belongs to. On a * multi-organization install that is the platform organization, not the * employer one the recipients live in. @@ -411,7 +411,7 @@ describe.each(DRIVERS)('#17123 zero-delivery is distinguishable [driver=%s]', (k it('DIFFERENTIAL CONTROL: the two trigger families no longer render the same run', async () => { stack = await boot(kind); - // Family 1 — the cron tick, in the shape #16659 gives it. The platform + // Family 1 — the cron tick, in the shape commit ecdfc9411 gives it. The platform // organization has no admin members, so the org-scoped `role:` expansion // resolves to nobody and `emit()` returns delivered 0 / enqueued 0. const scheduled = await stack.engine.execute('nudge', cronTickWithOrg()); diff --git a/packages/services/service-automation/src/builtin/screen-resume-signal-less.test.ts b/packages/services/service-automation/src/builtin/screen-resume-signal-less.test.ts index 18829d779c..c7849ae7fb 100644 --- a/packages/services/service-automation/src/builtin/screen-resume-signal-less.test.ts +++ b/packages/services/service-automation/src/builtin/screen-resume-signal-less.test.ts @@ -2,7 +2,7 @@ /** * A signal-less `resume(runId)` is held to the suspended screen's declared - * field contract exactly like a signal-carrying one (#13648). + * field contract exactly like a signal-carrying one (commit 7307191db). * * `refuseInvalidScreenInput` (#4477) used to open with `if (!signal) return * null;` — so `resume(runId, { variables: {} })` was refused with @@ -301,7 +301,7 @@ describe("engine-built continuation stays exempt — the flag is the ONLY exempt const degraded = records.filter((r) => r.message.includes(DEGRADED_SENTENCE)); expect(degraded).toHaveLength(1); - // The level does NOT move (#13398-class): this branch keeps `warn`. + // The level does NOT move (the published-sink ruling, commit e238c79f0): this branch keeps `warn`. expect(degraded[0].level).toBe('warn'); expect(degraded[0].message).toContain(child.runId); expect(degraded[0].message).toContain(parentRunId); diff --git a/packages/services/service-automation/src/builtin/subflow-child-refusal.test.ts b/packages/services/service-automation/src/builtin/subflow-child-refusal.test.ts index a6278fd9c0..96a7bd09dd 100644 --- a/packages/services/service-automation/src/builtin/subflow-child-refusal.test.ts +++ b/packages/services/service-automation/src/builtin/subflow-child-refusal.test.ts @@ -209,7 +209,7 @@ describe('subflow delegation: a child REFUSAL is answered as a refusal (#14379)' }); it('refuses the signal-less gesture the same way, both pauses intact', async () => { - // #13648 normalises an absent signal to `{}` at the public door, so + // Commit 7307191db normalises an absent signal to `{}` at the public door, so // `resume(parentRunId)` lands on this same delegation path. const [parentRunId, childRunId] = await startPair(); diff --git a/packages/services/service-automation/src/builtin/subflow-node.ts b/packages/services/service-automation/src/builtin/subflow-node.ts index 01e78c782e..4c246c6207 100644 --- a/packages/services/service-automation/src/builtin/subflow-node.ts +++ b/packages/services/service-automation/src/builtin/subflow-node.ts @@ -170,7 +170,7 @@ export function registerSubflowNode(engine: AutomationEngine, ctx: PluginContext // live in the child's log, so until this slot existed the parent's fold // could not see them and a parent whose child lost a row read // `failed: 0` — the misreading the run-level `failed` was added to - // prevent (#13681), one level up. Rolled up here, it folds into this + // prevent (commit 18d816a50), one level up. Rolled up here, it folds into this // node's `failures` and so into the run-level `failed`. // // Deliberately the SAME exit the three totals above already leave by, so diff --git a/packages/services/service-automation/src/builtin/template-functions.test.ts b/packages/services/service-automation/src/builtin/template-functions.test.ts index 11251be21f..3e925956a6 100644 --- a/packages/services/service-automation/src/builtin/template-functions.test.ts +++ b/packages/services/service-automation/src/builtin/template-functions.test.ts @@ -1,7 +1,7 @@ // Copyright (c) 2026 ObjectStack. Licensed under the Apache-2.0 license. /** - * #11060 — the flow VALUE-expression function table, both halves of the ruling: + * Commit 815585513 — the flow VALUE-expression function table, both halves of the ruling: * * 1. `round` / `floor` / `ceil` / `abs` / `min` / `max` work in value * expressions, every name and semantic mirrored **1:1 from the CEL diff --git a/packages/services/service-automation/src/builtin/template.ts b/packages/services/service-automation/src/builtin/template.ts index 1a8c6a1058..bb05ebc9fd 100644 --- a/packages/services/service-automation/src/builtin/template.ts +++ b/packages/services/service-automation/src/builtin/template.ts @@ -16,13 +16,13 @@ * {round(x)} {floor(x)} {ceil(x)} * {abs(x)} {min(a, b)} {max(a, b)} * → the CEL stdlib's numeric six, names and - * semantics mirrored 1:1 (#11060 — see + * semantics mirrored 1:1 (commit 815585513 — see * KNOWN_EXPRESSION_FUNCTIONS below) * * Anything that fails to resolve becomes the literal `null` value (for * single-token templates) or the empty string (for embedded substitution), * matching the behavior of common low-code formula engines — with ONE loud - * exception (#11060): an identifier in CALL position (`name(…)`) that is not a + * exception (commit 815585513): an identifier in CALL position (`name(…)`) that is not a * supported function throws {@link FlowExpressionFunctionError} instead of * being rewritten to `null`. Before that diagnostic, `ROUND(…)` / * `Math.round(…)` / `(x).toFixed(2)` all compiled to `null(…)`, the TypeError @@ -42,11 +42,11 @@ import { markGuardRefusal } from '../guard-refusal.js'; export type VariableMap = Map; /** - * A function-shaped defect in a flow VALUE expression (#11060) — an unknown + * A function-shaped defect in a flow VALUE expression (commit 815585513) — an unknown * name in call position, a supported name called at the wrong arity, or an * argument outside the function's domain. * - * This is the LOUD half of the #11060 ruling: the silent-`null` rewrite of + * This is the LOUD half of the ruling commit 815585513 records: the silent-`null` rewrite of * unknown identifiers hid every one of these as an `undefined` field write. * The error is a guard refusal (#3863) — the metadata (the authored * expression) is wrong, re-running the flow unchanged can never succeed, and @@ -68,7 +68,7 @@ export class FlowExpressionFunctionError extends Error { } /** - * The value-expression function table (#11060) — maintainer ruling 2026-08-23: + * The value-expression function table (commit 815585513) — maintainer ruling 2026-08-23: * exactly `round` / `floor` / `ceil` / `abs` / `min` / `max`, every name and * semantic mirrored **1:1 from the CEL stdlib** (`@objectstack/formula` * `src/stdlib.ts`, "Numbers" block), ⛔ no second semantics invented. A parity @@ -113,7 +113,7 @@ function requireArity(fn: string, args: unknown[]): void { if (args.length !== want) { // cel-js refuses the same call with "no matching overload" — same // outcome, message written for self-correction (ADR-0032 §1d). The - // `round(x, 2)` precision form is THE anticipated misuse (#11060), so + // `round(x, 2)` precision form is THE anticipated misuse (commit 815585513), so // its refusal carries the supported spelling. const precisionHint = fn === 'round' && args.length === 2 ? ' There is no precision form — the CEL stdlib\'s round() is integer-only; for N-decimal rounding write round(x * 100) / 100.0 (scale 2). Keep the decimal point: in CEL round() returns an int and int / int is integer division, so / 100 drops the decimals there, while / 100.0 is right in both dialects.' @@ -281,7 +281,7 @@ function resolveToken(token: string, variables: VariableMap, context: Automation // Don't substitute reserved literals if (match === 'true' || match === 'false' || match === 'null' || match === 'undefined') return match; // CALL position (`name(…)`) resolves against the function table, never - // against flow variables (#11060). A known name stays literal — it is + // against flow variables (commit 815585513). A known name stays literal — it is // bound as a Function parameter below. An unknown one is the loud half // of the ruling: refuse with a named error instead of the old `null` // rewrite, whose swallowed TypeError wrote the field as `undefined`. @@ -307,7 +307,7 @@ function resolveToken(token: string, variables: VariableMap, context: Automation } catch (err) { // The named diagnostics (arity / domain, thrown inside a table // function) must escape — swallowing them here would re-create the - // exact silence #11060 removes. Everything else (junk syntax after + // exact silence commit 815585513 removed. Everything else (junk syntax after // substitution) keeps the documented fail-soft contract. if (err instanceof FlowExpressionFunctionError) throw err; return undefined; diff --git a/packages/services/service-automation/src/builtin/try-catch-node.ts b/packages/services/service-automation/src/builtin/try-catch-node.ts index c4ed5b3078..553b265531 100644 --- a/packages/services/service-automation/src/builtin/try-catch-node.ts +++ b/packages/services/service-automation/src/builtin/try-catch-node.ts @@ -17,7 +17,7 @@ import { attachPartialSteps } from '../partial-steps.js'; * `retry` policy re-runs the `try` region with exponential backoff. If the * region still fails after retries, the optional `catch` region runs with the * caught error bound to `errorVariable` (default `$error`) — `{ nodeId, - * message }`, plus `code` (#14419) when the failing node's own result set a + * message }`, plus `code` (commit c5a7448d5) when the failing node's own result set a * platform-classified one (e.g. `create_record`'s `DUPLICATE_RECORD`), so the * catch region can branch on `{$error.code}` instead of only ever seeing a * string, plus `iteration` and `item` (#14456) when the container is running @@ -137,7 +137,7 @@ export function registerTryCatchNode(engine: AutomationEngine, ctx: PluginContex // container still reports `success` when it recovers. Only the record of // what happened changes. let lastError = 'unknown error'; - // #14419 — the classified `code` (ADR-0112 `StandardErrorCode`, e.g. + // Commit c5a7448d5 — the classified `code` (ADR-0112 `StandardErrorCode`, e.g. // `DUPLICATE_RECORD`) of whichever node inside the try region last // failed, when that node's executor set one. Captured from `$error` // (below) rather than from the caught exception itself: a node that @@ -148,7 +148,7 @@ export function registerTryCatchNode(engine: AutomationEngine, ctx: PluginContex // either the next retry attempt or this executor's own `errorVariable` // write (below) can shadow it. Without this, a `try_catch`'s catch // region could never distinguish "the row is already there" from any - // other failure — the whole point of #14419. + // other failure — the whole point of commit c5a7448d5. let lastErrorCode: string | undefined; const failedAttemptSteps: StepLogEntry[] = []; for (let attempt = 0; attempt <= maxRetries; attempt++) { @@ -257,7 +257,7 @@ export function registerTryCatchNode(engine: AutomationEngine, ctx: PluginContex // WHICH thing, and `message` names one only when it happens to echo // the template. // - // `code` (#14419) is bound alongside and IS declared on + // `code` (commit c5a7448d5) is bound alongside and IS declared on // `TryCatchErrorValueSchema` — an open, optional `string` whose // docblock in `packages/spec/src/automation/control-flow.zod.ts` // states why the type stays open rather than closing over diff --git a/packages/services/service-automation/src/end-node-refused-outcome.test.ts b/packages/services/service-automation/src/end-node-refused-outcome.test.ts index 56935aec6d..e3996301b8 100644 --- a/packages/services/service-automation/src/end-node-refused-outcome.test.ts +++ b/packages/services/service-automation/src/end-node-refused-outcome.test.ts @@ -330,7 +330,7 @@ describe('#15788 — one interpolator, not a second template engine', () => { 'first={record.tags.0}', // Context token, resolved from `AutomationContext`, not from variables. 'by {$User.Id}', - // The CEL-mirrored numeric stdlib (#11060) — nothing a naive + // The CEL-mirrored numeric stdlib (commit 815585513) — nothing a naive // substitution implements. 'score {round(record.score)}', // Unresolvable embedded token renders as the empty string, not the diff --git a/packages/services/service-automation/src/engine.test.ts b/packages/services/service-automation/src/engine.test.ts index 5b92d3af82..d3a2443bbc 100644 --- a/packages/services/service-automation/src/engine.test.ts +++ b/packages/services/service-automation/src/engine.test.ts @@ -1480,7 +1480,7 @@ describe('AutomationEngine - Execution History', () => { * assert the opposite — that unregistering a flow FORGOT it had been * switched off, so re-registering it came back enabled. That was a * faithful pin of the retired `flowEnabled` map: an in-process bit with - * no durable home, which is exactly the mechanism #10243 measured + * no durable home, which is exactly the mechanism commit 02b41232d measured * leaking and ADR-0126 §7.2 retires. * * Under the activation ledger the answer inverts, and it is ADR-0126 §6 diff --git a/packages/services/service-automation/src/engine.ts b/packages/services/service-automation/src/engine.ts index 1aea3ac4b6..26e9be7dc1 100644 --- a/packages/services/service-automation/src/engine.ts +++ b/packages/services/service-automation/src/engine.ts @@ -351,7 +351,7 @@ export interface NodeExecutionResult { output?: Record; error?: string; /** - * #14419 — the platform's own classified failure code (ADR-0112 + * Commit c5a7448d5 — the platform's own classified failure code (ADR-0112 * `StandardErrorCode`, e.g. `DUPLICATE_RECORD`), when the executor chose * to surface one. `error` stays the human-readable sentence a run log or * a notification renders; `code` is what a `try_catch` catch region or a @@ -359,7 +359,7 @@ export interface NodeExecutionResult { * "the row is already there" apart from "the store is down" or any other * reason the same string-shaped `error` could otherwise describe. * Optional, and deliberately narrow per executor rather than "any - * platform envelope, forwarded wholesale": `create_record` (#14419) is + * platform envelope, forwarded wholesale": `create_record` (commit c5a7448d5) is * the only executor that sets it today, and only for the one code its * repair was scoped to — `DUPLICATE_RECORD` — not any code a driver * error might someday carry unaudited. An executor that never classifies @@ -526,7 +526,7 @@ export interface FlowTriggerBinding { /** schedule: cron/interval descriptor (parsed but not yet acted on here). */ readonly schedule?: unknown; /** - * [#16659] schedule / time_relative: the ACTING ORGANIZATION the flow + * [commit ecdfc9411] schedule / time_relative: the ACTING ORGANIZATION the flow * declares on its start node (`config.organization`), resolved through * `@objectstack/spec`'s {@link resolveScheduleOrganization} so authoring, * this lift and the triggers cannot disagree about what counts as declared. @@ -1399,7 +1399,7 @@ function isEngineVariable(name: string): boolean { * ({@link ENGINE_BUILT_SIGNAL}) is exempt: `bubbleToParent` legitimately * writes the handoff keys, and it is not reachable from a transport. The * signal is never absent here — `resume` normalises a missing one to `{}` - * (#13648), which folds nothing and rejects nothing. + * (commit 7307191db), which folds nothing and rejects nothing. */ function applyResumeSignal( variables: Map, @@ -2109,14 +2109,14 @@ export interface FlowActivationRow { /** * [ADR-0126 §7.2] The durable off-switch for packaged flows — the mechanism - * that REPLACES the process-local `flowEnabled` map #10243 measured leaking + * that REPLACES the process-local `flowEnabled` map commit 02b41232d measured leaking * across tenants. * * Backed by `sys_metadata_activation` in production (see * `ObjectStoreFlowActivationStore`), so a disabled packaged flow stays * disabled across a restart — the property the retired in-process map could * not have, and the one that made its "mitigating but not exculpating" cold - * boot the only thing limiting the #10243 leak. + * boot the only thing limiting the leak commit 02b41232d measured. * * Absence of a row means the packaged default — ACTIVE — so an engine with no * store attached, or a store with no rows, behaves exactly as a stock boot @@ -2312,7 +2312,7 @@ export class AutomationEngine implements IAutomationService { * * ## ⛔ This is NOT the retired `flowEnabled` map under a new name * - * That distinction is the whole point of #10243, so it is spelled out + * That distinction is the whole point of ADR-0126 §7.2, so it is spelled out * rather than left to a reader's charity. The retired map was the TRUTH: * `toggleFlow` wrote it and nothing else recorded the bit, so the * off-switch was a name-keyed, unscoped, process-local value that one @@ -2328,7 +2328,7 @@ export class AutomationEngine implements IAutomationService { * 2. **The write door is gated.** Reaching `toggleFlow` from the wire * goes through the automation domain's activation gate: in `group` / * `isolated` postures the write requires the platform operator, so - * the tenant-org-admin caller #10243 measured is refused before any + * the tenant-org-admin caller commit 02b41232d measured is refused before any * of this runs (ADR-0126 §5). * 3. **It survives a restart** — because the row does. The retired map's * cold-boot amnesia was recorded as "mitigating but not exculpating"; @@ -3562,7 +3562,7 @@ export class AutomationEngine implements IAutomationService { ? config.objectName : undefined, schedule: config.schedule, - // [#16659] Lifted beside `schedule`, for the same + // [commit ecdfc9411] Lifted beside `schedule`, for the same // reason `schedule` is lifted: it is a BINDING fact the // trigger acts on, not a config value it interprets. // `config` still carries it verbatim below, so a @@ -3578,7 +3578,7 @@ export class AutomationEngine implements IAutomationService { case 'schedule': return { triggerType: kind, - // [#16659] `organization` rides beside `schedule`: the two + // [commit ecdfc9411] `organization` rides beside `schedule`: the two // together ARE a scheduled flow's binding — when it fires, // and which organization it fires as. binding: { @@ -5221,7 +5221,7 @@ export class AutomationEngine implements IAutomationService { * ## What changed, and why the durable write is inside this method * * This used to set a process-local map and nothing else, which is the - * mechanism #10243 measured leaking across tenants. It now writes the + * mechanism commit 02b41232d measured leaking across tenants. It now writes the * `sys_metadata_activation` row FIRST and updates the in-process * projection only after that write returns. Putting the durable write here * — rather than in the HTTP route that calls it — is deliberate: this is @@ -5298,7 +5298,7 @@ export class AutomationEngine implements IAutomationService { // Degrading to in-process is a legitimate mode for a host with no // ObjectQL — degrading to it while REPORTING durability is not // (the posture this package already takes for suspended runs). - // Note the #10243 leak is closed by the route's authority gate, + // Note the leak commit 02b41232d measured is closed by the route's authority gate, // not by durability, so this degraded mode is not that leak. this.logger.warn( `[Automation] flow '${name}' ${enabled ? 'enabled' : 'disabled'} IN PROCESS ONLY — no activation ledger is ` + @@ -6180,7 +6180,7 @@ export class AutomationEngine implements IAutomationService { // failed write. // // ⚠️ The level is the precedent's (#16273, #15555) and is NOT - // a #13398-class raise: that ruling forbids raising a site to + // a raise under the published-sink ruling (commit e238c79f0): that ruling forbids raising a site to // `error` where doing so means GROWING `error?` onto a // published sink that lacks it, and this sink — `Logger` from // `@objectstack/spec/contracts` — declares `error(message, @@ -6377,7 +6377,7 @@ export class AutomationEngine implements IAutomationService { // `recordLog` is in `DURABILITY_CRITICAL_CALLEES`. // // ⚠️ The level is the precedent's (#15555, #16273, #16274) and - // is NOT a #13398-class raise: that ruling forbids raising a + // is NOT a raise under the published-sink ruling (commit e238c79f0): that ruling forbids raising a // site to `error` where doing so means GROWING `error?` onto a // published sink that lacks it, and this sink — `Logger` from // `@objectstack/spec/contracts` — declares `error(message, @@ -6561,7 +6561,7 @@ export class AutomationEngine implements IAutomationService { const refusal = await this.refuseGatedResume(runId, signal); if (refusal) return refusal; // An ABSENT signal is an EMPTY caller submission, never an exemption - // (#13648). This is the in-process door, and `resume(runId)` used to + // (commit 7307191db). This is the in-process door, and `resume(runId)` used to // skip the screen contract that `resume(runId, {})` is held to: // `refuseInvalidScreenInput` short-circuited on a falsy signal — a // second, unnamed spelling of the exemption the engine already states @@ -7069,7 +7069,7 @@ export class AutomationEngine implements IAutomationService { private async resumeInternal( runId: string, // Never `undefined` past the public door: `resume` normalises an - // absent caller signal to `{}` (#13648), and the engine's own + // absent caller signal to `{}` (commit 7307191db), and the engine's own // continuations (subflow delegation / up-bubble, `map` re-entry) // always hand over a built signal. Typed so, the chokepoints below // cannot grow a falsy-signal branch again. @@ -7981,7 +7981,7 @@ export class AutomationEngine implements IAutomationService { * `map` item handoff are the engine's own continuations; they carry * author-named output variables, not a screen submission. This is the * ONLY exemption, and it is spelled once: an absent signal is not a - * case here — `resume` normalises it to `{}` (#13648) — because a bare + * case here — `resume` normalises it to `{}` (commit 7307191db) — because a bare * `if (!signal)` beside the flag was a second, unnamed spelling of the * same exemption that let `resume(runId)` skip every `required` the * author wrote. @@ -10783,7 +10783,7 @@ export class AutomationEngine implements IAutomationService { // {@link AutomationEngine.runRegion}), so EVERY thrown failure // inside a region left `$error` naming an earlier, unrelated // failure. The first reader that cared about `$error`'s freshness - // — `try_catch`'s `code` binding (#14419) — met it immediately + // — `try_catch`'s `code` binding (commit c5a7448d5) — met it immediately // and bound a message and a code that came from two different // failures: `{ code: 'DUPLICATE_RECORD', message: "Node 'mk' // timed out after 20ms" }`, swallowed by a catch region reading @@ -10893,7 +10893,7 @@ export class AutomationEngine implements IAutomationService { } // Write error output to variable context for downstream nodes. - // #14419 — carry the executor's classified `code` (when it set + // Commit c5a7448d5 — carry the executor's classified `code` (when it set // one) alongside `message`, so a `fault` edge handler reading // `{$error.code}` can branch on it; a `try_catch` region reads // this same node-level `$error` before its OWN `errorVariable` @@ -12317,7 +12317,7 @@ export class AutomationEngine implements IAutomationService { // and the same chokepoint discipline applies: one shape, both // attempt paths. // - // The reachable statements, the invariant and the #13398 reading + // The reachable statements, the invariant and the sink ruling's (commit e238c79f0) reading // are all stated at the `execute()` site; this is the same guard, // not a second design. let logged: ExecutionLogEntry | undefined; @@ -12336,7 +12336,7 @@ export class AutomationEngine implements IAutomationService { }, context); } catch (bookkeeping) { // #4632 verdict: DURABILITY, so `error` — see the `execute()` - // site for why this is outside #13398's class. The message + // site for why this is outside the sink ruling's (commit e238c79f0) class. The message // names the ATTEMPT, because the run id an operator finds in // the Runs surfaces is this attempt's own and not the failed // attempt's. Said ONCE per run, not once per failed write. @@ -12475,7 +12475,7 @@ export class AutomationEngine implements IAutomationService { const durationMs = Date.now() - startTime; // [#17562] The SECOND initial-execution instance of the guard above // in `execute()` — this path's own failure arm, one per RETRY - // attempt. The reachable statements, the invariant and the #13398 + // attempt. The reachable statements, the invariant and the sink ruling's (commit e238c79f0) // reading are all stated at the `execute()` site; this is the same // guard, not a second design. // @@ -12507,7 +12507,7 @@ export class AutomationEngine implements IAutomationService { }, context); } catch (bookkeeping) { // #4632 verdict: DURABILITY, so `error` — see the `execute()` - // site for why this is outside #13398's class and why a failure + // site for why this is outside the sink ruling's (commit e238c79f0) class and why a failure // handed to the caller does not exempt it. The message names // the ATTEMPT, because the run id an operator finds in the Runs // surfaces is this attempt's own and not the first attempt's. diff --git a/packages/services/service-automation/src/flow-activation-ledger.test.ts b/packages/services/service-automation/src/flow-activation-ledger.test.ts index 31e6caff87..e78e505fa3 100644 --- a/packages/services/service-automation/src/flow-activation-ledger.test.ts +++ b/packages/services/service-automation/src/flow-activation-ledger.test.ts @@ -7,7 +7,7 @@ // WHAT THIS REPLACES, AND WHY THE REPLACEMENT NEEDED TESTS OF ITS OWN // // The engine used to keep its off-switch in a process-local `flowEnabled` map. -// #10243 measured the cost: the bit was NOT a row, so no organization wall +// Commit 02b41232d measured the cost: the bit was NOT a row, so no organization wall // scoped it — `toggleFlow` wrote a name-keyed in-process map and the automation // service is ONE instance per environment, so on a real `isolated` posture a // tenant org owner switched a shipped flow off and an unrelated tenant in a @@ -268,8 +268,8 @@ describe('ADR-0126 §7.2 — the install-level row unbinds the trigger', () => { const store = new InMemoryFlowActivationStore(); await store.setActive({ name: 'f', packageId: 'crm', active: false }); - // A brand-new engine: the #10243 map's "cold boot reads enabled: true - // again" was recorded as mitigating-but-not-exculpating. It must no + // A brand-new engine: the retired map's "cold boot reads enabled: true + // again" was recorded (commit 266436a7f) as mitigating-but-not-exculpating. It must no // longer be true. const engine = new AutomationEngine(createTestLogger()); const trigger = recordingTrigger('record_change'); @@ -1021,7 +1021,7 @@ describe('ADR-0126 §4/§5 — the row this line writes', () => { }); // ───────────────────────────────────────────────────────────────────────────── -// #10243 — the retired mechanism is GONE, not shaded +// ADR-0126 §7.2 — the retired mechanism is GONE, not shaded // ───────────────────────────────────────────────────────────────────────────── describe('#10243 — the process-local `flowEnabled` map is retired', () => { diff --git a/packages/services/service-automation/src/flow-activation-store.ts b/packages/services/service-automation/src/flow-activation-store.ts index 8aaf1aa0ba..411960babe 100644 --- a/packages/services/service-automation/src/flow-activation-store.ts +++ b/packages/services/service-automation/src/flow-activation-store.ts @@ -15,7 +15,7 @@ import type { FlowActivationStore } from './engine.js'; * ## What this replaces, and why the replacement is durable * * The engine used to carry its off-switch in a process-local `flowEnabled` - * map. #10243 measured what that costs: the bit was NOT a row, so no + * map. Commit 02b41232d measured what that costs: the bit was NOT a row, so no * organization wall scoped it — `toggleFlow` wrote an in-process map keyed by * flow NAME only, and the automation service is ONE instance per environment. * On a real `isolated` posture a tenant org owner switched a shipped flow off @@ -64,7 +64,7 @@ export type FlowActivationStoreEngine = MetadataActivationStoreEngine; * In-memory {@link FlowActivationStore} — process-lifetime only. * * ⚠️ This is NOT the retired `flowEnabled` map wearing a new name. The - * difference is the one #10243 turned on: this store is only ever reached + * difference is the one the toggle ruling (commit 266436a7f) turned on: this store is only ever reached * through {@link AutomationEngine.toggleFlow}, which is reached from the wire * only through a door that refuses a tenant admin in a walled posture * (ADR-0126 §5). What it lacks versus the ObjectStore implementation is diff --git a/packages/services/service-automation/src/flow-field-expression-scale.integration.test.ts b/packages/services/service-automation/src/flow-field-expression-scale.integration.test.ts index 44b70d62fa..b309301f06 100644 --- a/packages/services/service-automation/src/flow-field-expression-scale.integration.test.ts +++ b/packages/services/service-automation/src/flow-field-expression-scale.integration.test.ts @@ -1,7 +1,7 @@ // Copyright (c) 2026 ObjectStack. Licensed under the Apache-2.0 license. /** - * #11060 end-to-end oracle — the hotcrm quote-flow shape (hotcrm#1206), + * Commit 815585513's end-to-end oracle — the hotcrm quote-flow shape (hotcrm#1206), * reproduced in-tree because that repo is out of reach from here: a flow * computes a discounted money value (`180000 * (1 - 30/100)` = * `125999.99999999999`) and writes it into a `scale: 2` field. @@ -186,7 +186,7 @@ describe('flow-computed money lands within its declared scale (#11060, oracle fo const dump = JSON.stringify(res); expect(dump).toContain("unknown function 'ROUND'"); expect(dump).toContain('round'); // the did-you-mean prescription travels with the failure - // Nothing persisted — before #11060 this wrote the field as undefined. + // Nothing persisted — before commit 815585513 this wrote the field as undefined. expect(await quoteByTitle('shouty')).toBeFalsy(); }); }); diff --git a/packages/services/service-automation/src/flow-precedence.ts b/packages/services/service-automation/src/flow-precedence.ts index 9ce7ab3a75..4da74e1967 100644 --- a/packages/services/service-automation/src/flow-precedence.ts +++ b/packages/services/service-automation/src/flow-precedence.ts @@ -45,7 +45,7 @@ // larger change, and ADR-0048 does not ask for it — the ADR's answer for this // case is a precedence plus a warning, both of which are here. -// [#10062] From `@objectstack/metadata-core`, which this package DECLARES — +// [commit fa5d137ab] From `@objectstack/metadata-core`, which this package DECLARES — // not from `@objectstack/objectql`, which it does not. The predicate is the // same one (it was sunk into metadata-core and objectql re-exports it), but the // import used to be an undeclared workspace dependency, and because the shared diff --git a/packages/services/service-automation/src/index.ts b/packages/services/service-automation/src/index.ts index 025bbd5218..2161e4dc16 100644 --- a/packages/services/service-automation/src/index.ts +++ b/packages/services/service-automation/src/index.ts @@ -30,7 +30,7 @@ export type { FlowDispatchOutcome, // [ADR-0126 §7.2] The packaged-flow activation ledger port and its row — // the durable off-switch that REPLACES the retired process-local - // `flowEnabled` map (#10243). Exported so a host can supply its own + // `flowEnabled` map (the leak commit 02b41232d measured). Exported so a host can supply its own // backing store, and so the shape a consumer reads is the platform's. FlowActivationStore, FlowActivationRow, diff --git a/packages/services/service-automation/src/initial-completion-history-throw.test.ts b/packages/services/service-automation/src/initial-completion-history-throw.test.ts index 488651a7ba..0384b08b31 100644 --- a/packages/services/service-automation/src/initial-completion-history-throw.test.ts +++ b/packages/services/service-automation/src/initial-completion-history-throw.test.ts @@ -286,7 +286,7 @@ describe('#16274 — a completed run must not be failed, and never re-run, by it // run — the judgment question answers YES, so `error`, with the // consequence and the fix in the first line. // - // ⚠️ And NOT a #13398-class raise: `Logger` from + // ⚠️ And NOT a raise under the published-sink ruling (commit e238c79f0): `Logger` from // `@objectstack/spec/contracts` declares `error` as a REQUIRED member, // so nothing is grown onto a published sink that lacks it. const { engine, errors, flowName } = boot({ retry: false, store: new SyncThrowTerminalStore() }); diff --git a/packages/services/service-automation/src/initial-failure-history-throw.test.ts b/packages/services/service-automation/src/initial-failure-history-throw.test.ts index 8c743cbd32..3bddbb34a8 100644 --- a/packages/services/service-automation/src/initial-failure-history-throw.test.ts +++ b/packages/services/service-automation/src/initial-failure-history-throw.test.ts @@ -296,7 +296,7 @@ describe('#17562 — a failed run is still ANSWERED when its own history write t // CALLER is not a degradation"): what the caller is handed is the NODE's // failure. The bookkeeping failure is handed to nobody. // - // ⚠️ And NOT a #13398-class raise: `Logger` from + // ⚠️ And NOT a raise under the published-sink ruling (commit e238c79f0): `Logger` from // `@objectstack/spec/contracts` declares `error(message, error?, meta?)` // as a REQUIRED member, so nothing is grown onto a sink that lacks it. const store = new SyncThrowTerminalStore(); diff --git a/packages/services/service-automation/src/input-schema-retry-parity.test.ts b/packages/services/service-automation/src/input-schema-retry-parity.test.ts index ba0023438b..56c26e326e 100644 --- a/packages/services/service-automation/src/input-schema-retry-parity.test.ts +++ b/packages/services/service-automation/src/input-schema-retry-parity.test.ts @@ -28,7 +28,7 @@ import { AutomationEngine } from './engine.js'; * NON-RETRYABLE and classifies as a NEVER-DISPATCHED exit under #9378, beside * `FLOW_DISABLED` / `FLOW_NO_START_NODE`. So the refusal is asserted as * `success: false` + its own ADR-0112 `code` (`FLOW_INPUT_SCHEMA_INVALID`, - * registered by #11504) + the guard's own message, with `status` ABSENT — + * registered by commit f90e82024) + the guard's own message, with `status` ABSENT — * that absence is the transport's discriminator, so an edit that stamps * `'failed'` on this exit "for consistency" must fail here. `execute()` * refuses ONCE and never hands the throw to `retryExecution`: one refusal diff --git a/packages/services/service-automation/src/stale-hot-consumed-suspension.test.ts b/packages/services/service-automation/src/stale-hot-consumed-suspension.test.ts index 6f30c55b59..06c9c2501f 100644 --- a/packages/services/service-automation/src/stale-hot-consumed-suspension.test.ts +++ b/packages/services/service-automation/src/stale-hot-consumed-suspension.test.ts @@ -1,7 +1,7 @@ // Copyright (c) 2026 ObjectStack. Licensed under the Apache-2.0 license. /** - * #16709 item 1 — the restore verb DROPS a hot copy the durable row proves + * Commit 8c7cca1ce, item 1 — the restore verb DROPS a hot copy the durable row proves * stale, and this file is the only thing that observes it. * * ## The gap this closes, measured diff --git a/packages/services/service-automation/src/suspended-run-store.test.ts b/packages/services/service-automation/src/suspended-run-store.test.ts index b92ccdc5e0..fda55608a8 100644 --- a/packages/services/service-automation/src/suspended-run-store.test.ts +++ b/packages/services/service-automation/src/suspended-run-store.test.ts @@ -901,7 +901,7 @@ describe('ObjectStoreSuspendedRunStore — organization attribution (cloud#1395) it('⛔ pins the sys_api_key divergence: the stamp column is the DECLARED active_organization_id, not the wall', async () => { // The credential table: unwalled by necessity (`enabled: false`, // #8287) while its rows are still ABOUT one organization under - // `tenancy.organizationField` (#8778). A flow triggered by an api-key + // `tenancy.organizationField` (commit 7901b2dd2). A flow triggered by an api-key // record must file its run under the org the key authenticates into — // limb 0 of the shared resolver, winning over the ADR-0066 opt-out. const engine = createFakeEngine({ diff --git a/packages/services/service-automation/src/suspended-run-store.ts b/packages/services/service-automation/src/suspended-run-store.ts index ccdc8bf7bd..08ec780db5 100644 --- a/packages/services/service-automation/src/suspended-run-store.ts +++ b/packages/services/service-automation/src/suspended-run-store.ts @@ -711,7 +711,7 @@ export class ObjectStoreSuspendedRunStore implements SuspendedRunStore { // inputs and same precedence as `serialize()` below, so a run's paused // row and its terminal row agree by construction. // - // [#16659] This used to end "a plain scheduled sweep has neither and + // [commit ecdfc9411] This used to end "a plain scheduled sweep has neither and // keeps NULL", and that stopped being true when a time-triggered flow // began declaring the organization it runs as: such a sweep now arrives // with `record.organizationId` set, so the second limb answers and the @@ -948,7 +948,7 @@ export class ObjectStoreSuspendedRunStore implements SuspendedRunStore { // before this every run they produced persisted `organization_id = NULL` // while `trigger_object` / `trigger_record_id` on the very same row named // a record that DOES belong to a customer. It is the same subject-first - // precedence `sys_audit_log`'s writer already stamped with (#8707 + // precedence `sys_audit_log`'s writer already stamped with (commit 1408fe385 // honouring #8287's ruling) — three platform side tables, one answer now. // // The fallback still stands, and still matters: an object with no diff --git a/packages/services/service-automation/src/sys-automation-run.object.ts b/packages/services/service-automation/src/sys-automation-run.object.ts index a2016b9646..bb97a4daca 100644 --- a/packages/services/service-automation/src/sys-automation-run.object.ts +++ b/packages/services/service-automation/src/sys-automation-run.object.ts @@ -109,7 +109,7 @@ export const SysAutomationRun = ObjectSchema.create({ // measured 31 of 31 rows org-less on a walled HotCRM SaaS boot — each row // naming a `trigger_object` / `trigger_record_id` that DOES belong to a // specific customer. Subject-first is what `sys_audit_log`'s writer - // already did (#8707 honouring #8287's ruling); three platform side + // already did (commit 1408fe385 honouring #8287's ruling); three platform side // tables, one answer now. A trigger with no record (a plain scheduled // sweep has no ONE subject) keeps the acting-context fallback — NULL there // stays NULL: fabricating an acting organization stays vetoed (Option C). From 3511e88cce00013c51a3571a9a44a2567812727f Mon Sep 17 00:00:00 2001 From: Claude Date: Wed, 30 Sep 2026 07:31:46 +0000 Subject: [PATCH 2/3] chore(changeset): patch for service-automation's re-anchored provenance comments The rewritten docblocks and inline comments ship in dist (index.js, index.cjs, index.d.ts, index.d.cts), so the package owes a patch note. Claude-Session: https://claude.ai/code/session_01XY5uCwTjZj7884yYtyur4H Co-authored-by: Claude --- .../20596-service-automation-provenance-anchors.md | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 .changeset/20596-service-automation-provenance-anchors.md diff --git a/.changeset/20596-service-automation-provenance-anchors.md b/.changeset/20596-service-automation-provenance-anchors.md new file mode 100644 index 0000000000..a747df1285 --- /dev/null +++ b/.changeset/20596-service-automation-provenance-anchors.md @@ -0,0 +1,11 @@ +--- +'@objectstack/service-automation': patch +--- + +Provenance comments in `service-automation` were re-anchored + +Comment and docblock lines under `src/` that cited tracker numbers which no +longer resolve on GitHub now cite the record in this repository that decided +the matter (an ADR where one exists, otherwise the commit in this repository's +history), and say in their own words what was decided. Comments only: no type, +schema, export, log or refusal text, or runtime behaviour changes. From a602c4003cd21afb3daa1a0f20fd14eb1a53499d Mon Sep 17 00:00:00 2001 From: Claude Date: Wed, 30 Sep 2026 07:57:53 +0000 Subject: [PATCH 3/3] docs(service-automation): keep the card and issue referents two rewritten test headers named Two test-file docblock headers that named a dead tracker number were rewritten to a commit; later lines in the same docblock still speak of "the card" / "the issue" that number named. Each header now names that card or issue by the commit behind it, so the later lines keep their antecedent. Both lines already carried a rewritten dead site; no other line changes and no code token moves. Claude-Session: https://claude.ai/code/session_01XY5uCwTjZj7884yYtyur4H Co-authored-by: Claude --- .../builtin/notify-zero-delivery-visibility.integration.test.ts | 2 +- .../src/flow-field-expression-scale.integration.test.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/services/service-automation/src/builtin/notify-zero-delivery-visibility.integration.test.ts b/packages/services/service-automation/src/builtin/notify-zero-delivery-visibility.integration.test.ts index 0290aa5191..168d540200 100644 --- a/packages/services/service-automation/src/builtin/notify-zero-delivery-visibility.integration.test.ts +++ b/packages/services/service-automation/src/builtin/notify-zero-delivery-visibility.integration.test.ts @@ -1,7 +1,7 @@ // Copyright (c) 2026 ObjectStack. Licensed under the Apache-2.0 license. /** - * Commit ae6dcf6a4 — a `notify` node that reached NOBODY must not read like a run that + * The card behind commit ae6dcf6a4: a `notify` node that reached NOBODY must not read like a run that * had nobody to reach. * * ## What was measured, and why a green suite proved nothing diff --git a/packages/services/service-automation/src/flow-field-expression-scale.integration.test.ts b/packages/services/service-automation/src/flow-field-expression-scale.integration.test.ts index b309301f06..b14cd830a7 100644 --- a/packages/services/service-automation/src/flow-field-expression-scale.integration.test.ts +++ b/packages/services/service-automation/src/flow-field-expression-scale.integration.test.ts @@ -1,7 +1,7 @@ // Copyright (c) 2026 ObjectStack. Licensed under the Apache-2.0 license. /** - * Commit 815585513's end-to-end oracle — the hotcrm quote-flow shape (hotcrm#1206), + * The end-to-end oracle for the issue behind commit 815585513 — the hotcrm quote-flow shape (hotcrm#1206), * reproduced in-tree because that repo is out of reach from here: a flow * computes a discounted money value (`180000 * (1 - 30/100)` = * `125999.99999999999`) and writes it into a `scale: 2` field.