@mobrienv/autoloop-harness 0.8.0 → 0.9.1

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.
Files changed (116) hide show
  1. package/dist/acceptance.d.ts +31 -0
  2. package/dist/acceptance.js +69 -0
  3. package/dist/acceptance.js.map +1 -0
  4. package/dist/ask.d.ts +16 -0
  5. package/dist/ask.js +47 -0
  6. package/dist/ask.js.map +1 -0
  7. package/dist/backend/types.d.ts +3 -0
  8. package/dist/backend/types.js +3 -0
  9. package/dist/backend/types.js.map +1 -1
  10. package/dist/circuit-breaker.d.ts +27 -0
  11. package/dist/circuit-breaker.js +41 -0
  12. package/dist/circuit-breaker.js.map +1 -0
  13. package/dist/completion-lint.d.ts +29 -0
  14. package/dist/completion-lint.js +51 -0
  15. package/dist/completion-lint.js.map +1 -0
  16. package/dist/config-helpers.js +141 -8
  17. package/dist/config-helpers.js.map +1 -1
  18. package/dist/control/command-adapter.d.ts +19 -0
  19. package/dist/control/command-adapter.js +57 -0
  20. package/dist/control/command-adapter.js.map +1 -0
  21. package/dist/control/dispatch.js +14 -0
  22. package/dist/control/dispatch.js.map +1 -1
  23. package/dist/control/types.d.ts +11 -2
  24. package/dist/display.d.ts +9 -2
  25. package/dist/display.js +37 -1
  26. package/dist/display.js.map +1 -1
  27. package/dist/emit.d.ts +27 -0
  28. package/dist/emit.js +321 -2
  29. package/dist/emit.js.map +1 -1
  30. package/dist/events.d.ts +36 -3
  31. package/dist/fanout-runner.d.ts +25 -0
  32. package/dist/fanout-runner.js +54 -0
  33. package/dist/fanout-runner.js.map +1 -0
  34. package/dist/file-mod-audit.d.ts +29 -0
  35. package/dist/file-mod-audit.js +63 -0
  36. package/dist/file-mod-audit.js.map +1 -0
  37. package/dist/git-diff.d.ts +19 -0
  38. package/dist/git-diff.js +74 -0
  39. package/dist/git-diff.js.map +1 -0
  40. package/dist/hooks.d.ts +78 -0
  41. package/dist/hooks.js +239 -0
  42. package/dist/hooks.js.map +1 -0
  43. package/dist/index.d.ts +15 -2
  44. package/dist/index.js +181 -5
  45. package/dist/index.js.map +1 -1
  46. package/dist/intent.d.ts +45 -0
  47. package/dist/intent.js +127 -0
  48. package/dist/intent.js.map +1 -0
  49. package/dist/iteration-diff.d.ts +32 -0
  50. package/dist/iteration-diff.js +92 -0
  51. package/dist/iteration-diff.js.map +1 -0
  52. package/dist/iteration.d.ts +20 -0
  53. package/dist/iteration.js +383 -16
  54. package/dist/iteration.js.map +1 -1
  55. package/dist/metareview.d.ts +9 -1
  56. package/dist/metareview.js +68 -30
  57. package/dist/metareview.js.map +1 -1
  58. package/dist/notify.d.ts +9 -2
  59. package/dist/notify.js +16 -4
  60. package/dist/notify.js.map +1 -1
  61. package/dist/parallel.d.ts +16 -0
  62. package/dist/parallel.js +28 -1
  63. package/dist/parallel.js.map +1 -1
  64. package/dist/postconditions.d.ts +25 -0
  65. package/dist/postconditions.js +107 -0
  66. package/dist/postconditions.js.map +1 -0
  67. package/dist/postfire-verify.d.ts +31 -0
  68. package/dist/postfire-verify.js +64 -0
  69. package/dist/postfire-verify.js.map +1 -0
  70. package/dist/premature-quit.d.ts +25 -0
  71. package/dist/premature-quit.js +54 -0
  72. package/dist/premature-quit.js.map +1 -0
  73. package/dist/progress.d.ts +20 -0
  74. package/dist/progress.js +56 -0
  75. package/dist/progress.js.map +1 -0
  76. package/dist/prompt.js +8 -2
  77. package/dist/prompt.js.map +1 -1
  78. package/dist/provisional.d.ts +42 -0
  79. package/dist/provisional.js +92 -0
  80. package/dist/provisional.js.map +1 -0
  81. package/dist/registry-bridge.js +25 -0
  82. package/dist/registry-bridge.js.map +1 -1
  83. package/dist/resume.d.ts +61 -0
  84. package/dist/resume.js +226 -0
  85. package/dist/resume.js.map +1 -0
  86. package/dist/scratchpad.js +27 -8
  87. package/dist/scratchpad.js.map +1 -1
  88. package/dist/stage.d.ts +21 -0
  89. package/dist/stage.js +131 -0
  90. package/dist/stage.js.map +1 -0
  91. package/dist/stop.d.ts +29 -2
  92. package/dist/stop.js +106 -0
  93. package/dist/stop.js.map +1 -1
  94. package/dist/suspend-state.d.ts +32 -0
  95. package/dist/suspend-state.js +104 -0
  96. package/dist/suspend-state.js.map +1 -0
  97. package/dist/tamper.d.ts +21 -0
  98. package/dist/tamper.js +88 -0
  99. package/dist/tamper.js.map +1 -0
  100. package/dist/types.d.ts +247 -2
  101. package/dist/types.js +91 -1
  102. package/dist/types.js.map +1 -1
  103. package/dist/wave/finalize-wave.d.ts +15 -5
  104. package/dist/wave/finalize-wave.js +56 -2
  105. package/dist/wave/finalize-wave.js.map +1 -1
  106. package/dist/wave/launch-branches.d.ts +23 -2
  107. package/dist/wave/launch-branches.js +56 -3
  108. package/dist/wave/launch-branches.js.map +1 -1
  109. package/dist/wave/stage-branch-runner.d.ts +12 -0
  110. package/dist/wave/stage-branch-runner.js +130 -0
  111. package/dist/wave/stage-branch-runner.js.map +1 -0
  112. package/dist/wave/types.d.ts +40 -2
  113. package/dist/wave.d.ts +32 -1
  114. package/dist/wave.js +163 -34
  115. package/dist/wave.js.map +1 -1
  116. package/package.json +83 -3
