You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Browse filesBrowse the repository at this point in the historyBrowse files
authored
fix(service-automation): route the last three suspended-run readers through the shared loader (#14650)
* fix(service-automation): route the three remaining suspended-run readers through the shared loader
`cancelRun`, `failAncestors` and `listSuspendedRunsDurable` still preferred the
per-process `suspendedRuns` map over the shared `SuspendedRunStore`, which
#13617 had already made authoritative for the resume path. Each now takes one
answer to "where is this run parked", with the degrading or strict loader
chosen per site so the recorded #4632 / #6299 degradation posture is preserved
by choice rather than re-derived.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AUF1NoViznQK32gqpK8wS8
* test(service-automation): import AutomationContext from its declaring module
`AutomationContext` is declared in `@objectstack/spec/contracts` and re-imported
by `engine.ts` as a type — it is not re-exported from there, so importing it
from `./engine.js` added a fourth error (TS2459) to a package whose type-check
debt ledger is frozen at 3.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AUF1NoViznQK32gqpK8wS8
---------
Co-authored-by: os-sales <sales@objectstack.ai>
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
**The last three readers of suspended-run state read the shared store, not this replica's memory of it.**#13617 made the resume path store-authoritative; `cancelRun`, `failAncestors` and `listSuspendedRunsDurable` still preferred the per-process `suspendedRuns` map, so on a replica holding a stale entry each acted on the node a run was parked at the last time THIS replica touched it. All three now take one answer to "where is this run parked", through the existing `loadSuspendedRun` / `loadSuspendedRunStrict` pair, with the degrading or strict loader chosen per site so each recorded degradation posture is preserved by choice rather than re-derived.
6
+
7
+
-**`cancelRun` — the strict loader.** Before: a stale replica cancelled from its own snapshot; the row deletion is by id and was right either way, but `forgetSuspendedRun` told the executor of the node in the SNAPSHOT that its pause was over, so the live node's pause stayed armed and a node the run had already left was released a second time. Now the shared row decides which pause is torn down. The strict loader is deliberate: "not found" still returns `false` (already terminal / unknown) exactly as before, and an unreadable store still lands on this seam's own #4632 DURABILITY record at `error` — the degrading loader would have answered `null` under its best-effort `warn` and silently downgraded that verdict. ⚠️ One consequence, stated: while a store is configured this process's map is no longer an answer, so a store outage now reaches that `error` record even for a run this replica is holding, where the old cache-first read cancelled from the local snapshot.
8
+
-**`failAncestors` — the degrading loader.** Before: a stale parent was failed at a node it had already left (#13617's own harm shape, one level up). The degrading loader is deliberate: this walk runs inside the catch arm already handling a run's failure, so it must not throw, and "a store failure reads as no ancestor here and stops the walk" is exactly the posture the bare `.catch(() => null)` had. The one thing gained beyond the fix: that silent swallow is now recorded, at the loader's declared best-effort `warn` — no new `error` seam.
9
+
-**`listSuspendedRunsDurable` — the merge direction, and the comment.** The durable row now wins an id collision; the comment claiming "In-memory entries win — they are the freshest copy" is corrected, since it is true of exactly one deployment shape. Map entries the durable listing does not carry are still included, deliberately: `store.list()` is a capped, best-effort enumeration (at most 1000 `paused` rows) and the same merge is reached on the degraded path, so "absent from the list" is not the per-id "the store answered and has no row" the strict loader rests on.
10
+
11
+
No signature, export or return-shape change on any of the three.
0 commit comments