@substrate-ai/core 0.20.6 → 0.20.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -23,10 +23,15 @@ export interface RetryableEscalationsResult {
23
23
  *
24
24
  * Key format in the DB: `{storyKey}:{runId}`
25
25
  *
26
+ * Scoping:
26
27
  * - When `runId` is provided, only decisions whose key contains that runId
27
- * are considered (AC5 scoping).
28
- * - When `runId` is omitted, the runId of the last (most recently created)
29
- * escalation-diagnosis decision is used as the default (AC1 defaulting).
28
+ * are considered (AC5). The caller is explicit; no status filter is applied
29
+ * so a user can still inspect diagnoses from a terminal run by naming it.
30
+ * - When `runId` is omitted, the latest run whose `pipeline_runs.status` is
31
+ * NOT terminal (`failed` / `stopped`) is used. Terminal runs are abandoned
32
+ * or manually stopped — their per-story diagnoses are historical noise and
33
+ * would generate false-positive retry proposals for work that either
34
+ * shipped in a later run or will never be retried in that run's lifetime.
30
35
  *
31
36
  * @param adapter The database adapter
32
37
  * @param runId Optional run ID to scope the query
@@ -1 +1 @@
1
- {"version":3,"file":"retry-escalated.d.ts","sourceRoot":"","sources":["../../../src/persistence/queries/retry-escalated.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,aAAa,CAAA;AAiBlD,MAAM,WAAW,YAAY;IAC3B,GAAG,EAAE,MAAM,CAAA;IACX,MAAM,EAAE,MAAM,CAAA;CACf;AAED,MAAM,WAAW,0BAA0B;IACzC,SAAS,EAAE,MAAM,EAAE,CAAA;IACnB,OAAO,EAAE,YAAY,EAAE,CAAA;CACxB;AAMD;;;;;;;;;;;;;GAaG;AACH,wBAAsB,uBAAuB,CAC3C,OAAO,EAAE,eAAe,EACxB,KAAK,CAAC,EAAE,MAAM,GACb,OAAO,CAAC,0BAA0B,CAAC,CAmErC"}
1
+ {"version":3,"file":"retry-escalated.d.ts","sourceRoot":"","sources":["../../../src/persistence/queries/retry-escalated.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,aAAa,CAAA;AA0BlD,MAAM,WAAW,YAAY;IAC3B,GAAG,EAAE,MAAM,CAAA;IACX,MAAM,EAAE,MAAM,CAAA;CACf;AAED,MAAM,WAAW,0BAA0B;IACzC,SAAS,EAAE,MAAM,EAAE,CAAA;IACnB,OAAO,EAAE,YAAY,EAAE,CAAA;CACxB;AAMD;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAsB,uBAAuB,CAC3C,OAAO,EAAE,eAAe,EACxB,KAAK,CAAC,EAAE,MAAM,GACb,OAAO,CAAC,0BAA0B,CAAC,CA8ErC"}
@@ -10,6 +10,13 @@
10
10
  */
11
11
  import { getDecisionsByCategory } from './decisions.js';
12
12
  import { ESCALATION_DIAGNOSIS } from '../schemas/operational.js';
13
+ /**
14
+ * Pipeline-run statuses that make escalation-diagnosis decisions non-actionable.
15
+ * Runs in these states are terminated (abandoned / stopped manually) and their
16
+ * per-story escalations are historical noise — the work either shipped in a
17
+ * later run or was never going to be retried in this run's lifetime.
18
+ */
19
+ const TERMINAL_RUN_STATUSES = ['failed', 'stopped'];
13
20
  // ---------------------------------------------------------------------------
14
21
  // Query
15
22
  // ---------------------------------------------------------------------------
@@ -19,10 +26,15 @@ import { ESCALATION_DIAGNOSIS } from '../schemas/operational.js';
19
26
  *
20
27
  * Key format in the DB: `{storyKey}:{runId}`
21
28
  *
29
+ * Scoping:
22
30
  * - When `runId` is provided, only decisions whose key contains that runId
23
- * are considered (AC5 scoping).
24
- * - When `runId` is omitted, the runId of the last (most recently created)
25
- * escalation-diagnosis decision is used as the default (AC1 defaulting).
31
+ * are considered (AC5). The caller is explicit; no status filter is applied
32
+ * so a user can still inspect diagnoses from a terminal run by naming it.
33
+ * - When `runId` is omitted, the latest run whose `pipeline_runs.status` is
34
+ * NOT terminal (`failed` / `stopped`) is used. Terminal runs are abandoned
35
+ * or manually stopped — their per-story diagnoses are historical noise and
36
+ * would generate false-positive retry proposals for work that either
37
+ * shipped in a later run or will never be retried in that run's lifetime.
26
38
  *
27
39
  * @param adapter The database adapter
28
40
  * @param runId Optional run ID to scope the query
@@ -53,9 +65,21 @@ export async function getRetryableEscalations(adapter, runId) {
53
65
  return result;
54
66
  }
55
67
  // Determine effective runId:
56
- // - If caller supplies a runId, use it (AC5)
57
- // - Otherwise, use the runId of the last decision (most recently created = latest run) (AC1)
58
- const effectiveRunId = runId ?? (parsed[parsed.length - 1].decisionRunId);
68
+ // - caller-supplied runId: honor it verbatim (explicit scoping)
69
+ // - omitted: walk decisions newest-first, pick the first whose referenced
70
+ // pipeline run is NOT in a terminal status
71
+ let effectiveRunId;
72
+ if (runId !== undefined) {
73
+ effectiveRunId = runId;
74
+ }
75
+ else {
76
+ const candidateRunId = await pickLatestNonTerminalRunId(adapter, parsed);
77
+ if (candidateRunId === undefined) {
78
+ // All referenced runs are terminal (or missing) → no actionable retries.
79
+ return result;
80
+ }
81
+ effectiveRunId = candidateRunId;
82
+ }
59
83
  // Deduplicate by storyKey: keep the last entry per storyKey (last write wins since the
60
84
  // list is ordered by created_at ASC — later entries in the array are more recent).
61
85
  const lastEntryByKey = new Map();
@@ -80,4 +104,47 @@ export async function getRetryableEscalations(adapter, runId) {
80
104
  }
81
105
  return result;
82
106
  }
107
+ /**
108
+ * Walk the parsed decision list newest-last (as supplied), map distinct runIds
109
+ * to their pipeline-run status, and return the runId of the newest decision
110
+ * whose run is NOT in a terminal status.
111
+ *
112
+ * Returns `undefined` when every referenced run is terminal or missing —
113
+ * signalling "nothing retryable" to the caller.
114
+ *
115
+ * The adapter query is a single SELECT against `pipeline_runs` restricted to
116
+ * the distinct runIds we actually care about; keeps the cost O(unique-runs)
117
+ * rather than O(decisions).
118
+ */
119
+ async function pickLatestNonTerminalRunId(adapter, parsed) {
120
+ const uniqueRunIds = Array.from(new Set(parsed.map((p) => p.decisionRunId)));
121
+ if (uniqueRunIds.length === 0)
122
+ return undefined;
123
+ // Bulk-fetch statuses. Use OR chain rather than IN-clause for
124
+ // InMemoryDatabaseAdapter compatibility (its WHERE parser doesn't support IN).
125
+ const statusByRunId = new Map();
126
+ for (const id of uniqueRunIds) {
127
+ const rows = await adapter.query('SELECT id, status FROM pipeline_runs WHERE id = ?', [id]);
128
+ if (rows.length > 0 && rows[0]) {
129
+ statusByRunId.set(rows[0].id, rows[0].status);
130
+ }
131
+ }
132
+ // Scan newest-last (parsed is ordered created_at ASC). Return the first
133
+ // (i.e. most-recent) entry whose run is NOT known-terminal.
134
+ //
135
+ // Treatment of missing runs: if a decision references a runId that has
136
+ // no `pipeline_runs` row at all (deleted, or never persisted because the
137
+ // pipeline ran without writing a run record), we *include* it. The filter's
138
+ // purpose is to skip KNOWN-abandoned runs; an unknown status is treated as
139
+ // "status available elsewhere or caller's choice" to preserve backward
140
+ // compatibility with callers that seed decisions without run rows.
141
+ for (let i = parsed.length - 1; i >= 0; i -= 1) {
142
+ const entry = parsed[i];
143
+ const status = statusByRunId.get(entry.decisionRunId);
144
+ if (status !== undefined && TERMINAL_RUN_STATUSES.includes(status))
145
+ continue;
146
+ return entry.decisionRunId;
147
+ }
148
+ return undefined;
149
+ }
83
150
  //# sourceMappingURL=retry-escalated.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"retry-escalated.js","sourceRoot":"","sources":["../../../src/persistence/queries/retry-escalated.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAGH,OAAO,EAAE,sBAAsB,EAAE,MAAM,gBAAgB,CAAA;AACvD,OAAO,EAAE,oBAAoB,EAAE,MAAM,2BAA2B,CAAA;AAyBhE,8EAA8E;AAC9E,QAAQ;AACR,8EAA8E;AAE9E;;;;;;;;;;;;;GAaG;AACH,MAAM,CAAC,KAAK,UAAU,uBAAuB,CAC3C,OAAwB,EACxB,KAAc;IAEd,MAAM,SAAS,GAAG,MAAM,sBAAsB,CAAC,OAAO,EAAE,oBAAoB,CAAC,CAAA;IAC7E,MAAM,MAAM,GAA+B,EAAE,SAAS,EAAE,EAAE,EAAE,OAAO,EAAE,EAAE,EAAE,CAAA;IAEzE,IAAI,SAAS,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC3B,OAAO,MAAM,CAAA;IACf,CAAC;IASD,MAAM,MAAM,GAAqB,EAAE,CAAA;IAEnC,KAAK,MAAM,QAAQ,IAAI,SAAS,EAAE,CAAC;QACjC,MAAM,QAAQ,GAAG,QAAQ,CAAC,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,CAAA;QAC1C,IAAI,QAAQ,KAAK,CAAC,CAAC;YAAE,SAAQ,CAAC,sBAAsB;QAEpD,MAAM,QAAQ,GAAG,QAAQ,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,EAAE,QAAQ,CAAC,CAAA;QAChD,MAAM,aAAa,GAAG,QAAQ,CAAC,GAAG,CAAC,KAAK,CAAC,QAAQ,GAAG,CAAC,CAAC,CAAA;QAEtD,IAAI,SAA8B,CAAA;QAClC,IAAI,CAAC;YACH,SAAS,GAAG,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAwB,CAAA;QAC/D,CAAC;QAAC,MAAM,CAAC;YACP,SAAQ,CAAC,wBAAwB;QACnC,CAAC;QAED,MAAM,CAAC,IAAI,CAAC,EAAE,QAAQ,EAAE,aAAa,EAAE,SAAS,EAAE,CAAC,CAAA;IACrD,CAAC;IAED,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACxB,OAAO,MAAM,CAAA;IACf,CAAC;IAED,6BAA6B;IAC7B,6CAA6C;IAC7C,6FAA6F;IAC7F,MAAM,cAAc,GAAW,KAAK,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,MAAM,GAAG,CAAC,CAAE,CAAC,aAAa,CAAC,CAAA;IAElF,uFAAuF;IACvF,mFAAmF;IACnF,MAAM,cAAc,GAAG,IAAI,GAAG,EAA0B,CAAA;IAExD,KAAK,MAAM,KAAK,IAAI,MAAM,EAAE,CAAC;QAC3B,IAAI,KAAK,CAAC,aAAa,KAAK,cAAc;YAAE,SAAQ;QACpD,wCAAwC;QACxC,cAAc,CAAC,GAAG,CAAC,KAAK,CAAC,QAAQ,EAAE,KAAK,CAAC,CAAA;IAC3C,CAAC;IAED,KAAK,MAAM,CAAC,QAAQ,EAAE,KAAK,CAAC,IAAI,cAAc,CAAC,OAAO,EAAE,EAAE,CAAC;QACzD,MAAM,EAAE,iBAAiB,EAAE,GAAG,KAAK,CAAC,SAAS,CAAA;QAE7C,IAAI,iBAAiB,KAAK,gBAAgB,EAAE,CAAC;YAC3C,MAAM,CAAC,SAAS,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAA;QACjC,CAAC;aAAM,IAAI,iBAAiB,KAAK,oBAAoB,EAAE,CAAC;YACtD,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,GAAG,EAAE,QAAQ,EAAE,MAAM,EAAE,oBAAoB,EAAE,CAAC,CAAA;QACtE,CAAC;aAAM,IAAI,iBAAiB,KAAK,aAAa,EAAE,CAAC;YAC/C,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,GAAG,EAAE,QAAQ,EAAE,MAAM,EAAE,uBAAuB,EAAE,CAAC,CAAA;QACzE,CAAC;QACD,wDAAwD;IAC1D,CAAC;IAED,OAAO,MAAM,CAAA;AACf,CAAC"}
1
+ {"version":3,"file":"retry-escalated.js","sourceRoot":"","sources":["../../../src/persistence/queries/retry-escalated.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@substrate-ai/core",
3
- "version": "0.20.6",
3
+ "version": "0.20.8",
4
4
  "type": "module",
5
5
  "license": "MIT",
6
6
  "repository": {