package/dist/types.d.ts CHANGED
@@ -2,6 +2,7 @@ import type { AcpSession } from "@mobrienv/autoloop-backends/acp-client";
2
2
  import type { ClaudeSdkSession } from "@mobrienv/autoloop-backends/claude-sdk-client";
3
3
  import type { PiSession } from "@mobrienv/autoloop-backends/pi-rpc-client";
4
4
  import type { AgentMap } from "@mobrienv/autoloop-core/agent-map";
5
+ import type { HookSpec } from "@mobrienv/autoloop-core/hooks-schema";
5
6
  import type * as topo from "@mobrienv/autoloop-core/topology";
6
7
  import type { LiveControlAdapter } from "./control/adapter.js";
7
8
  import type { LoopEventEmitter } from "./events.js";
@@ -11,13 +12,22 @@ export interface LaunchMetadata {
11
12
  trigger: TriggerSource;
12
13
  createdAt: string;
13
14
  parentRunId: string;
15
+ /**
16
+ * Absolute path to a single-file (`.toml`) preset, when the run was launched
17
+ * from one. Config and topology are loaded from this file instead of the
18
+ * `projectDir`'s `autoloops.toml` + `topology.toml`. Empty for directory
19
+ * presets.
20
+ */
21
+ presetFile?: string;
14
22
  }
15
23
  export interface ProfileInfo {
16
24
  active: string[];
17
25
  fragments: Map<string, string>;
18
26
  warnings: string[];
19
27
  }
