substrate-ai 0.20.64 → 0.20.65
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.
- package/dist/adapter-registry-BbVWH3Yv.js +4 -0
- package/dist/cli/index.js +93 -24
- package/dist/{decision-router-BA__VYIp.js → decision-router-DblHY8se.js} +1 -1
- package/dist/{decisions-4F91LrVD.js → decisions-DilHo99V.js} +2 -2
- package/dist/{dist-W2emvN3F.js → dist-K_RRWnBX.js} +2 -2
- package/dist/{errors-CKFu8YI9.js → errors-pSiZbn6e.js} +2 -2
- package/dist/{experimenter-BgpUcUaW.js → experimenter-DT9v2Pto.js} +1 -1
- package/dist/health-DC3y-sR6.js +1715 -0
- package/dist/health-qhtWYh49.js +8 -0
- package/dist/index-c924O9mj.d.ts +1432 -0
- package/dist/index.d.ts +56 -735
- package/dist/index.js +2 -2
- package/dist/interactive-prompt-C7wpE4z4.js +183 -0
- package/dist/{health-DudlnqXd.js → manifest-read-DDkXC3L_.js} +120 -2012
- package/dist/modules/interactive-prompt/index.d.ts +86 -0
- package/dist/modules/interactive-prompt/index.js +6 -0
- package/dist/recovery-engine-BKGBeBnW.js +281 -0
- package/dist/{routing-0ykvBl_4.js → routing-CzF0p6lI.js} +2 -2
- package/dist/run-DX95j4_D.js +14 -0
- package/dist/{run-CCxsv-9M.js → run-DzB4rgkj.js} +224 -31
- package/dist/src/modules/decision-router/index.js +1 -1
- package/dist/src/modules/recovery-engine/index.d.ts +1101 -0
- package/dist/src/modules/recovery-engine/index.js +5 -0
- package/dist/{upgrade-OFeC_NIx.js → upgrade-DxzQ1nss.js} +3 -3
- package/dist/{upgrade-aW7GYL2F.js → upgrade-MP9XzrI6.js} +2 -2
- package/dist/version-manager-impl-GZDUBt0Q.js +4 -0
- package/dist/work-graph-repository-DZyJv5pV.js +265 -0
- package/package.json +1 -1
- package/dist/adapter-registry-k7ZX3Bz6.js +0 -4
- package/dist/health-CLNmnZiw.js +0 -6
- package/dist/run-ChxsPICN.js +0 -10
- package/dist/version-manager-impl-BCSf5E3j.js +0 -4
- /package/dist/{decisions-C0pz9Clx.js → decisions-CzSIEeGP.js} +0 -0
- /package/dist/{routing-CcBOCuC9.js → routing-DFxoKHDt.js} +0 -0
- /package/dist/{version-manager-impl-FH4TTnXm.js → version-manager-impl-qFBiO4Eh.js} +0 -0
|
@@ -0,0 +1,1101 @@
|
|
|
1
|
+
import { CoreEvents, DatabaseAdapter, TypedEventBus } from "../../../index-c924O9mj.js";
|
|
2
|
+
import { z } from "zod";
|
|
3
|
+
|
|
4
|
+
//#region packages/sdlc/dist/verification/findings.d.ts
|
|
5
|
+
/**
|
|
6
|
+
* VerificationFinding — structured per-issue payload emitted by verification checks.
|
|
7
|
+
*
|
|
8
|
+
* Replaces the ad-hoc "stuff everything into VerificationResult.details" pattern
|
|
9
|
+
* that preceded it: every downstream consumer (retry prompts, run manifest,
|
|
10
|
+
* post-run analysis) used to string-parse a free-form blob that the emitting
|
|
11
|
+
* check never promised a schema for. With findings, each issue is an
|
|
12
|
+
* addressable record the pipeline can act on individually.
|
|
13
|
+
*
|
|
14
|
+
* The {command, exitCode, stdoutTail, stderrTail} optional fields are reserved
|
|
15
|
+
* primarily for Phase 2 runtime probes — they cost nothing on the current four
|
|
16
|
+
* Tier A checks (which leave them undefined) but let probe output flow through
|
|
17
|
+
* the same shape without a second refactor.
|
|
18
|
+
*/
|
|
19
|
+
/**
|
|
20
|
+
* Severity of a single verification finding.
|
|
21
|
+
*
|
|
22
|
+
* - `error` → verification check returns `fail` (the aggregate status of the
|
|
23
|
+
* containing VerificationResult is `fail`).
|
|
24
|
+
* - `warn` → verification check returns `warn`.
|
|
25
|
+
* - `info` → a finding captured for diagnostic/telemetry purposes that does
|
|
26
|
+
* not itself raise the containing VerificationResult's status.
|
|
27
|
+
*/
|
|
28
|
+
/**
|
|
29
|
+
* VerificationFinding — structured per-issue payload emitted by verification checks.
|
|
30
|
+
*
|
|
31
|
+
* Replaces the ad-hoc "stuff everything into VerificationResult.details" pattern
|
|
32
|
+
* that preceded it: every downstream consumer (retry prompts, run manifest,
|
|
33
|
+
* post-run analysis) used to string-parse a free-form blob that the emitting
|
|
34
|
+
* check never promised a schema for. With findings, each issue is an
|
|
35
|
+
* addressable record the pipeline can act on individually.
|
|
36
|
+
*
|
|
37
|
+
* The {command, exitCode, stdoutTail, stderrTail} optional fields are reserved
|
|
38
|
+
* primarily for Phase 2 runtime probes — they cost nothing on the current four
|
|
39
|
+
* Tier A checks (which leave them undefined) but let probe output flow through
|
|
40
|
+
* the same shape without a second refactor.
|
|
41
|
+
*/
|
|
42
|
+
/**
|
|
43
|
+
* Severity of a single verification finding.
|
|
44
|
+
*
|
|
45
|
+
* - `error` → verification check returns `fail` (the aggregate status of the
|
|
46
|
+
* containing VerificationResult is `fail`).
|
|
47
|
+
* - `warn` → verification check returns `warn`.
|
|
48
|
+
* - `info` → a finding captured for diagnostic/telemetry purposes that does
|
|
49
|
+
* not itself raise the containing VerificationResult's status.
|
|
50
|
+
*/
|
|
51
|
+
type VerificationFindingSeverity = 'error' | 'warn' | 'info';
|
|
52
|
+
/**
|
|
53
|
+
* One structured issue emitted by a verification check.
|
|
54
|
+
*
|
|
55
|
+
* `category` is the stable machine-readable identifier consumers filter/group
|
|
56
|
+
* on (e.g. `'build-error'`, `'ac-missing-evidence'`, `'phantom-review'`,
|
|
57
|
+
* `'trivial-output'`). New categories should be introduced alongside the
|
|
58
|
+
* check that emits them.
|
|
59
|
+
*/
|
|
60
|
+
interface VerificationFinding {
|
|
61
|
+
/** Stable machine-readable category (e.g. `'build-error'`). */
|
|
62
|
+
category: string;
|
|
63
|
+
/** Severity classification for aggregate-status computation. */
|
|
64
|
+
severity: VerificationFindingSeverity;
|
|
65
|
+
/** Single-line human-readable summary. */
|
|
66
|
+
message: string;
|
|
67
|
+
/** The command that produced this finding, if any. Reserved primarily for Phase 2 runtime probes. */
|
|
68
|
+
command?: string;
|
|
69
|
+
/** Exit status of `command`, if applicable. */
|
|
70
|
+
exitCode?: number;
|
|
71
|
+
/** Last ≤ 4 KiB of stdout from `command`, if captured. */
|
|
72
|
+
stdoutTail?: string;
|
|
73
|
+
/** Last ≤ 4 KiB of stderr from `command`, if captured. */
|
|
74
|
+
stderrTail?: string;
|
|
75
|
+
/** Wall-clock milliseconds the producing action took. */
|
|
76
|
+
durationMs?: number;
|
|
77
|
+
/**
|
|
78
|
+
* Story 60-15: when this finding came from a runtime-probe failure,
|
|
79
|
+
* records who authored the failing probe (`'probe-author'` if Epic 60
|
|
80
|
+
* Phase 2's probe-author phase appended it, `'create-story-ac-transfer'`
|
|
81
|
+
* for the legacy AC-transfer path). Absent for findings from other
|
|
82
|
+
* checks (build, phantom-review, ac-evidence, etc.). Persisted on the
|
|
83
|
+
* stored finding so post-run analysis can compute byAuthor breakdowns
|
|
84
|
+
* and the catch-rate KPI's per-author attribution.
|
|
85
|
+
*/
|
|
86
|
+
_authoredBy?: 'probe-author' | 'create-story-ac-transfer';
|
|
87
|
+
/**
|
|
88
|
+
* Story 66-7: when this finding has category
|
|
89
|
+
* `runtime-probe-placeholder-not-substituted`, carries the escaped
|
|
90
|
+
* placeholder token (e.g. `<UNKNOWN_VAR>`) that was not substituted
|
|
91
|
+
* before probe execution. Absent for all other finding categories.
|
|
92
|
+
*/
|
|
93
|
+
unrecognizedPlaceholder?: string;
|
|
94
|
+
} //#endregion
|
|
95
|
+
//#region packages/sdlc/dist/verification/types.d.ts
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* source-ac-shellout-npx-fallback — Story 67-3, obs_2026-05-03_023 fix #3.
|
|
99
|
+
*
|
|
100
|
+
* Severity: warn. Emitted by SourceAcShelloutCheck when a bare `npx <package>`
|
|
101
|
+
* invocation (without `--no-install`) is detected in a story-modified source file.
|
|
102
|
+
* A bare `npx <package>` without `--no-install` falls through to the public npm
|
|
103
|
+
* registry on first use if the package binary is not locally installed —
|
|
104
|
+
* a dependency-confusion attack vector.
|
|
105
|
+
*/
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* Result returned by a single VerificationCheck.run() invocation.
|
|
109
|
+
*
|
|
110
|
+
* `details` is a human-readable rendering, preserved for any consumer that
|
|
111
|
+
* already reads it. `findings` (story 55-1) is the structured per-issue
|
|
112
|
+
* surface — downstream consumers (retry prompts, run manifest, post-run
|
|
113
|
+
* analysis) should prefer it. Checks that emit findings should derive
|
|
114
|
+
* `details` via `renderFindings(findings)` so the two stay in sync.
|
|
115
|
+
*/
|
|
116
|
+
interface VerificationResult {
|
|
117
|
+
status: 'pass' | 'warn' | 'fail';
|
|
118
|
+
details: string;
|
|
119
|
+
duration_ms: number;
|
|
120
|
+
/** Structured per-issue payload. Optional for backward compatibility with
|
|
121
|
+
* checks and consumers that pre-date story 55-1. */
|
|
122
|
+
findings?: VerificationFinding[];
|
|
123
|
+
}
|
|
124
|
+
/**
|
|
125
|
+
* Per-check result included in a VerificationSummary.
|
|
126
|
+
* Extends VerificationResult with the check's name.
|
|
127
|
+
*/
|
|
128
|
+
interface VerificationCheckResult extends VerificationResult {
|
|
129
|
+
checkName: string;
|
|
130
|
+
}
|
|
131
|
+
/**
|
|
132
|
+
* Aggregated summary for a single story's verification pipeline run.
|
|
133
|
+
*
|
|
134
|
+
* AC4: contains storyKey, array of per-check results, worst-case overall status,
|
|
135
|
+
* and total duration.
|
|
136
|
+
*/
|
|
137
|
+
interface VerificationSummary {
|
|
138
|
+
storyKey: string;
|
|
139
|
+
checks: VerificationCheckResult[];
|
|
140
|
+
/** Worst-case aggregate: fail > warn > pass. */
|
|
141
|
+
status: 'pass' | 'warn' | 'fail';
|
|
142
|
+
duration_ms: number;
|
|
143
|
+
} //#endregion
|
|
144
|
+
//#region packages/sdlc/dist/events.d.ts
|
|
145
|
+
|
|
146
|
+
/**
|
|
147
|
+
* Interface that all verification check implementations must satisfy.
|
|
148
|
+
*
|
|
149
|
+
* AC1: name, tier ('A' | 'B'), and run(context) method.
|
|
150
|
+
* Tier A checks are static analysis; Tier B checks may use cross-story context.
|
|
151
|
+
*/
|
|
152
|
+
/**
|
|
153
|
+
* Structured escalation diagnosis from `orchestrator:story-escalated`.
|
|
154
|
+
* Carries classification and recommended action for the escalation.
|
|
155
|
+
*/
|
|
156
|
+
interface EscalationDiagnosis {
|
|
157
|
+
issueDistribution: 'concentrated' | 'widespread';
|
|
158
|
+
severityProfile: 'blocker-present' | 'major-only' | 'minor-only' | 'no-structured-issues';
|
|
159
|
+
totalIssues: number;
|
|
160
|
+
blockerCount: number;
|
|
161
|
+
majorCount: number;
|
|
162
|
+
minorCount: number;
|
|
163
|
+
affectedFiles: string[];
|
|
164
|
+
reviewCycles: number;
|
|
165
|
+
recommendedAction: 'retry-targeted' | 'split-story' | 'human-intervention';
|
|
166
|
+
rationale: string;
|
|
167
|
+
}
|
|
168
|
+
/**
|
|
169
|
+
* A single finding from `solutioning:readiness-failed`.
|
|
170
|
+
*/
|
|
171
|
+
interface SolutioningFinding {
|
|
172
|
+
category: string;
|
|
173
|
+
severity: string;
|
|
174
|
+
description: string;
|
|
175
|
+
affected_items: string[];
|
|
176
|
+
}
|
|
177
|
+
/**
|
|
178
|
+
* Phase-level timing breakdown from `story:metrics`.
|
|
179
|
+
* Keys are phase names (e.g., "dev-story", "code-review"), values are elapsed milliseconds.
|
|
180
|
+
*/
|
|
181
|
+
type StoryPhaseBreakdown = Record<string, number>;
|
|
182
|
+
/**
|
|
183
|
+
* Complete typed map of all SDLC orchestrator events.
|
|
184
|
+
* Intersection with CoreEvents so TypedEventBus<SdlcEvents> includes all core event keys.
|
|
185
|
+
*/
|
|
186
|
+
type SdlcEvents = CoreEvents & {
|
|
187
|
+
/** Implementation orchestrator has started processing story keys */
|
|
188
|
+
'orchestrator:started': {
|
|
189
|
+
storyKeys: string[];
|
|
190
|
+
pipelineRunId?: string;
|
|
191
|
+
};
|
|
192
|
+
/** A story phase has started within the implementation orchestrator */
|
|
193
|
+
'orchestrator:story-phase-start': {
|
|
194
|
+
storyKey: string;
|
|
195
|
+
phase: string;
|
|
196
|
+
pipelineRunId?: string;
|
|
197
|
+
};
|
|
198
|
+
/** A story phase has completed within the implementation orchestrator */
|
|
199
|
+
'orchestrator:story-phase-complete': {
|
|
200
|
+
storyKey: string;
|
|
201
|
+
phase: string;
|
|
202
|
+
result: unknown;
|
|
203
|
+
pipelineRunId?: string;
|
|
204
|
+
};
|
|
205
|
+
/** A story has completed the full pipeline with SHIP_IT verdict */
|
|
206
|
+
'orchestrator:story-complete': {
|
|
207
|
+
storyKey: string;
|
|
208
|
+
reviewCycles: number;
|
|
209
|
+
};
|
|
210
|
+
/** A story has been escalated after exceeding max review cycles */
|
|
211
|
+
'orchestrator:story-escalated': {
|
|
212
|
+
storyKey: string;
|
|
213
|
+
lastVerdict: string;
|
|
214
|
+
reviewCycles: number;
|
|
215
|
+
issues: unknown[];
|
|
216
|
+
/** Structured diagnosis with classification and recommended action (Story 22-3) */
|
|
217
|
+
diagnosis?: EscalationDiagnosis;
|
|
218
|
+
};
|
|
219
|
+
/** A non-fatal warning occurred during story processing */
|
|
220
|
+
'orchestrator:story-warn': {
|
|
221
|
+
storyKey: string;
|
|
222
|
+
msg: string;
|
|
223
|
+
};
|
|
224
|
+
/** Zero-diff detection gate: dev-story reported COMPLETE but git diff is empty (Story 24-1) */
|
|
225
|
+
'orchestrator:zero-diff-escalation': {
|
|
226
|
+
storyKey: string;
|
|
227
|
+
reason: string;
|
|
228
|
+
};
|
|
229
|
+
/** Implementation orchestrator has finished all stories */
|
|
230
|
+
'orchestrator:complete': {
|
|
231
|
+
totalStories: number;
|
|
232
|
+
completed: number;
|
|
233
|
+
escalated: number;
|
|
234
|
+
failed: number;
|
|
235
|
+
};
|
|
236
|
+
/** Implementation orchestrator has been paused */
|
|
237
|
+
'orchestrator:paused': Record<string, never>;
|
|
238
|
+
/** Implementation orchestrator has been resumed */
|
|
239
|
+
'orchestrator:resumed': Record<string, never>;
|
|
240
|
+
/** Periodic heartbeat emitted every 30s during pipeline execution */
|
|
241
|
+
'orchestrator:heartbeat': {
|
|
242
|
+
runId: string;
|
|
243
|
+
activeDispatches: number;
|
|
244
|
+
completedDispatches: number;
|
|
245
|
+
queuedDispatches: number;
|
|
246
|
+
};
|
|
247
|
+
/** Watchdog detected no progress for an extended period */
|
|
248
|
+
'orchestrator:stall': {
|
|
249
|
+
runId: string;
|
|
250
|
+
storyKey: string;
|
|
251
|
+
phase: string;
|
|
252
|
+
elapsedMs: number;
|
|
253
|
+
/** PIDs of child processes at time of stall detection */
|
|
254
|
+
childPids: number[];
|
|
255
|
+
/** Whether any child process was actively running (not zombie) */
|
|
256
|
+
childActive: boolean;
|
|
257
|
+
};
|
|
258
|
+
/** Plan generation has started */
|
|
259
|
+
'plan:generating': {
|
|
260
|
+
agent: string;
|
|
261
|
+
description: string;
|
|
262
|
+
};
|
|
263
|
+
/** Plan generation has completed */
|
|
264
|
+
'plan:generated': {
|
|
265
|
+
taskCount: number;
|
|
266
|
+
estimatedCost: number;
|
|
267
|
+
};
|
|
268
|
+
/** Plan was approved by the user */
|
|
269
|
+
'plan:approved': {
|
|
270
|
+
taskCount: number;
|
|
271
|
+
};
|
|
272
|
+
/** Plan was rejected by the user */
|
|
273
|
+
'plan:rejected': {
|
|
274
|
+
reason: string;
|
|
275
|
+
};
|
|
276
|
+
/** Plan is being refined based on feedback */
|
|
277
|
+
'plan:refining': {
|
|
278
|
+
planId: string;
|
|
279
|
+
feedback: string;
|
|
280
|
+
currentVersion: number;
|
|
281
|
+
};
|
|
282
|
+
/** Plan refinement completed successfully */
|
|
283
|
+
'plan:refined': {
|
|
284
|
+
planId: string;
|
|
285
|
+
newVersion: number;
|
|
286
|
+
taskCount: number;
|
|
287
|
+
};
|
|
288
|
+
/** Plan was rolled back to a previous version */
|
|
289
|
+
'plan:rolled-back': {
|
|
290
|
+
planId: string;
|
|
291
|
+
fromVersion: number;
|
|
292
|
+
toVersion: number;
|
|
293
|
+
newVersion: number;
|
|
294
|
+
};
|
|
295
|
+
/** Plan refinement failed */
|
|
296
|
+
'plan:refinement-failed': {
|
|
297
|
+
planId: string;
|
|
298
|
+
currentVersion: number;
|
|
299
|
+
error: string;
|
|
300
|
+
};
|
|
301
|
+
/** Readiness check has completed — emitted for all verdicts (READY, NEEDS_WORK, NOT_READY) */
|
|
302
|
+
'solutioning:readiness-check': {
|
|
303
|
+
runId: string;
|
|
304
|
+
verdict: 'READY' | 'NEEDS_WORK' | 'NOT_READY';
|
|
305
|
+
coverageScore: number;
|
|
306
|
+
findingCount: number;
|
|
307
|
+
blockerCount: number;
|
|
308
|
+
};
|
|
309
|
+
/** Readiness check returned NOT_READY — solutioning phase will not proceed to implementation */
|
|
310
|
+
'solutioning:readiness-failed': {
|
|
311
|
+
runId: string;
|
|
312
|
+
verdict: 'NOT_READY';
|
|
313
|
+
coverageScore: number;
|
|
314
|
+
findings: SolutioningFinding[];
|
|
315
|
+
};
|
|
316
|
+
/**
|
|
317
|
+
* Emitted when a dev-story timeout has partial work on disk and the
|
|
318
|
+
* orchestrator captures it as a checkpoint for retry (Story 39-5).
|
|
319
|
+
*/
|
|
320
|
+
'story:checkpoint-saved': {
|
|
321
|
+
/** Story key that timed out with partial work */
|
|
322
|
+
storyKey: string;
|
|
323
|
+
/** Number of files modified before the timeout */
|
|
324
|
+
filesCount: number;
|
|
325
|
+
/** Approximate byte length of the git diff captured */
|
|
326
|
+
diffSizeBytes: number;
|
|
327
|
+
};
|
|
328
|
+
/**
|
|
329
|
+
* Emitted when the orchestrator dispatches a checkpoint retry for a story
|
|
330
|
+
* that timed out with partial work (Story 39-6).
|
|
331
|
+
*/
|
|
332
|
+
'story:checkpoint-retry': {
|
|
333
|
+
/** Story key being retried */
|
|
334
|
+
storyKey: string;
|
|
335
|
+
/** Number of files modified in the partial work captured at checkpoint */
|
|
336
|
+
filesCount: number;
|
|
337
|
+
/** Retry attempt number (always 2 — first retry after initial timeout) */
|
|
338
|
+
attempt: number;
|
|
339
|
+
};
|
|
340
|
+
/** Build verification command failed with non-zero exit or timeout */
|
|
341
|
+
'story:build-verification-failed': {
|
|
342
|
+
storyKey: string;
|
|
343
|
+
exitCode: number;
|
|
344
|
+
/** Build output (stdout+stderr), truncated to 2000 chars */
|
|
345
|
+
output: string;
|
|
346
|
+
};
|
|
347
|
+
/** Build verification command exited with code 0 */
|
|
348
|
+
'story:build-verification-passed': {
|
|
349
|
+
storyKey: string;
|
|
350
|
+
};
|
|
351
|
+
/** Non-blocking warning: modified .ts files export shared interfaces referenced by cross-module tests */
|
|
352
|
+
'story:interface-change-warning': {
|
|
353
|
+
storyKey: string;
|
|
354
|
+
modifiedInterfaces: string[];
|
|
355
|
+
potentiallyAffectedTests: string[];
|
|
356
|
+
};
|
|
357
|
+
/** Per-story metrics snapshot emitted when a story reaches a terminal state (Story 24-4) */
|
|
358
|
+
'story:metrics': {
|
|
359
|
+
storyKey: string;
|
|
360
|
+
wallClockMs: number;
|
|
361
|
+
phaseBreakdown: StoryPhaseBreakdown;
|
|
362
|
+
tokens: {
|
|
363
|
+
input: number;
|
|
364
|
+
output: number;
|
|
365
|
+
};
|
|
366
|
+
reviewCycles: number;
|
|
367
|
+
dispatches: number;
|
|
368
|
+
};
|
|
369
|
+
/** A pipeline phase has started (emitted by full pipeline path for NDJSON visibility) */
|
|
370
|
+
'pipeline:phase-start': {
|
|
371
|
+
phase: string;
|
|
372
|
+
ts: string;
|
|
373
|
+
};
|
|
374
|
+
/** A pipeline phase has completed (emitted by full pipeline path for NDJSON visibility) */
|
|
375
|
+
'pipeline:phase-complete': {
|
|
376
|
+
phase: string;
|
|
377
|
+
ts: string;
|
|
378
|
+
};
|
|
379
|
+
/** Pre-flight build check failed before any stories were dispatched */
|
|
380
|
+
'pipeline:pre-flight-failure': {
|
|
381
|
+
exitCode: number;
|
|
382
|
+
/** Build output (stdout+stderr), truncated to 2000 chars */
|
|
383
|
+
output: string;
|
|
384
|
+
};
|
|
385
|
+
/** Contract verification found a mismatch between declared export/import contracts */
|
|
386
|
+
'pipeline:contract-mismatch': {
|
|
387
|
+
/** Story key that declared the export for this contract */
|
|
388
|
+
exporter: string;
|
|
389
|
+
/** Story key that declared the import for this contract (null if no importer found) */
|
|
390
|
+
importer: string | null;
|
|
391
|
+
/** TypeScript interface or Zod schema name (e.g., "JudgeResult") */
|
|
392
|
+
contractName: string;
|
|
393
|
+
/** Human-readable description of the mismatch */
|
|
394
|
+
mismatchDescription: string;
|
|
395
|
+
};
|
|
396
|
+
/** Consolidated contract verification summary (emitted once per verification pass) */
|
|
397
|
+
'pipeline:contract-verification-summary': {
|
|
398
|
+
/** Number of contract declarations verified (current sprint only) */
|
|
399
|
+
verified: number;
|
|
400
|
+
/** Number of stale declarations pruned (from previous epics) */
|
|
401
|
+
stalePruned: number;
|
|
402
|
+
/** Number of real mismatches found */
|
|
403
|
+
mismatches: number;
|
|
404
|
+
/** 'pass' if zero mismatches, 'fail' otherwise */
|
|
405
|
+
verdict: 'pass' | 'fail';
|
|
406
|
+
};
|
|
407
|
+
/** Dolt merge conflict detected when merging a story branch into main */
|
|
408
|
+
'pipeline:state-conflict': {
|
|
409
|
+
storyKey: string;
|
|
410
|
+
conflict: unknown;
|
|
411
|
+
};
|
|
412
|
+
/**
|
|
413
|
+
* Emitted at pipeline startup when the repo-map symbol index is detected as stale.
|
|
414
|
+
*/
|
|
415
|
+
'pipeline:repo-map-stale': {
|
|
416
|
+
/** SHA stored in the repo-map meta (last index update) */
|
|
417
|
+
storedSha: string;
|
|
418
|
+
/** Current HEAD commit SHA */
|
|
419
|
+
headSha: string;
|
|
420
|
+
/** Number of files in the stored index */
|
|
421
|
+
fileCount: number;
|
|
422
|
+
};
|
|
423
|
+
/** Project profile may be outdated relative to the actual project structure */
|
|
424
|
+
'pipeline:profile-stale': {
|
|
425
|
+
/** Human-readable message describing the staleness indicators found */
|
|
426
|
+
message: string;
|
|
427
|
+
/** List of staleness indicators detected */
|
|
428
|
+
indicators: string[];
|
|
429
|
+
};
|
|
430
|
+
/**
|
|
431
|
+
* Emitted when classifyAndPersist() successfully persists a finding from
|
|
432
|
+
* a failing story. Used by observability and monitoring consumers.
|
|
433
|
+
*/
|
|
434
|
+
'pipeline:finding-captured': {
|
|
435
|
+
storyKey: string;
|
|
436
|
+
runId: string;
|
|
437
|
+
rootCause: string;
|
|
438
|
+
};
|
|
439
|
+
/**
|
|
440
|
+
* Emitted when a file overlap is detected between a pending story and a
|
|
441
|
+
* completed story, but no namespace collision exists. Dispatch proceeds
|
|
442
|
+
* normally after this event (non-blocking warning).
|
|
443
|
+
*/
|
|
444
|
+
'pipeline:dispatch-warn': {
|
|
445
|
+
storyKey: string;
|
|
446
|
+
completedStoryKey: string;
|
|
447
|
+
overlappingFiles: string[];
|
|
448
|
+
};
|
|
449
|
+
/**
|
|
450
|
+
* Emitted when the dispatch gate cannot resolve a conflict and places the
|
|
451
|
+
* story in the `gated` phase for operator review. Dispatch does not proceed.
|
|
452
|
+
*/
|
|
453
|
+
'pipeline:story-gated': {
|
|
454
|
+
storyKey: string;
|
|
455
|
+
conflictType: string;
|
|
456
|
+
reason: string;
|
|
457
|
+
completedStoryKey?: string;
|
|
458
|
+
};
|
|
459
|
+
/**
|
|
460
|
+
* Emitted after each individual verification check completes (pass, warn, or fail).
|
|
461
|
+
* AC5: payload matches VerificationCheckResult fields plus storyKey.
|
|
462
|
+
*/
|
|
463
|
+
'verification:check-complete': {
|
|
464
|
+
storyKey: string;
|
|
465
|
+
checkName: string;
|
|
466
|
+
status: 'pass' | 'warn' | 'fail';
|
|
467
|
+
details: string;
|
|
468
|
+
duration_ms: number;
|
|
469
|
+
};
|
|
470
|
+
/**
|
|
471
|
+
* Emitted once per story after all verification checks have run.
|
|
472
|
+
* AC5: payload is the full VerificationSummary for the story.
|
|
473
|
+
*/
|
|
474
|
+
'verification:story-complete': VerificationSummary;
|
|
475
|
+
/**
|
|
476
|
+
* Emitted when reconcile-from-disk successfully completes a reconciliation.
|
|
477
|
+
* Foundation primitive for Epic 70 / 73 automated Path A recovery.
|
|
478
|
+
*/
|
|
479
|
+
'pipeline:reconcile-from-disk': {
|
|
480
|
+
runId: string;
|
|
481
|
+
/** Story keys that were transitioned to status='complete' */
|
|
482
|
+
affectedStories: string[];
|
|
483
|
+
/** Whether all validation gates passed before reconciling */
|
|
484
|
+
gatesPassed: boolean;
|
|
485
|
+
/** Whether the operator confirmed the reconciliation (false = --yes flag used) */
|
|
486
|
+
operatorConfirmed: boolean;
|
|
487
|
+
durationMs: number;
|
|
488
|
+
};
|
|
489
|
+
/**
|
|
490
|
+
* Emitted when a validation gate fails during reconcile-from-disk.
|
|
491
|
+
* No Dolt mutation occurs after this event.
|
|
492
|
+
*/
|
|
493
|
+
'pipeline:reconcile-gate-failed': {
|
|
494
|
+
runId: string;
|
|
495
|
+
/** Name of the gate that failed (e.g. 'build', 'typecheck:gate', 'test:fast') */
|
|
496
|
+
failedGate: string;
|
|
497
|
+
/** Tail of subprocess stderr captured at gate failure (~64KB max, UTF-8) */
|
|
498
|
+
stderrTail?: string;
|
|
499
|
+
/** Tail of subprocess stdout captured at gate failure (~64KB max, UTF-8) */
|
|
500
|
+
stdoutTail?: string;
|
|
501
|
+
durationMs: number;
|
|
502
|
+
};
|
|
503
|
+
}; //#endregion
|
|
504
|
+
//#region packages/sdlc/dist/run-model/per-story-state.d.ts
|
|
505
|
+
|
|
506
|
+
//# sourceMappingURL=events.d.ts.map
|
|
507
|
+
/**
|
|
508
|
+
* Schema for a single per-story state entry in the run manifest.
|
|
509
|
+
*
|
|
510
|
+
* Field semantics:
|
|
511
|
+
* - `status`: High-level consumer-facing status (state-machine value). Use this
|
|
512
|
+
* for state-machine decisions and display.
|
|
513
|
+
* - `phase`: Raw orchestrator `StoryPhase` string (e.g., `'IN_DEV'`, `'IN_REVIEW'`).
|
|
514
|
+
* Informational only — do NOT compare this field in state-machine logic.
|
|
515
|
+
* - `started_at`: ISO-8601 timestamp when the story entered an active phase.
|
|
516
|
+
* - `completed_at`: ISO-8601 timestamp when the story reached a terminal state.
|
|
517
|
+
* - `verification_result`: Verification pipeline result (populated by story 52-7).
|
|
518
|
+
* - `cost_usd`: Accumulated cost in USD (populated at terminal transition).
|
|
519
|
+
*/
|
|
520
|
+
declare const PerStoryStateSchema: z.ZodObject<{
|
|
521
|
+
status: z.ZodUnion<readonly [z.ZodLiteral<"pending">, z.ZodLiteral<"dispatched">, z.ZodLiteral<"in-review">, z.ZodLiteral<"complete">, z.ZodLiteral<"failed">, z.ZodLiteral<"escalated">, z.ZodLiteral<"recovered">, z.ZodLiteral<"verification-failed">, z.ZodLiteral<"gated">, z.ZodLiteral<"skipped">, z.ZodLiteral<"verification-stale">, z.ZodString]>;
|
|
522
|
+
phase: z.ZodString;
|
|
523
|
+
started_at: z.ZodString;
|
|
524
|
+
completed_at: z.ZodOptional<z.ZodString>;
|
|
525
|
+
verification_result: z.ZodOptional<z.ZodObject<{
|
|
526
|
+
storyKey: z.ZodString;
|
|
527
|
+
checks: z.ZodArray<z.ZodObject<{
|
|
528
|
+
checkName: z.ZodString;
|
|
529
|
+
status: z.ZodEnum<{
|
|
530
|
+
warn: "warn";
|
|
531
|
+
pass: "pass";
|
|
532
|
+
fail: "fail";
|
|
533
|
+
}>;
|
|
534
|
+
details: z.ZodString;
|
|
535
|
+
duration_ms: z.ZodNumber;
|
|
536
|
+
findings: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
537
|
+
category: z.ZodString;
|
|
538
|
+
severity: z.ZodEnum<{
|
|
539
|
+
error: "error";
|
|
540
|
+
warn: "warn";
|
|
541
|
+
info: "info";
|
|
542
|
+
}>;
|
|
543
|
+
message: z.ZodString;
|
|
544
|
+
command: z.ZodOptional<z.ZodString>;
|
|
545
|
+
exitCode: z.ZodOptional<z.ZodNumber>;
|
|
546
|
+
stdoutTail: z.ZodOptional<z.ZodString>;
|
|
547
|
+
stderrTail: z.ZodOptional<z.ZodString>;
|
|
548
|
+
durationMs: z.ZodOptional<z.ZodNumber>;
|
|
549
|
+
_authoredBy: z.ZodOptional<z.ZodEnum<{
|
|
550
|
+
"probe-author": "probe-author";
|
|
551
|
+
"create-story-ac-transfer": "create-story-ac-transfer";
|
|
552
|
+
}>>;
|
|
553
|
+
}, z.core.$strip>>>;
|
|
554
|
+
}, z.core.$strip>>;
|
|
555
|
+
status: z.ZodEnum<{
|
|
556
|
+
warn: "warn";
|
|
557
|
+
pass: "pass";
|
|
558
|
+
fail: "fail";
|
|
559
|
+
}>;
|
|
560
|
+
duration_ms: z.ZodNumber;
|
|
561
|
+
annotations: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
562
|
+
findingCategory: z.ZodString;
|
|
563
|
+
judgment: z.ZodEnum<{
|
|
564
|
+
"confirmed-defect": "confirmed-defect";
|
|
565
|
+
"false-positive": "false-positive";
|
|
566
|
+
"probe-bug": "probe-bug";
|
|
567
|
+
}>;
|
|
568
|
+
probeName: z.ZodOptional<z.ZodString>;
|
|
569
|
+
note: z.ZodOptional<z.ZodString>;
|
|
570
|
+
createdAt: z.ZodString;
|
|
571
|
+
}, z.core.$strip>>>;
|
|
572
|
+
}, z.core.$strip>>;
|
|
573
|
+
cost_usd: z.ZodOptional<z.ZodNumber>;
|
|
574
|
+
review_cycles: z.ZodOptional<z.ZodNumber>;
|
|
575
|
+
dispatches: z.ZodOptional<z.ZodNumber>;
|
|
576
|
+
retry_count: z.ZodOptional<z.ZodNumber>;
|
|
577
|
+
dev_story_signals: z.ZodOptional<z.ZodObject<{
|
|
578
|
+
result: z.ZodOptional<z.ZodUnion<readonly [z.ZodLiteral<"completed">, z.ZodLiteral<"failed">, z.ZodLiteral<"partial">, z.ZodString]>>;
|
|
579
|
+
ac_met: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
580
|
+
ac_failures: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
581
|
+
files_modified: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
582
|
+
tests: z.ZodOptional<z.ZodUnion<readonly [z.ZodLiteral<"pass">, z.ZodLiteral<"fail">, z.ZodLiteral<"unknown">, z.ZodString]>>;
|
|
583
|
+
}, z.core.$strip>>;
|
|
584
|
+
probe_author_triggered_by: z.ZodOptional<z.ZodUnion<readonly [z.ZodLiteral<"event-driven">, z.ZodLiteral<"state-integrating">, z.ZodLiteral<"both">, z.ZodString]>>;
|
|
585
|
+
verification_re_run: z.ZodOptional<z.ZodBoolean>;
|
|
586
|
+
}, z.core.$strip>;
|
|
587
|
+
type PerStoryState = z.infer<typeof PerStoryStateSchema>;
|
|
588
|
+
|
|
589
|
+
//#endregion
|
|
590
|
+
//#region packages/sdlc/dist/run-model/recovery-history.d.ts
|
|
591
|
+
//# sourceMappingURL=per-story-state.d.ts.map
|
|
592
|
+
/**
|
|
593
|
+
* A single recovery attempt recorded in the run manifest.
|
|
594
|
+
*
|
|
595
|
+
* `attempt_number` is 1-indexed: 1 = first retry, NOT the initial dispatch.
|
|
596
|
+
* The initial dispatch of a story is never recorded as a RecoveryEntry.
|
|
597
|
+
*
|
|
598
|
+
* `strategy` is free-form (e.g., `'retry-with-context'`, `'re-scope'`).
|
|
599
|
+
*
|
|
600
|
+
* `cost_usd` is the cost of THIS single retry attempt only — NOT cumulative.
|
|
601
|
+
* Cumulative per-story retry cost is tracked in `CostAccumulation.per_story`.
|
|
602
|
+
*/
|
|
603
|
+
declare const RecoveryEntrySchema: z.ZodObject<{
|
|
604
|
+
story_key: z.ZodString;
|
|
605
|
+
attempt_number: z.ZodNumber;
|
|
606
|
+
strategy: z.ZodString;
|
|
607
|
+
root_cause: z.ZodString;
|
|
608
|
+
outcome: z.ZodUnion<readonly [z.ZodLiteral<"retried">, z.ZodLiteral<"escalated">, z.ZodLiteral<"skipped">, z.ZodString]>;
|
|
609
|
+
cost_usd: z.ZodNumber;
|
|
610
|
+
timestamp: z.ZodString;
|
|
611
|
+
}, z.core.$strip>;
|
|
612
|
+
type RecoveryEntry = z.infer<typeof RecoveryEntrySchema>;
|
|
613
|
+
/**
|
|
614
|
+
* Accumulated retry cost data for a pipeline run.
|
|
615
|
+
*
|
|
616
|
+
* `per_story` maps story_key → sum of all RecoveryEntry.cost_usd for that
|
|
617
|
+
* story. It does NOT include the initial dispatch cost, which is tracked in
|
|
618
|
+
* `PerStoryState.cost_usd`.
|
|
619
|
+
*
|
|
620
|
+
* `run_total` is the sum of all RecoveryEntry.cost_usd values in the run
|
|
621
|
+
* (i.e., total retry cost only, not total run cost).
|
|
622
|
+
*
|
|
623
|
+
* An empty `{ per_story: {}, run_total: 0 }` is the valid initial value.
|
|
624
|
+
*/
|
|
625
|
+
declare const CostAccumulationSchema: z.ZodObject<{
|
|
626
|
+
per_story: z.ZodRecord<z.ZodString, z.ZodNumber>;
|
|
627
|
+
run_total: z.ZodNumber;
|
|
628
|
+
}, z.core.$strip>;
|
|
629
|
+
type CostAccumulation = z.infer<typeof CostAccumulationSchema>;
|
|
630
|
+
|
|
631
|
+
//#endregion
|
|
632
|
+
//#region packages/sdlc/dist/run-model/types.d.ts
|
|
633
|
+
//# sourceMappingURL=recovery-history.d.ts.map
|
|
634
|
+
/**
|
|
635
|
+
* A pending supervisor proposal awaiting user confirmation.
|
|
636
|
+
*/
|
|
637
|
+
interface Proposal {
|
|
638
|
+
/** Unique proposal ID. */
|
|
639
|
+
id: string;
|
|
640
|
+
/** ISO-8601 timestamp when the proposal was created. */
|
|
641
|
+
created_at: string;
|
|
642
|
+
/** Short description of what is being proposed. */
|
|
643
|
+
description: string;
|
|
644
|
+
/** Proposal type (e.g. 'retry', 'fix', 'escalate'). */
|
|
645
|
+
type: string;
|
|
646
|
+
/** Story key this proposal pertains to, if any (snake_case legacy field). */
|
|
647
|
+
story_key?: string;
|
|
648
|
+
/** Additional payload data for the proposal. */
|
|
649
|
+
payload?: Record<string, unknown>;
|
|
650
|
+
/** Story key this proposal pertains to (camelCase, Recovery Engine primary). */
|
|
651
|
+
storyKey?: string;
|
|
652
|
+
/** Root cause classification for the failure (e.g. 'scope-violation'). */
|
|
653
|
+
rootCause?: string;
|
|
654
|
+
/** Number of retry attempts made before proposing. */
|
|
655
|
+
attempts?: number;
|
|
656
|
+
/** Human-readable suggested action for the operator. */
|
|
657
|
+
suggestedAction?: string;
|
|
658
|
+
/** Keys of stories that share files with this story (blast radius). */
|
|
659
|
+
blastRadius?: string[];
|
|
660
|
+
}
|
|
661
|
+
/**
|
|
662
|
+
* Full data shape for a run manifest stored on disk.
|
|
663
|
+
*
|
|
664
|
+
* All fields are required; optional values use `null`.
|
|
665
|
+
* `per_story_state` is typed as `Record<string, PerStoryState>` (story 52-4).
|
|
666
|
+
* Each key is a story key (e.g., '52-1'); the value tracks full per-story
|
|
667
|
+
* lifecycle state for manifest consumers across Epics 52–54.
|
|
668
|
+
*/
|
|
669
|
+
interface RunManifestData {
|
|
670
|
+
/** Unique run identifier (UUID). */
|
|
671
|
+
run_id: string;
|
|
672
|
+
/** CLI flags used to start this run. */
|
|
673
|
+
cli_flags: Record<string, unknown>;
|
|
674
|
+
/** Explicit story scope (empty = all pending stories). */
|
|
675
|
+
story_scope: string[];
|
|
676
|
+
/**
|
|
677
|
+
* Pipeline run status. Authoritative source — Dolt `pipeline_runs.status`
|
|
678
|
+
* is the degraded fallback. Consumers MUST read this field first.
|
|
679
|
+
*/
|
|
680
|
+
run_status?: 'running' | 'completed' | 'failed' | 'stopped';
|
|
681
|
+
/**
|
|
682
|
+
* Number of supervisor-triggered restarts for this run.
|
|
683
|
+
* Authoritative source — Dolt `run_metrics.restarts` is the degraded fallback.
|
|
684
|
+
*/
|
|
685
|
+
restart_count?: number;
|
|
686
|
+
/** PID of the attached supervisor process, or null if none. */
|
|
687
|
+
supervisor_pid: number | null;
|
|
688
|
+
/** Session ID of the supervisor process, or null if none. */
|
|
689
|
+
supervisor_session_id: string | null;
|
|
690
|
+
/** Per-story state keyed by story key (story 52-4). */
|
|
691
|
+
per_story_state: Record<string, PerStoryState>;
|
|
692
|
+
/** Log of recovery attempts for this run (Story 52-8). */
|
|
693
|
+
recovery_history: RecoveryEntry[];
|
|
694
|
+
/** Accumulated retry cost data (Story 52-8). */
|
|
695
|
+
cost_accumulation: CostAccumulation;
|
|
696
|
+
/** Pending proposals awaiting confirmation. */
|
|
697
|
+
pending_proposals: Proposal[];
|
|
698
|
+
/**
|
|
699
|
+
* Human-readable reason the run was stopped (e.g. 'killed_by_user').
|
|
700
|
+
* Set by the SIGTERM/SIGINT handler (Story 58-7). Absent on pre-58-7 manifests.
|
|
701
|
+
*/
|
|
702
|
+
stopped_reason?: string;
|
|
703
|
+
/**
|
|
704
|
+
* ISO-8601 timestamp when the run was stopped.
|
|
705
|
+
* Set by the SIGTERM/SIGINT handler (Story 58-7). Absent on pre-58-7 manifests.
|
|
706
|
+
*/
|
|
707
|
+
stopped_at?: string;
|
|
708
|
+
/**
|
|
709
|
+
* Monotonic write counter. Incremented on every successful `write()`.
|
|
710
|
+
* Used to detect which file is newer after a mid-rename crash.
|
|
711
|
+
*/
|
|
712
|
+
generation: number;
|
|
713
|
+
/** ISO-8601 timestamp when the manifest was first created. */
|
|
714
|
+
created_at: string;
|
|
715
|
+
/** ISO-8601 timestamp of the most recent write. */
|
|
716
|
+
updated_at: string;
|
|
717
|
+
}
|
|
718
|
+
|
|
719
|
+
//#endregion
|
|
720
|
+
//#region packages/sdlc/dist/run-model/cli-flags.d.ts
|
|
721
|
+
//# sourceMappingURL=types.d.ts.map
|
|
722
|
+
/**
|
|
723
|
+
* Zod schema for the CLI flags persisted in the run manifest.
|
|
724
|
+
*
|
|
725
|
+
* All fields are optional — only flags explicitly provided on the CLI are written.
|
|
726
|
+
* `halt_on` defaults to `'none'` at write time; `cost_ceiling` is omitted when not provided.
|
|
727
|
+
*/
|
|
728
|
+
declare const CliFlagsSchema: z.ZodObject<{
|
|
729
|
+
stories: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
730
|
+
halt_on: z.ZodOptional<z.ZodEnum<{
|
|
731
|
+
none: "none";
|
|
732
|
+
all: "all";
|
|
733
|
+
critical: "critical";
|
|
734
|
+
}>>;
|
|
735
|
+
cost_ceiling: z.ZodOptional<z.ZodNumber>;
|
|
736
|
+
agent: z.ZodOptional<z.ZodString>;
|
|
737
|
+
skip_verification: z.ZodOptional<z.ZodBoolean>;
|
|
738
|
+
events: z.ZodOptional<z.ZodBoolean>;
|
|
739
|
+
non_interactive: z.ZodOptional<z.ZodBoolean>;
|
|
740
|
+
halt_skipped: z.ZodOptional<z.ZodBoolean>;
|
|
741
|
+
halt_skipped_decisions: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
742
|
+
decisionType: z.ZodString;
|
|
743
|
+
severity: z.ZodString;
|
|
744
|
+
defaultAction: z.ZodString;
|
|
745
|
+
reason: z.ZodString;
|
|
746
|
+
skippedAt: z.ZodString;
|
|
747
|
+
}, z.core.$strip>>>;
|
|
748
|
+
}, z.core.$strip>;
|
|
749
|
+
type CliFlags = z.infer<typeof CliFlagsSchema>;
|
|
750
|
+
|
|
751
|
+
//#endregion
|
|
752
|
+
//#region packages/sdlc/dist/run-model/run-manifest.d.ts
|
|
753
|
+
//# sourceMappingURL=cli-flags.d.ts.map
|
|
754
|
+
/**
|
|
755
|
+
* Minimal interface for Dolt query access needed by the degraded-mode fallback.
|
|
756
|
+
* Consumers inject a real `DatabaseAdapter` (from @substrate-ai/core) or null.
|
|
757
|
+
*/
|
|
758
|
+
interface IDoltAdapter {
|
|
759
|
+
query<T = unknown>(sql: string, params?: unknown[]): Promise<T[]>;
|
|
760
|
+
}
|
|
761
|
+
/**
|
|
762
|
+
* Typed, atomic file-backed run manifest.
|
|
763
|
+
*
|
|
764
|
+
* Each instance is bound to a specific run ID and base directory.
|
|
765
|
+
* Use `RunManifest.create()` to initialize a new manifest,
|
|
766
|
+
* or `RunManifest.read()` to load an existing one.
|
|
767
|
+
*/
|
|
768
|
+
declare class RunManifest {
|
|
769
|
+
readonly runId: string;
|
|
770
|
+
readonly baseDir: string;
|
|
771
|
+
/** Optional Dolt adapter for degraded-mode fallback on read. */
|
|
772
|
+
private doltAdapter;
|
|
773
|
+
/**
|
|
774
|
+
* Serializes all write operations on this instance to prevent lost-update races.
|
|
775
|
+
* Initialized to a resolved promise; each enqueued operation chains off the tail.
|
|
776
|
+
*/
|
|
777
|
+
private _writeChain;
|
|
778
|
+
constructor(runId: string, baseDir?: string, doltAdapter?: IDoltAdapter | null);
|
|
779
|
+
get primaryPath(): string;
|
|
780
|
+
get bakPath(): string;
|
|
781
|
+
get tmpPath(): string;
|
|
782
|
+
/**
|
|
783
|
+
* Read this manifest from disk (multi-tier fallback).
|
|
784
|
+
*
|
|
785
|
+
* Delegates to `RunManifest.read()` with this instance's runId, baseDir,
|
|
786
|
+
* and doltAdapter. Primarily used by `SupervisorLock` (and tests that mock it).
|
|
787
|
+
*
|
|
788
|
+
* @throws ManifestReadError if all sources fail
|
|
789
|
+
*/
|
|
790
|
+
read(): Promise<RunManifestData>;
|
|
791
|
+
/**
|
|
792
|
+
* Atomically update specific fields in the manifest.
|
|
793
|
+
*
|
|
794
|
+
* Reads the current manifest, merges in the provided partial data (shallow
|
|
795
|
+
* merge), then writes the result atomically. Generation is incremented and
|
|
796
|
+
* `updated_at` is refreshed by `write()`.
|
|
797
|
+
*
|
|
798
|
+
* Callers should pass only the fields they intend to change. Do NOT use this
|
|
799
|
+
* to change `run_id` or `created_at` — those are immutable after creation.
|
|
800
|
+
*
|
|
801
|
+
* @throws ManifestReadError if the current manifest cannot be read
|
|
802
|
+
*/
|
|
803
|
+
update(partial: Partial<Omit<RunManifestData, 'generation' | 'updated_at'>>): Promise<void>;
|
|
804
|
+
/**
|
|
805
|
+
* Append `fn` to the per-instance write chain so all write operations execute
|
|
806
|
+
* strictly sequentially, preventing lost-update races on concurrent callers.
|
|
807
|
+
*
|
|
808
|
+
* The chain itself never rejects (errors are swallowed on the chain side);
|
|
809
|
+
* the returned promise resolves or rejects exactly when `fn` settles, so
|
|
810
|
+
* fire-and-forget `.catch()` callers still receive failure signals.
|
|
811
|
+
*/
|
|
812
|
+
private _enqueue;
|
|
813
|
+
/**
|
|
814
|
+
* Raw implementation of the atomic manifest write.
|
|
815
|
+
* Must only be called from within an `_enqueue`-d function to maintain
|
|
816
|
+
* the serialization invariant.
|
|
817
|
+
*
|
|
818
|
+
* Sequence:
|
|
819
|
+
* 1. Auto-increment `generation`, set `updated_at`
|
|
820
|
+
* 2. Serialize to JSON and validate round-trip
|
|
821
|
+
* 3. Ensure baseDir exists (mkdir -p)
|
|
822
|
+
* 4. Write to `.tmp` via open → write → datasync → close (fsync)
|
|
823
|
+
* 5. If primary exists, copy to `.bak`
|
|
824
|
+
* 6. Rename `.tmp` → primary path
|
|
825
|
+
*/
|
|
826
|
+
private _writeImpl;
|
|
827
|
+
/**
|
|
828
|
+
* Atomically write the manifest to disk.
|
|
829
|
+
*
|
|
830
|
+
* Enqueues the write via `_enqueue` so concurrent calls are serialized.
|
|
831
|
+
* The returned promise resolves when this call's write completes.
|
|
832
|
+
*/
|
|
833
|
+
write(data: Omit<RunManifestData, 'generation' | 'updated_at'>): Promise<void>;
|
|
834
|
+
/**
|
|
835
|
+
* Return a bound `RunManifest` instance without performing any file I/O.
|
|
836
|
+
*
|
|
837
|
+
* Use `open()` when you want to call instance methods (`read()`, `patchCLIFlags()`)
|
|
838
|
+
* on an existing run without reading the manifest upfront.
|
|
839
|
+
*
|
|
840
|
+
* ```typescript
|
|
841
|
+
* await RunManifest.open(runId, runsDir).patchCLIFlags(cliFlags)
|
|
842
|
+
* ```
|
|
843
|
+
*/
|
|
844
|
+
static open(runId: string, baseDir?: string, doltAdapter?: IDoltAdapter | null): RunManifest;
|
|
845
|
+
/**
|
|
846
|
+
* Raw implementation — must only be called from within `_enqueue`.
|
|
847
|
+
*/
|
|
848
|
+
private _patchCLIFlagsImpl;
|
|
849
|
+
/**
|
|
850
|
+
* Read the current manifest (or create a minimal default), merge the provided
|
|
851
|
+
* CLI flags into `cli_flags`, and write the result atomically.
|
|
852
|
+
*
|
|
853
|
+
* Enqueues the operation via `_enqueue` so concurrent calls are serialized.
|
|
854
|
+
* Non-fatal: callers should wrap in try/catch and log a warning on failure.
|
|
855
|
+
* The pipeline must not abort if manifest write fails.
|
|
856
|
+
*/
|
|
857
|
+
patchCLIFlags(flags: CliFlags): Promise<void>;
|
|
858
|
+
/**
|
|
859
|
+
* Raw implementation — must only be called from within `_enqueue`.
|
|
860
|
+
*/
|
|
861
|
+
private _patchStoryStateImpl;
|
|
862
|
+
/**
|
|
863
|
+
* Atomically upsert the per-story lifecycle state for a single story key.
|
|
864
|
+
*
|
|
865
|
+
* Reads the current manifest (or creates a minimal default if absent),
|
|
866
|
+
* shallowly merges `updates` into `per_story_state[storyKey]`, and writes
|
|
867
|
+
* the result atomically via a single `write()` call.
|
|
868
|
+
*
|
|
869
|
+
* Fields not included in `updates` on an existing entry are preserved unchanged.
|
|
870
|
+
*
|
|
871
|
+
* Enqueues the operation via `_enqueue` so concurrent calls are serialized.
|
|
872
|
+
* Non-fatal: callers MUST wrap in `.catch((err) => logger.warn(...))`.
|
|
873
|
+
* The pipeline must never abort due to a manifest write failure.
|
|
874
|
+
*
|
|
875
|
+
* @param storyKey - Story key (e.g. '52-4')
|
|
876
|
+
* @param updates - Partial PerStoryState fields to merge
|
|
877
|
+
*/
|
|
878
|
+
patchStoryState(storyKey: string, updates: Partial<PerStoryState>): Promise<void>;
|
|
879
|
+
/**
|
|
880
|
+
* Raw implementation — must only be called from within `_enqueue`.
|
|
881
|
+
*/
|
|
882
|
+
private _patchRunStatusImpl;
|
|
883
|
+
/**
|
|
884
|
+
* Atomically update the run-level status fields in the manifest.
|
|
885
|
+
*
|
|
886
|
+
* Reads the current manifest (or creates a minimal default if absent),
|
|
887
|
+
* merges the provided status updates at the top level, and writes the
|
|
888
|
+
* result atomically via a single `write()` call.
|
|
889
|
+
*
|
|
890
|
+
* Enqueues the operation via `_enqueue` so concurrent calls are serialized
|
|
891
|
+
* (preserves the single-writer guarantee from Epic 57-1).
|
|
892
|
+
* Non-fatal: callers MUST wrap in `.catch((err) => logger.warn(...))`.
|
|
893
|
+
*
|
|
894
|
+
* @param updates - Top-level status fields to merge (run_status, stopped_reason, stopped_at)
|
|
895
|
+
*/
|
|
896
|
+
patchRunStatus(updates: {
|
|
897
|
+
run_status?: RunManifestData['run_status'];
|
|
898
|
+
stopped_reason?: string;
|
|
899
|
+
stopped_at?: string;
|
|
900
|
+
}): Promise<void>;
|
|
901
|
+
/**
|
|
902
|
+
* Raw implementation — must only be called from within `_enqueue`.
|
|
903
|
+
*/
|
|
904
|
+
private _appendRecoveryEntryImpl;
|
|
905
|
+
/**
|
|
906
|
+
* Atomically append a recovery entry and update cost accumulation.
|
|
907
|
+
*
|
|
908
|
+
* Reads the current manifest, appends `entry` to `recovery_history[]`,
|
|
909
|
+
* increments `cost_accumulation.per_story[entry.story_key]` by `entry.cost_usd`,
|
|
910
|
+
* increments `cost_accumulation.run_total` by `entry.cost_usd`, then writes
|
|
911
|
+
* atomically via a single `write()` call.
|
|
912
|
+
*
|
|
913
|
+
* Enqueues the operation via `_enqueue` so concurrent calls are serialized.
|
|
914
|
+
* Non-fatal: callers MUST wrap in `.catch((err) => logger.warn(...))`.
|
|
915
|
+
* The pipeline must never abort due to a manifest write failure.
|
|
916
|
+
*
|
|
917
|
+
* `entry.cost_usd` is the cost of this single retry attempt only (NOT cumulative).
|
|
918
|
+
* Cumulative per-story retry cost is tracked in `cost_accumulation.per_story`.
|
|
919
|
+
*
|
|
920
|
+
* @param entry - Recovery entry to append (attempt_number is 1-indexed)
|
|
921
|
+
*/
|
|
922
|
+
appendRecoveryEntry(entry: RecoveryEntry): Promise<void>;
|
|
923
|
+
/**
|
|
924
|
+
* Raw implementation — must only be called from within `_enqueue`.
|
|
925
|
+
*/
|
|
926
|
+
private _appendProposalImpl;
|
|
927
|
+
/**
|
|
928
|
+
* Atomically append a proposal to `pending_proposals`, deduplicating by storyKey.
|
|
929
|
+
*
|
|
930
|
+
* If a proposal with the same `storyKey` (or `story_key`) is already present in
|
|
931
|
+
* `pending_proposals`, the call is a silent no-op (idempotent). This guards
|
|
932
|
+
* against duplicate proposals from concurrent or repeated recovery invocations.
|
|
933
|
+
*
|
|
934
|
+
* Enqueues the operation via `_enqueue` so concurrent calls are serialized.
|
|
935
|
+
* Non-fatal: callers MUST wrap in `.catch((err) => logger.warn(...))`.
|
|
936
|
+
* The pipeline must never abort due to a manifest write failure.
|
|
937
|
+
*
|
|
938
|
+
* Story 73-1 (Recovery Engine). Canonical manifest helper per AC3.
|
|
939
|
+
*
|
|
940
|
+
* @param proposal - Proposal to append (id, created_at, description, type + Epic 73 fields)
|
|
941
|
+
*/
|
|
942
|
+
appendProposal(proposal: Proposal): Promise<void>;
|
|
943
|
+
/**
|
|
944
|
+
* Create a new manifest with `generation: 0` and write it.
|
|
945
|
+
* Returns a bound `RunManifest` instance.
|
|
946
|
+
*/
|
|
947
|
+
static create(runId: string, initialData: Omit<RunManifestData, 'generation' | 'updated_at' | 'created_at'>, baseDir?: string, doltAdapter?: IDoltAdapter | null): Promise<RunManifest>;
|
|
948
|
+
/**
|
|
949
|
+
* Read a manifest from disk with multi-tier fallback.
|
|
950
|
+
*
|
|
951
|
+
* Attempts sources in order:
|
|
952
|
+
* 1. Primary `.json`
|
|
953
|
+
* 2. Backup `.json.bak` (preferred over primary if generation is higher)
|
|
954
|
+
* 3. Temporary `.json.tmp`
|
|
955
|
+
* 4. Dolt degraded reconstruction (if doltAdapter is provided)
|
|
956
|
+
*
|
|
957
|
+
* Generation tiebreak: if `.bak` has a higher `generation` than primary,
|
|
958
|
+
* `.bak` is preferred (indicates primary was overwritten mid-rename).
|
|
959
|
+
*
|
|
960
|
+
* @throws ManifestReadError if all sources fail
|
|
961
|
+
*/
|
|
962
|
+
static read(runId: string, baseDir?: string, doltAdapter?: IDoltAdapter | null): Promise<RunManifestData>;
|
|
963
|
+
}
|
|
964
|
+
|
|
965
|
+
//#endregion
|
|
966
|
+
//#region src/modules/recovery-engine/index.d.ts
|
|
967
|
+
//# sourceMappingURL=run-manifest.d.ts.map
|
|
968
|
+
/**
|
|
969
|
+
* Failure information passed to the Recovery Engine.
|
|
970
|
+
*/
|
|
971
|
+
interface RecoveryFailure {
|
|
972
|
+
/** Root cause classification (e.g. 'build-failure', 'scope-violation'). */
|
|
973
|
+
rootCause: string;
|
|
974
|
+
/** Raw findings from the verification summary. */
|
|
975
|
+
findings?: unknown[];
|
|
976
|
+
/** Narrative diagnosis text to prepend to the retry prompt (Tier A). */
|
|
977
|
+
diagnosis?: string;
|
|
978
|
+
}
|
|
979
|
+
/**
|
|
980
|
+
* Retry budget for the current story.
|
|
981
|
+
*/
|
|
982
|
+
interface RecoveryBudget {
|
|
983
|
+
/** Remaining retry attempts. 0 = budget exhausted. */
|
|
984
|
+
remaining: number;
|
|
985
|
+
/** Maximum retry attempts configured for this run. */
|
|
986
|
+
max: number;
|
|
987
|
+
}
|
|
988
|
+
/**
|
|
989
|
+
* Input for `runRecoveryEngine`.
|
|
990
|
+
* Modelled on StaleVerificationRecoveryInput from cross-story-race-recovery.ts (Epic 70).
|
|
991
|
+
*/
|
|
992
|
+
interface RecoveryEngineInput {
|
|
993
|
+
/** Run ID for event emission and manifest access. */
|
|
994
|
+
runId: string;
|
|
995
|
+
/** Story key that failed. */
|
|
996
|
+
storyKey: string;
|
|
997
|
+
/** Failure information (root cause, findings, diagnosis). */
|
|
998
|
+
failure: RecoveryFailure;
|
|
999
|
+
/** Retry budget for the current story. */
|
|
1000
|
+
budget: RecoveryBudget;
|
|
1001
|
+
/** Typed event bus for emitting recovery lifecycle events. */
|
|
1002
|
+
bus: TypedEventBus<SdlcEvents>;
|
|
1003
|
+
/** RunManifest instance for state reads and writes. */
|
|
1004
|
+
manifest: RunManifest;
|
|
1005
|
+
/**
|
|
1006
|
+
* Database adapter for Dolt persistence and work-graph dependency queries.
|
|
1007
|
+
* Used by back-pressure logic to query story_dependencies.
|
|
1008
|
+
*/
|
|
1009
|
+
adapter: DatabaseAdapter;
|
|
1010
|
+
/**
|
|
1011
|
+
* Engine mode — 'linear' disables work-graph dependency resolution and pauses
|
|
1012
|
+
* all pending dispatches when >= 2 proposals exist.
|
|
1013
|
+
*/
|
|
1014
|
+
engine?: 'linear' | 'graph';
|
|
1015
|
+
/**
|
|
1016
|
+
* Keys of stories still pending/dispatched at the time of recovery invocation.
|
|
1017
|
+
* Used by back-pressure logic to compute pause/continue sets.
|
|
1018
|
+
*/
|
|
1019
|
+
pendingStoryKeys?: string[];
|
|
1020
|
+
}
|
|
1021
|
+
/**
|
|
1022
|
+
* Discriminated result union returned by `runRecoveryEngine`.
|
|
1023
|
+
* The orchestrator reads this result and acts accordingly (AC9).
|
|
1024
|
+
*/
|
|
1025
|
+
type RecoveryEngineResult = {
|
|
1026
|
+
/** Tier A: re-dispatch with enriched prompt. */
|
|
1027
|
+
action: 'retry';
|
|
1028
|
+
attempt: number;
|
|
1029
|
+
retryBudgetRemaining: number;
|
|
1030
|
+
/** Enriched prompt with diagnosis + findings prepended (ready for dispatcher). */
|
|
1031
|
+
enrichedPrompt?: string;
|
|
1032
|
+
} | {
|
|
1033
|
+
/** Tier B: re-scope proposal appended. Apply back-pressure. */
|
|
1034
|
+
action: 'propose';
|
|
1035
|
+
storyKey: string;
|
|
1036
|
+
pendingProposalsCount: number;
|
|
1037
|
+
/** Story keys whose dispatch should be paused (depend on a proposed story). */
|
|
1038
|
+
pause?: string[];
|
|
1039
|
+
/** Story keys that can continue (independent of proposed stories). */
|
|
1040
|
+
continue?: string[];
|
|
1041
|
+
/** When true (linear mode): pause ALL remaining dispatches. */
|
|
1042
|
+
pauseAll?: boolean;
|
|
1043
|
+
} | {
|
|
1044
|
+
/** Tier C: operator intervention required (Decision Router / Story 73-2 prompt). */
|
|
1045
|
+
action: 'halt';
|
|
1046
|
+
} | {
|
|
1047
|
+
/** Safety valve: >= 5 proposals — halt the entire run (exit 1). */
|
|
1048
|
+
action: 'halt-entire-run';
|
|
1049
|
+
pendingProposalsCount: number;
|
|
1050
|
+
};
|
|
1051
|
+
/**
|
|
1052
|
+
* Classify a failure into a recovery action tier.
|
|
1053
|
+
*
|
|
1054
|
+
* Pure function — no imports from I/O modules, no side effects.
|
|
1055
|
+
* The action handler (`runRecoveryEngine`) owns all I/O.
|
|
1056
|
+
*
|
|
1057
|
+
* Classification rules:
|
|
1058
|
+
* 1. HALT root causes → 'halt' always
|
|
1059
|
+
* 2. PROPOSE root causes → 'propose' always (structural, not retry-able)
|
|
1060
|
+
* 3. RETRY root causes:
|
|
1061
|
+
* - budget.remaining > 0 → 'retry'
|
|
1062
|
+
* - budget.remaining <= 0 → 'propose' (exhausted, escalate)
|
|
1063
|
+
* 4. Unknown root cause → 'propose' (safe default)
|
|
1064
|
+
*
|
|
1065
|
+
* @param failure - Failure with rootCause field
|
|
1066
|
+
* @param budget - Retry budget with remaining count
|
|
1067
|
+
* @returns 'retry' | 'propose' | 'halt'
|
|
1068
|
+
*/
|
|
1069
|
+
declare function classifyRecoveryAction(failure: Pick<RecoveryFailure, 'rootCause'>, budget: RecoveryBudget): 'retry' | 'propose' | 'halt';
|
|
1070
|
+
/**
|
|
1071
|
+
* Recovery Engine action handler.
|
|
1072
|
+
*
|
|
1073
|
+
* Consumes a halt decision (failure + budget) and applies tiered recovery:
|
|
1074
|
+
*
|
|
1075
|
+
* Tier A (retry): emits recovery:tier-a-retry, returns enriched prompt for re-dispatch.
|
|
1076
|
+
* Tier B (propose): appends Proposal to RunManifest.pending_proposals, applies back-pressure.
|
|
1077
|
+
* Tier C (halt): emits recovery:tier-c-halt, returns halt action for Decision Router.
|
|
1078
|
+
*
|
|
1079
|
+
* Safety valve: if pending_proposals.length >= 5 after any Tier B append,
|
|
1080
|
+
* emits pipeline:halted-pending-proposals and returns halt-entire-run.
|
|
1081
|
+
*
|
|
1082
|
+
* Back-pressure:
|
|
1083
|
+
* >= 2 proposals + work graph: pauses dependent stories, continues independent ones.
|
|
1084
|
+
* >= 2 proposals + linear mode: pauses ALL remaining dispatches.
|
|
1085
|
+
*
|
|
1086
|
+
* Idempotency: re-invoking on a story already in pending_proposals is a no-op
|
|
1087
|
+
* (handled inside RunManifest.appendProposal via storyKey deduplication).
|
|
1088
|
+
*
|
|
1089
|
+
* Canonical helper discipline: all manifest reads/writes via RunManifest class.
|
|
1090
|
+
* No direct JSON file access. No new aggregate manifest formats.
|
|
1091
|
+
*
|
|
1092
|
+
* @param input - Recovery engine input (see RecoveryEngineInput)
|
|
1093
|
+
* @returns Recovery action result for the orchestrator to act on
|
|
1094
|
+
*/
|
|
1095
|
+
declare function runRecoveryEngine(input: RecoveryEngineInput): Promise<RecoveryEngineResult>;
|
|
1096
|
+
|
|
1097
|
+
//#endregion
|
|
1098
|
+
//# sourceMappingURL=index.d.ts.map
|
|
1099
|
+
|
|
1100
|
+
export { RecoveryBudget, RecoveryEngineInput, RecoveryEngineResult, RecoveryFailure, classifyRecoveryAction, runRecoveryEngine };
|
|
1101
|
+
//# sourceMappingURL=index.d.ts.map
|