20
- export type VerdictKind = "CONTINUE" | "REDIRECT" | "TAKEOVER" | "EXIT";
28
+ export type VerdictKind = "CONTINUE" | "REDIRECT" | "TAKEOVER" | "EXIT" | "UNKNOWN";
29
+ /** What to do when a metareview yields an UNKNOWN verdict. */
30
+ export type ReviewOnError = "hold" | "exit" | "continue";
21
31
  export interface Verdict {
22
32
  verdict: VerdictKind;
23
33
  confidence: number;
@@ -26,6 +36,10 @@ export interface Verdict {
26
36
  takeover_output?: string;
27
37
  suggestions?: string[];
28
38
  }
39
+ /** Live `command`-backend child process, tracked for signal-based interrupt. */
40
+ export interface CommandSession {
41
+ pid: number;
42
+ }
29
43
  export interface LoopContext {
30
44
  objective: string;
31
45
  topology: topo.Topology;
@@ -39,11 +53,93 @@ export interface LoopContext {
39
53
  maxIterationRuntimeMs?: number;
40
54
  /** Loop wall-clock budget in ms (0 = disabled). */
41
55
  maxRuntimeMs?: number;
56
+ /**
57
+ * Circuit-breaker: max transient/rate-limit pauses before the breaker opens
58
+ * and the run stops with a typed reason. Default 3.
59
+ */
60
+ transientMaxPauses?: number;
61
+ /** Base backoff (ms) for the transient retry ladder. Default 5000. */
62
+ transientPauseMs?: number;
63
+ /** Upper bound (ms) on the exponential transient backoff. Default 30000. */
64
+ transientBackoffCapMs?: number;
65
+ /**
66
+ * Max times a premature quit (stall with authorized work remaining and no
67
+ * blocker) is re-armed before it escalates to attention. Default 1.
68
+ */
69
+ prematureMaxRearms?: number;
42
70
  };
43
71
  completion: {
44
72
  promise: string;
45
73
  event: string;
46
74
  requiredEvents: string[];
75
+ /**
76
+ * Ralph-parity ordering policy (opt-in, default false). When true, a
77
+ * completion claim via `event` is rejected if any other event was
78
+ * emitted after it within the same turn — set-membership alone is not
79
+ * enough. Order-insensitive behavior (unchanged) when false.
80
+ */
81
+ mustBeLast: boolean;
82
+ };
83
+ /**
84
+ * Permission-enforcement policies (orthogonal to completion gating).
85
+ */
86
+ policy: {
87
+ /**
88
+ * Ralph-parity emit-boundary audit (opt-in, default false). After every
89
+ * iteration, diffs the working tree against HEAD; if the acting role has
90
+ * `disallowedTools`/`readOnly` and files changed, emits
91
+ * `policy.file_modification_violation` with the role + file list. Purely
92
+ * observational — never alters loop control flow on its own.
93
+ */
94
+ fileModAudit: boolean;
95
+ };
96
+ /**
97
+ * Out-of-band acceptance gate. On a done-claim the HARNESS (not the agent's
98
+ * session) runs every `verifyCmds` entry in a clean shell; completion is
99
+ * accepted only when all exit 0. A failing command blocks completion and
100
+ * re-injects its output. Empty `verifyCmds` disables the gate.
101
+ */
102
+ acceptance: {
103
+ verifyCmds: string[];
104
+ timeoutMs: number;
105
+ /**
106
+ * Declarative required-absence guards evaluated at the acceptance gate
107
+ * regardless of agent claims. Each defaults off (opt-in per preset) so
108
+ * existing loops are unaffected. They catch reward-hacks the LLM gates miss:
109
+ * leftover TODO/FIXME, skipped/only/xfail tests, committed secrets, and a
110
+ * dirty/untracked working tree.
111
+ */
112
+ assertNoTodo: boolean;
113
+ assertNoSkippedTests: boolean;
114
+ assertNoSecrets: boolean;
115
+ assertCleanTree: boolean;
116
+ /**
117
+ * Anti-reward-hack screen: under bypassPermissions the maker can edit the
118
+ * very tests that gate it. When on, a test-backed done-claim is blocked if
119
+ * the run modified test files or inserted tamper patterns (skip/only/xfail,
120
+ * early exit, tautological assertions) on test paths. Default off.
121
+ */
122
+ screenTestTamper: boolean;
123
+ /**
124
+ * Intent-binding acceptance criteria captured at loop start (the contract
125
+ * between what was asked and what is accepted). Sourced from
126
+ * `[acceptance].criteria` and an "Acceptance criteria" section of the
127
+ * objective. A criterion may bind a deterministic check with ` :: <shell
128
+ * cmd>`; checked criteria gate completion, unchecked ones are advisory.
129
+ */
130
+ criteria: string[];
131
+ };
132
+ /**
133
+ * Human-in-the-loop. When an agent emits `ask.event`, the loop blocks until
134
+ * an operator responds (via the `respond` control verb) or `timeoutMs`
135
+ * elapses, then injects the answer into the next prompt as guidance.
136
+ * Disabled when `enabled` is false (ask.event is empty).
137
+ */
138
+ ask: {
139
+ enabled: boolean;
140
+ event: string;
141
+ timeoutMs: number;
142
+ pollMs: number;
47
143
  };
48
144
  backend: {
49
145
  kind: string;
@@ -55,6 +151,14 @@ export interface LoopContext {
55
151
  trustAllTools: boolean;
56
152
  agent: string;
57
153
  model: string;
154
+ profile?: string;
155
+ disallowedTools: string[];
156
+ /**
157
+ * Opt-in cost-telemetry convention for `command`-kind backends: `"file"`
158
+ * reads a JSON usage object the wrapped command wrote to
159
+ * `$AUTOLOOP_USAGE_FILE`. Empty disables extraction (default).
160
+ */
161
+ usageFrom: string;
58
162
  };
59
163
  review: {
60
164
  enabled: boolean;
@@ -70,11 +174,52 @@ export interface LoopContext {
70
174
  trustAllTools: boolean;
71
175
  agent: string;
72
176
  model: string;
177
+ profile?: string;
178
+ /**
179
+ * Fail-closed routing for an UNKNOWN verdict (malformed/empty/timed-out
180
+ * review, or confidence below `minConfidence`). Default `hold`: stop the
181
+ * loop and raise attention rather than silently continuing.
182
+ */
183
+ onError: ReviewOnError;
184
+ /**
185
+ * A parsed verdict whose confidence is below this threshold is downgraded
186
+ * to UNKNOWN. Default 0.5. Set 0 to disable confidence gating.
187
+ */
188
+ minConfidence: number;
73
189
  };
74
190
  parallel: {
75
191
  enabled: boolean;
76
192
  maxBranches: number;
77
193
  branchTimeoutMs: number;
194
+ /** Loop-level default wave completion strategy (role `aggregate` overrides). */
195
+ aggregate: {
196
+ mode: "wait_for_all" | "first_success" | "timeout";
197
+ timeoutMs: number;
198
+ };
199
+ };
200
+ /**
201
+ * Fan-out `[[stage]]` execution knobs. `concurrency` bounds how many stage
202
+ * branches run at once run-wide (defaults to `defaultConcurrency()`, the
203
+ * same core/os-derived ceiling waves use); `branchTimeoutMs` bounds one
204
+ * branch's wall-clock time.
205
+ */
206
+ stage: {
207
+ concurrency: number;
208
+ branchTimeoutMs: number;
209
+ };
210
+ hooks: {
211
+ preRun: string;
212
+ preIteration: string;
213
+ postIteration: string;
214
+ postRun: string;
215
+ strict: boolean;
216
+ /**
217
+ * Structured per-hook specs (legacy flat keys + `[[hook]]` entries),
218
+ * merged and template-expanded. This is the engine's source of truth;
219
+ * the flat fields above remain for backward compatibility with existing
220
+ * callers/tests but `specs` is what `runPhaseHooks` iterates.
221
+ */
222
+ specs: HookSpec[];
78
223
  };
79
224
  memory: {
80
225
  budgetChars: number;
@@ -85,6 +230,16 @@ export interface LoopContext {
85
230
  harness: {
86
231
  instructions: string;
87
232
  };
233
+ /**
234
+ * Preset-declared progress metric: a command whose numeric stdout is the
235
+ * scalar (tests passing, coverage, tasks closed, ...) journaled per iteration
236
+ * to enable drift/convergence detection. Disabled when `metricCmd` is "".
237
+ */
238
+ progress?: {
239
+ metricCmd: string;
240
+ name: string;
241
+ timeoutMs: number;
242
+ };
88
243
  profiles: ProfileInfo;
89
244
  paths: {
90
245
  projectDir: string;
@@ -113,6 +268,13 @@ export interface LoopContext {
113
268
  logLevel: string;
114
269
  branchMode: boolean;
115
270
  isolationMode: string;
271
+ /**
272
+ * Forces every fan-out stage branch to relaunch rather than reuse a
273
+ * journaled `stage.branch.finish` record from an interrupted prior
274
+ * attempt (`run --no-resume` / `resume --no-resume`). Lives on `runtime`
275
+ * (not config-derived `stage`) because it survives `reloadLoop`.
276
+ */
277
+ noResume?: boolean;
116
278
  };
117
279
  launch: LaunchMetadata;
118
280
  store: Record<string, unknown>;
@@ -141,13 +303,31 @@ export interface LoopContext {
141
303
  claudeSdkSession: {
142
304
  current: ClaudeSdkSession | undefined;
143
305
  };
306
+ /**
307
+ * Live `command`-backend child process holder, aliased like the other
308
+ * session holders. Populated for the duration of an in-flight `command`
309
+ * iteration (async-spawned, not `execSync`) so `commandControlAdapter` can
310
+ * signal it (SIGUSR1 → SIGTERM → SIGKILL) for the `interrupt` verb. Cleared
311
+ * once the iteration's process exits.
312
+ */
313
+ commandSession: {
314
+ current: CommandSession | undefined;
315
+ };
144
316
  lastVerdict?: Verdict;
145
317
  /** Optional structured-event emitter, forwarded from RunOptions.onEvent. */
146
318
  onEvent?: LoopEventEmitter;
147
319
  controlAdapter?: LiveControlAdapter;
320
+ /** Abort signal for the run; consulted while blocking on a human ask. */
321
+ signal?: AbortSignal;
148
322
  }
149
323
  export interface RunOptions {
150
324
  workDir?: string;
325
+ /**
326
+ * Absolute path to a single-file (`.toml`) preset. When set, config and
327
+ * topology are loaded from this file rather than from `projectDir`'s
328
+ * `autoloops.toml` + `topology.toml`.
329
+ */
330
+ presetFile?: string;
151
331
  backendOverride?: Record<string, unknown>;
152
332
  configOverride?: Record<string, unknown>;
153
333
  logLevel?: string | null;
@@ -163,6 +343,11 @@ export interface RunOptions {
163
343
  mergeStrategy?: string;
164
344
  automerge?: boolean;
165
345
  keepWorktree?: boolean;
346
+ /**
347
+ * Force every fan-out stage branch to relaunch rather than reuse a
348
+ * journaled `stage.branch.finish` record from an interrupted prior attempt.
349
+ */
350
+ noResume?: boolean;
166
351
  /**
167
352
  * Optional abort signal. When provided, the CLI (or SDK caller) owns
168
353
  * process signal handling; the harness only listens to this signal for
@@ -176,8 +361,68 @@ export interface RunOptions {
176
361
  */
177
362
  onEvent?: LoopEventEmitter;
178
363
  }
364
+ /**
365
+ * Versioned, exhaustive set of terminal `RunSummary.stopReason` values.
366
+ * Every literal string ever assigned to `RunSummary.stopReason` anywhere in
367
+ * this package must appear here. Consumers (e.g. ralph v3) can rely on this
368
+ * being a closed set instead of an unbounded `string`, and can use
369
+ * {@link STOP_REASONS} to validate values arriving over an untrusted
370
+ * boundary (e.g. a persisted journal or IPC payload) at runtime.
371
+ *
372
+ * Emission sites (see the enforcement test in
373
+ * `packages/harness/test/harness/stop-reason.test.ts`):
374
+ * - `stop.ts` — `max_iterations`, `backend_failed`, `backend_timeout`,
375
+ * `stalled`, `cost_budget`, `max_runtime`, `review_unknown`,
376
+ * `premature_quit`, `suspended`, and `completeLoop()` which echoes the
377
+ * `completion_event`/`completion_promise`/`verdict_*` literals its
378
+ * callers pass in.
379
+ * - `index.ts` — `interrupted`, `error`, and `verdict_unknown` /
380
+ * `verdict_exit` / `verdict_takeover` (via `completeLoop`).
381
+ * - `iteration.ts` — `completion_event`, `completion_promise` (via
382
+ * `completeLoop`), and a direct `interrupted` result.
383
+ * - `provisional.ts` — `completion_held` (non-terminal `failure.diagnostic`
384
+ * event emitted while a done-claim is parked in `awaiting_acceptance`).
385
+ * - `circuit-breaker.ts` — `auth_failed`, `quota_exhausted`, `rate_limited`,
386
+ * `transient_error`, `backend_failed` (consumed by
387
+ * `stopBackendErrorClass` in `stop.ts`).
388
+ * - `wave.ts` / `wave/finalize-wave.ts` — `parallel_wave_timeout`,
389
+ * `parallel_wave_failed` (terminal, via `stopAfterParallelWave`), and
390
+ * `parallel_wave_invalid` (terminal — `wave.ts` rejects a dispatch while
391
+ * another wave is already active, or an unparsable payload, and that
392
+ * rejection is *also* routed through `stopAfterParallelWave`, ending the
393
+ * run). Note: `parallel_wave_complete` is a distinct, non-terminal
394
+ * internal value used only to trigger `continueAfterParallelJoin`; it
395
+ * never reaches `RunSummary` and is intentionally **not** part of this
396
+ * union.
397
+ *
398
+ * Categories (23 values enumerated by the originating issue, plus two
399
+ * additional real terminal literals — `parallel_wave_invalid` and `suspended` —
400
+ * discovered during implementation; see the parallel-wave and hooks notes above):
401
+ * - success (3): `completed`, `completion_event`, `completion_promise`
402
+ * - failures (7): `backend_failed`, `backend_timeout`, `auth_failed`,
403
+ * `quota_exhausted`, `rate_limited`, `transient_error`, `review_unknown`
404
+ * - stops (7): `max_iterations`, `stalled`, `cost_budget`, `max_runtime`,
405
+ * `premature_quit`, `interrupted`, `suspended`
406
+ * - verdicts (3): `verdict_exit`, `verdict_takeover`, `verdict_unknown`
407
+ * - held (1): `completion_held`
408
+ * - parallel (3): `parallel_wave_timeout`, `parallel_wave_failed`,
409
+ * `parallel_wave_invalid`
410
+ * - error handling (1): `error`
411
+ *
412
+ * Naming-collision warning: `stopReason` is also a field name on the ACP
413
+ * per-turn protocol (`"end_turn" | "cancelled" | "refusal"`, see
414
+ * `packages/cli/src/acp/*.ts` and `packages/backends/src/acp-client.ts`) and
415
+ * on the wave-branch-parsing domain (`BranchResult.stopReason`,
416
+ * `WaveResult.reason` in `wave/types.ts`, which additionally include the
417
+ * synthetic `"branch_process_failed"` value parsed from untrusted
418
+ * subprocess stdout). Those are unrelated, wider domains — do not widen
419
+ * this union to cover them, and do not narrow them to this union.
420
+ */
421
+ export declare const STOP_REASONS: readonly ["completed", "completion_event", "completion_promise", "backend_failed", "backend_timeout", "auth_failed", "quota_exhausted", "rate_limited", "transient_error", "review_unknown", "max_iterations", "stalled", "cost_budget", "max_runtime", "premature_quit", "interrupted", "suspended", "verdict_exit", "verdict_takeover", "verdict_unknown", "completion_held", "parallel_wave_timeout", "parallel_wave_failed", "parallel_wave_invalid", "error"];
422
+ /** See {@link STOP_REASONS} for documentation of every literal. */
423
+ export type StopReason = (typeof STOP_REASONS)[number];
179
424
  export interface RunSummary {
180
425
  iterations: number;
181
- stopReason: string;
426
+ stopReason: StopReason;
182
427
  runId?: string;
183
428
  }
package/dist/types.js CHANGED
@@ -1,2 +1,92 @@
1
- export {};
1
+ /**
2
+ * Versioned, exhaustive set of terminal `RunSummary.stopReason` values.
3
+ * Every literal string ever assigned to `RunSummary.stopReason` anywhere in
4
+ * this package must appear here. Consumers (e.g. ralph v3) can rely on this
5
+ * being a closed set instead of an unbounded `string`, and can use
6
+ * {@link STOP_REASONS} to validate values arriving over an untrusted
7
+ * boundary (e.g. a persisted journal or IPC payload) at runtime.
8
+ *
9
+ * Emission sites (see the enforcement test in
10
+ * `packages/harness/test/harness/stop-reason.test.ts`):
11
+ * - `stop.ts` — `max_iterations`, `backend_failed`, `backend_timeout`,
12
+ * `stalled`, `cost_budget`, `max_runtime`, `review_unknown`,
13
+ * `premature_quit`, `suspended`, and `completeLoop()` which echoes the
14
+ * `completion_event`/`completion_promise`/`verdict_*` literals its
15
+ * callers pass in.
16
+ * - `index.ts` — `interrupted`, `error`, and `verdict_unknown` /
17
+ * `verdict_exit` / `verdict_takeover` (via `completeLoop`).
18
+ * - `iteration.ts` — `completion_event`, `completion_promise` (via
19
+ * `completeLoop`), and a direct `interrupted` result.
20
+ * - `provisional.ts` — `completion_held` (non-terminal `failure.diagnostic`
21
+ * event emitted while a done-claim is parked in `awaiting_acceptance`).
22
+ * - `circuit-breaker.ts` — `auth_failed`, `quota_exhausted`, `rate_limited`,
23
+ * `transient_error`, `backend_failed` (consumed by
24
+ * `stopBackendErrorClass` in `stop.ts`).
25
+ * - `wave.ts` / `wave/finalize-wave.ts` — `parallel_wave_timeout`,
26
+ * `parallel_wave_failed` (terminal, via `stopAfterParallelWave`), and
27
+ * `parallel_wave_invalid` (terminal — `wave.ts` rejects a dispatch while
28
+ * another wave is already active, or an unparsable payload, and that
29
+ * rejection is *also* routed through `stopAfterParallelWave`, ending the
30
+ * run). Note: `parallel_wave_complete` is a distinct, non-terminal
31
+ * internal value used only to trigger `continueAfterParallelJoin`; it
32
+ * never reaches `RunSummary` and is intentionally **not** part of this
33
+ * union.
34
+ *
35
+ * Categories (23 values enumerated by the originating issue, plus two
36
+ * additional real terminal literals — `parallel_wave_invalid` and `suspended` —
37
+ * discovered during implementation; see the parallel-wave and hooks notes above):
38
+ * - success (3): `completed`, `completion_event`, `completion_promise`
39
+ * - failures (7): `backend_failed`, `backend_timeout`, `auth_failed`,
40
+ * `quota_exhausted`, `rate_limited`, `transient_error`, `review_unknown`
41
+ * - stops (7): `max_iterations`, `stalled`, `cost_budget`, `max_runtime`,
42
+ * `premature_quit`, `interrupted`, `suspended`
43
+ * - verdicts (3): `verdict_exit`, `verdict_takeover`, `verdict_unknown`
44
+ * - held (1): `completion_held`
45
+ * - parallel (3): `parallel_wave_timeout`, `parallel_wave_failed`,
46
+ * `parallel_wave_invalid`
47
+ * - error handling (1): `error`
48
+ *
49
+ * Naming-collision warning: `stopReason` is also a field name on the ACP
50
+ * per-turn protocol (`"end_turn" | "cancelled" | "refusal"`, see
51
+ * `packages/cli/src/acp/*.ts` and `packages/backends/src/acp-client.ts`) and
52
+ * on the wave-branch-parsing domain (`BranchResult.stopReason`,
53
+ * `WaveResult.reason` in `wave/types.ts`, which additionally include the
54
+ * synthetic `"branch_process_failed"` value parsed from untrusted
55
+ * subprocess stdout). Those are unrelated, wider domains — do not widen
56
+ * this union to cover them, and do not narrow them to this union.
57
+ */
58
+ export const STOP_REASONS = [
59
+ // success (3)
60
+ "completed",
61
+ "completion_event",
62
+ "completion_promise",
63
+ // failures (7)
64
+ "backend_failed",
65
+ "backend_timeout",
66
+ "auth_failed",
67
+ "quota_exhausted",
68
+ "rate_limited",
69
+ "transient_error",
70
+ "review_unknown",
71
+ // stops (7)
72
+ "max_iterations",
73
+ "stalled",
74
+ "cost_budget",
75
+ "max_runtime",
76
+ "premature_quit",
77
+ "interrupted",
78
+ "suspended",
79
+ // verdicts (3)
80
+ "verdict_exit",
81
+ "verdict_takeover",
82
+ "verdict_unknown",
83
+ // held (1)
84
+ "completion_held",
85
+ // parallel (3)
86
+ "parallel_wave_timeout",
87
+ "parallel_wave_failed",
88
+ "parallel_wave_invalid",
89
+ // error handling (1)
90
+ "error",
91
+ ];
2
92
  //# sourceMappingURL=types.js.map
package/dist/types.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":""}
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAsWA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAwDG;AACH,MAAM,CAAC,MAAM,YAAY,GAAG;IAC1B,cAAc;IACd,WAAW;IACX,kBAAkB;IAClB,oBAAoB;IACpB,eAAe;IACf,gBAAgB;IAChB,iBAAiB;IACjB,aAAa;IACb,iBAAiB;IACjB,cAAc;IACd,iBAAiB;IACjB,gBAAgB;IAChB,YAAY;IACZ,gBAAgB;IAChB,SAAS;IACT,aAAa;IACb,aAAa;IACb,gBAAgB;IAChB,aAAa;IACb,WAAW;IACX,eAAe;IACf,cAAc;IACd,kBAAkB;IAClB,iBAAiB;IACjB,WAAW;IACX,iBAAiB;IACjB,eAAe;IACf,uBAAuB;IACvB,sBAAsB;IACtB,uBAAuB;IACvB,qBAAqB;IACrB,OAAO;CACC,CAAC"}
@@ -1,9 +1,19 @@
1
1
  import type { IterationContext } from "../prompt.js";
2
- import type { LoopContext, RunSummary } from "../types.js";
3
- import type { BranchResult, IterateFn } from "./types.js";
4
- export declare function finalizeParallelWave(loop: LoopContext, iter: IterationContext, waveId: string, results: BranchResult[]): {
5
- reason: string;
2
+ import type { LoopContext, RunSummary, StopReason } from "../types.js";
3
+ import type { AggregateConfig, AggregateOutcome, BranchResult, IterateFn, WaveJoinReason } from "./types.js";
4
+ /**
5
+ * Finalize a joined wave under its configured aggregate strategy.
6
+ * - `wait_for_all` (default): unchanged semantics — any branch timeout or
7
+ * non-success stop reason fails the whole wave.
8
+ * - `first_success`: the wave completes as soon as ONE branch succeeds, even
9
+ * though its siblings were cancelled (not treated as failures).
10
+ * - `timeout`: if the wall-clock deadline was hit, the wave completes if at
11
+ * least one branch had already succeeded, else it is a wave timeout.
12
+ */
13
+ export declare function finalizeParallelWave(loop: LoopContext, iter: IterationContext, waveId: string, results: BranchResult[], aggregate: AggregateConfig, aggregateOutcome: AggregateOutcome): {
14
+ reason: WaveJoinReason;
6
15
  waveId: string;
7
16
  };
17
+ export declare function branchSuccessStatus(status: string): boolean;
8
18
  export declare function continueAfterParallelJoin(loop: LoopContext, iter: IterationContext, waveId: string, emittedTopic: string, totalElapsedMs: number, iterateFn: IterateFn): Promise<RunSummary>;
9
- export declare function stopAfterParallelWave(loop: LoopContext, iter: IterationContext, reason: string, waveId: string): RunSummary;
19
+ export declare function stopAfterParallelWave(loop: LoopContext, iter: IterationContext, reason: StopReason, waveId: string): RunSummary;
@@ -2,7 +2,30 @@ import { joinCsv, jsonField } from "@mobrienv/autoloop-core";
2
2
  import { appendEvent, appendHarnessEvent, } from "@mobrienv/autoloop-core/journal";
3
3
  import * as topology from "@mobrienv/autoloop-core/topology";
4
4
  import { parallelDispatchBase, parallelJoinedTopic } from "../emit.js";
5
- export function finalizeParallelWave(loop, iter, waveId, results) {
5
+ /**
6
+ * Finalize a joined wave under its configured aggregate strategy.
7
+ * - `wait_for_all` (default): unchanged semantics — any branch timeout or
8
+ * non-success stop reason fails the whole wave.
9
+ * - `first_success`: the wave completes as soon as ONE branch succeeds, even
10
+ * though its siblings were cancelled (not treated as failures).
11
+ * - `timeout`: if the wall-clock deadline was hit, the wave completes if at
12
+ * least one branch had already succeeded, else it is a wave timeout.
13
+ */
14
+ export function finalizeParallelWave(loop, iter, waveId, results, aggregate, aggregateOutcome) {
15
+ appendWaveAggregate(loop, iter, waveId, aggregate, aggregateOutcome);
16
+ if (aggregate.mode === "first_success") {
17
+ if (aggregateOutcome.satisfiedByBranchId) {
18
+ return { reason: "parallel_wave_complete", waveId };
19
+ }
20
+ return finalizeNoSuccess(loop, iter, waveId, results);
21
+ }
22
+ if (aggregate.mode === "timeout" && aggregateOutcome.aggregateTimedOut) {
23
+ const anySucceeded = results.some((r) => branchSuccessStatus(r.stopReason));
24
+ if (anySucceeded)
25
+ return { reason: "parallel_wave_complete", waveId };
26
+ return finalizeNoSuccess(loop, iter, waveId, results);
27
+ }
28
+ // wait_for_all (default), or timeout mode that resolved before its deadline.
6
29
  const timedOut = results
7
30
  .filter((r) => r.stopReason === "backend_timeout")
8
31
  .map((r) => r.branchId);
@@ -23,7 +46,38 @@ export function finalizeParallelWave(loop, iter, waveId, results) {
23
46
  }
24
47
  return { reason: "parallel_wave_complete", waveId };
25
48
  }
26
- function branchSuccessStatus(status) {
49
+ /** No branch succeeded (first_success/timeout modes without a winner):
50
+ * prefer a `wave.timeout` verdict when any branch actually timed out
51
+ * (backend- or aggregate-level), otherwise `wave.failed`. */
52
+ function finalizeNoSuccess(loop, iter, waveId, results) {
53
+ const timedOut = results
54
+ .filter((r) => r.stopReason === "backend_timeout" ||
55
+ r.stopReason === "wave_aggregate_timeout")
56
+ .map((r) => r.branchId);
57
+ if (timedOut.length > 0) {
58
+ appendEvent(loop.paths.journalFile, loop.runtime.runId, String(iter.iteration), "wave.timeout", jsonField("wave_id", waveId) +
59
+ ", " +
60
+ jsonField("timed_out_branches", joinCsv(timedOut)));
61
+ return { reason: "parallel_wave_timeout", waveId };
62
+ }
63
+ const failed = results
64
+ .filter((r) => !branchSuccessStatus(r.stopReason))
65
+ .map((r) => r.branchId);
66
+ appendEvent(loop.paths.journalFile, loop.runtime.runId, String(iter.iteration), "wave.failed", jsonField("wave_id", waveId) +
67
+ ", " +
68
+ jsonField("failed_branches", joinCsv(failed)));
69
+ return { reason: "parallel_wave_failed", waveId };
70
+ }
71
+ function appendWaveAggregate(loop, iter, waveId, aggregate, aggregateOutcome) {
72
+ appendEvent(loop.paths.journalFile, loop.runtime.runId, String(iter.iteration), "wave.aggregate", jsonField("wave_id", waveId) +
73
+ ", " +
74
+ jsonField("mode", aggregate.mode) +
75
+ ", " +
76
+ jsonField("satisfied_by_branch_id", aggregateOutcome.satisfiedByBranchId ?? "") +
77
+ ", " +
78
+ jsonField("aggregate_timed_out", String(aggregateOutcome.aggregateTimedOut)));
79
+ }
80
+ export function branchSuccessStatus(status) {
27
81
  return (status === "max_iterations" ||
28
82
  status === "completion_event" ||
29
83
  status === "completion_promise");
@@ -1 +1 @@
1
- {"version":3,"file":"finalize-wave.js","sourceRoot":"","sources":["../../src/wave/finalize-wave.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"finalize-wave.js","sourceRoot":"","sources":["../../src/wave/finalize-wave.ts"],"names":[],"mappings":"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"}
@@ -1,6 +1,27 @@
1
1
  import type { IterationContext } from "../prompt.js";
2
2
  import type { LoopContext } from "../types.js";
3
- import type { BranchResult, BranchSpec } from "./types.js";
3
+ import type { AggregateConfig, AggregateOutcome, BranchResult, BranchSpec } from "./types.js";
4
4
  export declare function prepareParallelBranches(loop: LoopContext, iter: IterationContext, waveId: string, waveDir: string, emittedTopic: string, objectives: string[]): BranchSpec[];
5
5
  export declare function launchParallelBranches(loop: LoopContext, specs: BranchSpec[]): BranchSpec[];
6
- export declare function joinParallelBranches(loop: LoopContext, iter: IterationContext, waveId: string, pending: BranchSpec[]): BranchResult[];
6
+ export interface JoinResult {
7
+ results: BranchResult[];
8
+ aggregateOutcome: AggregateOutcome;
9
+ }
10
+ /**
11
+ * Poll all branches to completion. The default `wait_for_all` aggregate mode
12
+ * blocks until every branch has a result (unchanged from prior behavior). Two
13
+ * additional modes let a wave resolve early:
14
+ * - `first_success`: return as soon as one branch succeeds, terminating the
15
+ * remaining pending branches (their results are synthesized as cancelled).
16
+ * - `timeout`: bound the whole wave's wall clock; branches still pending at
17
+ * the deadline are terminated and synthesized as timed out.
18
+ */
19
+ export declare function joinParallelBranches(loop: LoopContext, iter: IterationContext, waveId: string, pending: BranchSpec[], aggregate: AggregateConfig, waveStartMs: number): JoinResult;
20
+ /**
21
+ * Write the `launch.json` a `branch-run` subprocess reads to drive one
22
+ * isolated branch iteration. Exported so the fan-out stage runner
23
+ * (`wave/stage-branch-runner.ts`) can launch stage branches through the exact
24
+ * same supervisor/`branch-run` spawn machinery as parallel-wave branches,
25
+ * rather than a second bespoke spawner.
26
+ */
27
+ export declare function writeBranchLaunch(spec: BranchSpec, loop: LoopContext): void;