sequant 2.8.0 → 2.9.0
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/.claude-plugin/marketplace.json +1 -1
- package/.claude-plugin/plugin.json +1 -1
- package/README.md +9 -1
- package/dist/bin/cli.js +2 -1
- package/dist/marketplace/external_plugins/sequant/.claude-plugin/plugin.json +1 -1
- package/dist/marketplace/external_plugins/sequant/README.md +2 -0
- package/dist/marketplace/external_plugins/sequant/hooks/post-tool.sh +18 -3
- package/dist/marketplace/external_plugins/sequant/hooks/pre-tool.sh +330 -57
- package/dist/marketplace/external_plugins/sequant/skills/assess/SKILL.md +96 -15
- package/dist/marketplace/external_plugins/sequant/skills/assess/references/predicted-collision-detection.md +9 -6
- package/dist/marketplace/external_plugins/sequant/skills/fullsolve/SKILL.md +1 -1
- package/dist/marketplace/external_plugins/sequant/skills/reflect/SKILL.md +27 -13
- package/dist/marketplace/external_plugins/sequant/skills/reflect/references/documentation-tiers.md +80 -68
- package/dist/marketplace/external_plugins/sequant/skills/reflect/references/phase-reflection.md +31 -15
- package/dist/marketplace/external_plugins/sequant/skills/release/SKILL.md +10 -2
- package/dist/marketplace/external_plugins/sequant/skills/spec/references/verification-criteria.md +1 -1
- package/dist/src/commands/logs.js +6 -1
- package/dist/src/commands/run-display.d.ts +20 -0
- package/dist/src/commands/run-display.js +80 -1
- package/dist/src/commands/stats.js +47 -0
- package/dist/src/lib/assess-collision-detect.d.ts +19 -2
- package/dist/src/lib/assess-collision-detect.js +68 -4
- package/dist/src/lib/cli-ui/run-renderer.js +17 -9
- package/dist/src/lib/errors.d.ts +6 -0
- package/dist/src/lib/errors.js +9 -2
- package/dist/src/lib/manifest.js +1 -17
- package/dist/src/lib/version-check.js +1 -5
- package/dist/src/lib/workflow/batch-executor.d.ts +13 -0
- package/dist/src/lib/workflow/batch-executor.js +81 -18
- package/dist/src/lib/workflow/chain-preflight.d.ts +89 -0
- package/dist/src/lib/workflow/chain-preflight.js +199 -0
- package/dist/src/lib/workflow/chain-resume.d.ts +116 -0
- package/dist/src/lib/workflow/chain-resume.js +166 -0
- package/dist/src/lib/workflow/dependency-markers.d.ts +29 -0
- package/dist/src/lib/workflow/dependency-markers.js +79 -0
- package/dist/src/lib/workflow/drivers/claude-code.d.ts +7 -0
- package/dist/src/lib/workflow/drivers/claude-code.js +30 -6
- package/dist/src/lib/workflow/error-classifier.d.ts +9 -2
- package/dist/src/lib/workflow/error-classifier.js +14 -1
- package/dist/src/lib/workflow/log-writer.js +6 -8
- package/dist/src/lib/workflow/metrics-schema.d.ts +39 -0
- package/dist/src/lib/workflow/metrics-schema.js +16 -0
- package/dist/src/lib/workflow/metrics-writer.d.ts +2 -1
- package/dist/src/lib/workflow/phase-executor.d.ts +32 -0
- package/dist/src/lib/workflow/phase-executor.js +77 -5
- package/dist/src/lib/workflow/run-log-schema.d.ts +23 -0
- package/dist/src/lib/workflow/run-log-schema.js +45 -1
- package/dist/src/lib/workflow/run-orchestrator.d.ts +14 -0
- package/dist/src/lib/workflow/run-orchestrator.js +291 -30
- package/dist/src/lib/workflow/status-derivation.d.ts +30 -0
- package/dist/src/lib/workflow/status-derivation.js +27 -0
- package/dist/src/lib/workflow/types.d.ts +23 -0
- package/dist/src/lib/workflow/worktree-manager.d.ts +43 -1
- package/dist/src/lib/workflow/worktree-manager.js +103 -33
- package/dist/src/mcp/tools/run.d.ts +2 -0
- package/dist/src/mcp/tools/run.js +2 -0
- package/package.json +2 -4
- package/templates/hooks/post-tool.sh +18 -3
- package/templates/hooks/pre-tool.sh +330 -57
- package/templates/scripts/cleanup-worktree.sh +103 -14
- package/templates/skills/assess/SKILL.md +96 -15
- package/templates/skills/assess/references/predicted-collision-detection.md +9 -6
- package/templates/skills/fullsolve/SKILL.md +1 -1
- package/templates/skills/reflect/SKILL.md +27 -13
- package/templates/skills/reflect/references/documentation-tiers.md +80 -68
- package/templates/skills/reflect/references/phase-reflection.md +31 -15
- package/templates/skills/release/SKILL.md +10 -2
- package/templates/skills/spec/references/verification-criteria.md +1 -1
|
@@ -12,7 +12,7 @@ import { execSync, execFileSync } from "child_process";
|
|
|
12
12
|
import { readAgentsMd } from "../agents-md.js";
|
|
13
13
|
import { getDriver } from "./drivers/index.js";
|
|
14
14
|
import { classifyError } from "./error-classifier.js";
|
|
15
|
-
import { ApiError, BillingError } from "../errors.js";
|
|
15
|
+
import { ApiError, BillingError, RateLimitError, resetsAtToMs, } from "../errors.js";
|
|
16
16
|
import { phaseRegistry } from "./phase-registry.js";
|
|
17
17
|
import { bracketedConsoleLog } from "./notice.js";
|
|
18
18
|
/**
|
|
@@ -98,6 +98,44 @@ const SPEC_RETRY_STRATEGY = phaseRegistry.get("spec").retryStrategy;
|
|
|
98
98
|
export const SPEC_RETRY_BACKOFF_MS = SPEC_RETRY_STRATEGY?.backoffMs ?? 5000;
|
|
99
99
|
/** @internal Exported for testing only */
|
|
100
100
|
export const SPEC_EXTRA_RETRIES = SPEC_RETRY_STRATEGY?.extraRetries ?? 1;
|
|
101
|
+
/**
|
|
102
|
+
* A rate limit whose window resets further out than this is treated as
|
|
103
|
+
* exhausted rather than transient (#761 AC-2): no retry can succeed inside a
|
|
104
|
+
* closed window, so consuming cold-start retries (each burning up to a full
|
|
105
|
+
* `phaseTimeout`) only delays the labeled halt. Five minutes comfortably
|
|
106
|
+
* exceeds any backoff this executor performs while staying far below the
|
|
107
|
+
* five-hour/seven-day windows the check exists to catch.
|
|
108
|
+
*
|
|
109
|
+
* @internal Exported for testing only
|
|
110
|
+
*/
|
|
111
|
+
export const RATE_LIMIT_WINDOW_SKIP_THRESHOLD_MS = 5 * 60 * 1000;
|
|
112
|
+
/**
|
|
113
|
+
* Base backoff for transient rate-limit retries (#761 AC-4), doubled per
|
|
114
|
+
* attempt (5s, 10s). Same scale as `SPEC_RETRY_BACKOFF_MS` — long enough to
|
|
115
|
+
* outlive a momentary throttle, short enough to be negligible next to a
|
|
116
|
+
* phase's runtime.
|
|
117
|
+
*
|
|
118
|
+
* @internal Exported for testing only
|
|
119
|
+
*/
|
|
120
|
+
export const RATE_LIMIT_RETRY_BACKOFF_MS = 5000;
|
|
121
|
+
/**
|
|
122
|
+
* True when a failure is a rate limit whose reset lies beyond
|
|
123
|
+
* {@link RATE_LIMIT_WINDOW_SKIP_THRESHOLD_MS} — i.e. window exhaustion, not a
|
|
124
|
+
* transient throttle. Metadata-absent rate limits (the assistant-error channel
|
|
125
|
+
* carries no `resetsAt`, see #761 AC-9) return false and fall through to the
|
|
126
|
+
* transient path: with no timing signal, retry-with-backoff is the safe
|
|
127
|
+
* default, skipping all retries is not.
|
|
128
|
+
*
|
|
129
|
+
* @internal Exported for testing only
|
|
130
|
+
*/
|
|
131
|
+
export function isWindowExhaustedRateLimit(error, now = Date.now()) {
|
|
132
|
+
if (!(error instanceof RateLimitError))
|
|
133
|
+
return false;
|
|
134
|
+
const resetsAt = error.metadata.resetsAt;
|
|
135
|
+
if (typeof resetsAt !== "number")
|
|
136
|
+
return false;
|
|
137
|
+
return resetsAtToMs(resetsAt) - now > RATE_LIMIT_WINDOW_SKIP_THRESHOLD_MS;
|
|
138
|
+
}
|
|
101
139
|
export function parseQaVerdict(output) {
|
|
102
140
|
if (!output)
|
|
103
141
|
return null;
|
|
@@ -348,9 +386,12 @@ export function mapAgentSuccessToPhaseResult(phase, agentResult, durationSeconds
|
|
|
348
386
|
const summary = agentResult.output
|
|
349
387
|
? (parseQaSummary(agentResult.output) ?? undefined)
|
|
350
388
|
: undefined;
|
|
351
|
-
if (verdict
|
|
352
|
-
|
|
353
|
-
|
|
389
|
+
if (verdict === "AC_NOT_MET") {
|
|
390
|
+
// #749: only AC_NOT_MET (and the null branch below, #534) hard-fails.
|
|
391
|
+
// AC_MET_BUT_NOT_A_PLUS is a stopping/ready state — it must break to PR,
|
|
392
|
+
// not feed the quality loop (mirrors ready-gate.ts's `ac` policy). The
|
|
393
|
+
// verdict is retained on the success result so the PR/log surfaces the
|
|
394
|
+
// "not A+" note.
|
|
354
395
|
return {
|
|
355
396
|
phase,
|
|
356
397
|
success: false,
|
|
@@ -746,6 +787,32 @@ delayFn = (ms) => new Promise((resolve) => setTimeout(resolve, ms))) {
|
|
|
746
787
|
if (lastResult.capped) {
|
|
747
788
|
return lastResult;
|
|
748
789
|
}
|
|
790
|
+
// Window-exhausted rate limit (#761 AC-2): the reset is hours away, so
|
|
791
|
+
// every retry re-spawns into the same closed window — worst case
|
|
792
|
+
// ~4 × phaseTimeout (≈2h) of doomed attempts before the run halts.
|
|
793
|
+
// Modelled on the `capped` early return above: skip all remaining
|
|
794
|
+
// cold-start retries and (via the return) the MCP fallback. Checked
|
|
795
|
+
// before the duration branch because a rate-limit rejection typically
|
|
796
|
+
// fails fast and would otherwise be mistaken for a cold-start failure.
|
|
797
|
+
if (isWindowExhaustedRateLimit(lastResult.structuredError)) {
|
|
798
|
+
if (config.verbose) {
|
|
799
|
+
bracketedConsoleLog(spinner, chalk.yellow(`\n ✕ ${lastResult.error ?? "Rate limited"} — window exhausted, skipping retries`));
|
|
800
|
+
}
|
|
801
|
+
return lastResult;
|
|
802
|
+
}
|
|
803
|
+
// Transient rate limit (#761 AC-4): retry, but with real backoff — the
|
|
804
|
+
// bare `continue` this replaces re-spawned immediately into the same
|
|
805
|
+
// throttle. Reuses the injected `delayFn`; delay doubles per attempt.
|
|
806
|
+
// Metadata-absent rate limits land here by design (AC-9 fallback rule).
|
|
807
|
+
if (lastResult.structuredError instanceof RateLimitError &&
|
|
808
|
+
attempt < COLD_START_MAX_RETRIES) {
|
|
809
|
+
const backoffMs = RATE_LIMIT_RETRY_BACKOFF_MS * 2 ** attempt;
|
|
810
|
+
if (config.verbose) {
|
|
811
|
+
bracketedConsoleLog(spinner, chalk.yellow(`\n ⟳ ${lastResult.error ?? "Rate limited"} — backing off ${backoffMs}ms before retry... (attempt ${attempt + 2}/${COLD_START_MAX_RETRIES + 1})`));
|
|
812
|
+
}
|
|
813
|
+
await delayFn(backoffMs);
|
|
814
|
+
continue;
|
|
815
|
+
}
|
|
749
816
|
// Genuine failure (took long enough to be real work) → skip cold-start retries.
|
|
750
817
|
// Use error classification (AC-9): if the error is retryable (e.g., API
|
|
751
818
|
// rate limit, transient 503), allow one more attempt even for genuine failures.
|
|
@@ -793,11 +860,16 @@ delayFn = (ms) => new Promise((resolve) => setTimeout(resolve, ms))) {
|
|
|
793
860
|
// intent is documented and future code paths can't accidentally re-spawn a
|
|
794
861
|
// capped phase without MCP.
|
|
795
862
|
const failureIsCapped = lastResult.capped === true;
|
|
863
|
+
// A throttle must not trigger "retrying without MCP" (#761 AC-3): MCP was
|
|
864
|
+
// never the cause, and the re-spawn burns up to another full phaseTimeout
|
|
865
|
+
// against the same limit while mislabeling the failure as MCP-related.
|
|
866
|
+
const failureIsRateLimited = lastResult.structuredError instanceof RateLimitError;
|
|
796
867
|
if (config.mcp &&
|
|
797
868
|
!lastResult.success &&
|
|
798
869
|
!skipColdStartRetry &&
|
|
799
870
|
!failureIsBilling &&
|
|
800
|
-
!failureIsCapped
|
|
871
|
+
!failureIsCapped &&
|
|
872
|
+
!failureIsRateLimited) {
|
|
801
873
|
bracketedConsoleLog(spinner, chalk.yellow(`\n ! Phase failed with MCP enabled, retrying without MCP...`));
|
|
802
874
|
// Create config copy with MCP disabled
|
|
803
875
|
const configWithoutMcp = {
|
|
@@ -89,6 +89,8 @@ export declare const ErrorContextSchema: z.ZodObject<{
|
|
|
89
89
|
api_error: "api_error";
|
|
90
90
|
hook_failure: "hook_failure";
|
|
91
91
|
build_error: "build_error";
|
|
92
|
+
rate_limit: "rate_limit";
|
|
93
|
+
billing: "billing";
|
|
92
94
|
}>;
|
|
93
95
|
errorType: z.ZodOptional<z.ZodString>;
|
|
94
96
|
errorMetadata: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
|
|
@@ -170,6 +172,8 @@ export declare const PhaseLogSchema: z.ZodObject<{
|
|
|
170
172
|
api_error: "api_error";
|
|
171
173
|
hook_failure: "hook_failure";
|
|
172
174
|
build_error: "build_error";
|
|
175
|
+
rate_limit: "rate_limit";
|
|
176
|
+
billing: "billing";
|
|
173
177
|
}>;
|
|
174
178
|
errorType: z.ZodOptional<z.ZodString>;
|
|
175
179
|
errorMetadata: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
|
|
@@ -247,6 +251,8 @@ export declare const IssueLogSchema: z.ZodObject<{
|
|
|
247
251
|
api_error: "api_error";
|
|
248
252
|
hook_failure: "hook_failure";
|
|
249
253
|
build_error: "build_error";
|
|
254
|
+
rate_limit: "rate_limit";
|
|
255
|
+
billing: "billing";
|
|
250
256
|
}>;
|
|
251
257
|
errorType: z.ZodOptional<z.ZodString>;
|
|
252
258
|
errorMetadata: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
|
|
@@ -277,6 +283,7 @@ export declare const RunSummarySchema: z.ZodObject<{
|
|
|
277
283
|
totalIssues: z.ZodNumber;
|
|
278
284
|
passed: z.ZodNumber;
|
|
279
285
|
failed: z.ZodNumber;
|
|
286
|
+
partial: z.ZodDefault<z.ZodNumber>;
|
|
280
287
|
totalDurationSeconds: z.ZodNumber;
|
|
281
288
|
}, z.core.$strip>;
|
|
282
289
|
export type RunSummary = z.infer<typeof RunSummarySchema>;
|
|
@@ -365,6 +372,8 @@ export declare const RunLogSchema: z.ZodObject<{
|
|
|
365
372
|
api_error: "api_error";
|
|
366
373
|
hook_failure: "hook_failure";
|
|
367
374
|
build_error: "build_error";
|
|
375
|
+
rate_limit: "rate_limit";
|
|
376
|
+
billing: "billing";
|
|
368
377
|
}>;
|
|
369
378
|
errorType: z.ZodOptional<z.ZodString>;
|
|
370
379
|
errorMetadata: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
|
|
@@ -379,6 +388,7 @@ export declare const RunLogSchema: z.ZodObject<{
|
|
|
379
388
|
totalIssues: z.ZodNumber;
|
|
380
389
|
passed: z.ZodNumber;
|
|
381
390
|
failed: z.ZodNumber;
|
|
391
|
+
partial: z.ZodDefault<z.ZodNumber>;
|
|
382
392
|
totalDurationSeconds: z.ZodNumber;
|
|
383
393
|
}, z.core.$strip>;
|
|
384
394
|
startCommit: z.ZodOptional<z.ZodString>;
|
|
@@ -439,3 +449,16 @@ export declare function completePhaseLog(phaseLog: Omit<PhaseLog, "endTime" | "d
|
|
|
439
449
|
export declare function finalizeRunLog(runLog: Omit<RunLog, "endTime">, options?: {
|
|
440
450
|
endCommit?: string;
|
|
441
451
|
}): RunLog;
|
|
452
|
+
/**
|
|
453
|
+
* Derive an issue's overall log status from its phase log entries (#766).
|
|
454
|
+
*
|
|
455
|
+
* Phases are appended in execution order, so the last entry for a given phase
|
|
456
|
+
* name is its latest attempt — that attempt wins. This lets a `timeout`/
|
|
457
|
+
* `failure` that a later quality-loop iteration recovers from de-escalate to
|
|
458
|
+
* `success`, keeping the JSON log consistent with the live card and summary
|
|
459
|
+
* table (AC-3/AC-5). `loop` is auxiliary recovery and never determines the
|
|
460
|
+
* verdict (mirrors the live-card rule); an unrecovered failure still leaves a
|
|
461
|
+
* non-loop phase failed. Priority among latest attempts: failure > timeout >
|
|
462
|
+
* success.
|
|
463
|
+
*/
|
|
464
|
+
export declare function deriveIssueLogStatus(phases: PhaseLog[]): IssueStatus;
|
|
@@ -79,13 +79,19 @@ export const ErrorContextSchema = z.object({
|
|
|
79
79
|
stdoutTail: z.array(z.string()),
|
|
80
80
|
/** Process exit code */
|
|
81
81
|
exitCode: z.number().int().optional(),
|
|
82
|
-
/**
|
|
82
|
+
/**
|
|
83
|
+
* Classified error category (legacy, kept for backwards compatibility).
|
|
84
|
+
* Keep in sync with `ERROR_CATEGORIES` in `error-classifier.ts` —
|
|
85
|
+
* `rate_limit` / `billing` added by #761 AC-6.
|
|
86
|
+
*/
|
|
83
87
|
category: z.enum([
|
|
84
88
|
"context_overflow",
|
|
85
89
|
"api_error",
|
|
86
90
|
"hook_failure",
|
|
87
91
|
"build_error",
|
|
88
92
|
"timeout",
|
|
93
|
+
"rate_limit",
|
|
94
|
+
"billing",
|
|
89
95
|
"unknown",
|
|
90
96
|
]),
|
|
91
97
|
/** Typed error class name (AC-8), e.g. "ApiError", "BuildError" */
|
|
@@ -206,6 +212,13 @@ export const RunSummarySchema = z.object({
|
|
|
206
212
|
passed: z.number().int().nonnegative(),
|
|
207
213
|
/** Number of issues that failed */
|
|
208
214
|
failed: z.number().int().nonnegative(),
|
|
215
|
+
/**
|
|
216
|
+
* Number of issues that ended `partial` — timed out with no genuine failure
|
|
217
|
+
* and no recovery (#766). Given its own bucket so an all-partial run no longer
|
|
218
|
+
* vanishes from both `passed` and `failed` (the `0 passed · 0 failed` bug).
|
|
219
|
+
* `.default(0)` keeps logs written before this field parseable.
|
|
220
|
+
*/
|
|
221
|
+
partial: z.number().int().nonnegative().default(0),
|
|
209
222
|
/** Total execution time in seconds */
|
|
210
223
|
totalDurationSeconds: z.number().nonnegative(),
|
|
211
224
|
});
|
|
@@ -274,6 +287,7 @@ export function createEmptyRunLog(config, options) {
|
|
|
274
287
|
totalIssues: 0,
|
|
275
288
|
passed: 0,
|
|
276
289
|
failed: 0,
|
|
290
|
+
partial: 0,
|
|
277
291
|
totalDurationSeconds: 0,
|
|
278
292
|
},
|
|
279
293
|
startCommit: options?.startCommit,
|
|
@@ -326,6 +340,9 @@ export function finalizeRunLog(runLog, options) {
|
|
|
326
340
|
const totalDurationSeconds = (endTime.getTime() - startTime.getTime()) / 1000;
|
|
327
341
|
const passed = runLog.issues.filter((i) => i.status === "success").length;
|
|
328
342
|
const failed = runLog.issues.filter((i) => i.status === "failure").length;
|
|
343
|
+
// #766: `partial` gets its own bucket so an all-partial run isn't counted as
|
|
344
|
+
// `0 passed · 0 failed` — it landed in neither before.
|
|
345
|
+
const partial = runLog.issues.filter((i) => i.status === "partial").length;
|
|
329
346
|
return {
|
|
330
347
|
...runLog,
|
|
331
348
|
endTime: endTime.toISOString(),
|
|
@@ -333,8 +350,35 @@ export function finalizeRunLog(runLog, options) {
|
|
|
333
350
|
totalIssues: runLog.issues.length,
|
|
334
351
|
passed,
|
|
335
352
|
failed,
|
|
353
|
+
partial,
|
|
336
354
|
totalDurationSeconds,
|
|
337
355
|
},
|
|
338
356
|
endCommit: options?.endCommit ?? runLog.endCommit,
|
|
339
357
|
};
|
|
340
358
|
}
|
|
359
|
+
/**
|
|
360
|
+
* Derive an issue's overall log status from its phase log entries (#766).
|
|
361
|
+
*
|
|
362
|
+
* Phases are appended in execution order, so the last entry for a given phase
|
|
363
|
+
* name is its latest attempt — that attempt wins. This lets a `timeout`/
|
|
364
|
+
* `failure` that a later quality-loop iteration recovers from de-escalate to
|
|
365
|
+
* `success`, keeping the JSON log consistent with the live card and summary
|
|
366
|
+
* table (AC-3/AC-5). `loop` is auxiliary recovery and never determines the
|
|
367
|
+
* verdict (mirrors the live-card rule); an unrecovered failure still leaves a
|
|
368
|
+
* non-loop phase failed. Priority among latest attempts: failure > timeout >
|
|
369
|
+
* success.
|
|
370
|
+
*/
|
|
371
|
+
export function deriveIssueLogStatus(phases) {
|
|
372
|
+
const latest = new Map();
|
|
373
|
+
for (const p of phases) {
|
|
374
|
+
if (p.phase === "loop")
|
|
375
|
+
continue;
|
|
376
|
+
latest.set(p.phase, p.status);
|
|
377
|
+
}
|
|
378
|
+
const statuses = [...latest.values()];
|
|
379
|
+
if (statuses.some((s) => s === "failure"))
|
|
380
|
+
return "failure";
|
|
381
|
+
if (statuses.some((s) => s === "timeout"))
|
|
382
|
+
return "partial";
|
|
383
|
+
return "success";
|
|
384
|
+
}
|
|
@@ -13,6 +13,7 @@ import { LogWriter } from "./log-writer.js";
|
|
|
13
13
|
import { StateManager } from "./state-manager.js";
|
|
14
14
|
import { ShutdownManager } from "../shutdown.js";
|
|
15
15
|
import type { LockFile } from "../locks/index.js";
|
|
16
|
+
import { type ChainResumePlan } from "./chain-resume.js";
|
|
16
17
|
import { WorkflowEventEmitter } from "./event-emitter.js";
|
|
17
18
|
import type { SequantSettings } from "../settings.js";
|
|
18
19
|
/**
|
|
@@ -55,6 +56,12 @@ export interface OrchestratorConfig {
|
|
|
55
56
|
packageManager?: string;
|
|
56
57
|
/** Base branch for rebase/PR targets */
|
|
57
58
|
baseBranch?: string;
|
|
59
|
+
/**
|
|
60
|
+
* Chain resume plan (#760). Present only when re-running a `--chain` batch
|
|
61
|
+
* whose completed prefix is being skipped. Drives the first active link's
|
|
62
|
+
* rebase onto the last completed link's committed tip in `executeSequential`.
|
|
63
|
+
*/
|
|
64
|
+
chainResume?: ChainResumePlan;
|
|
58
65
|
/** Per-phase progress callback (parallel mode) */
|
|
59
66
|
onProgress?: ProgressCallback;
|
|
60
67
|
/** #672 AC-2: phase-plan callback forwarded into per-issue contexts. */
|
|
@@ -112,6 +119,13 @@ export interface ResolvedRun {
|
|
|
112
119
|
config: ExecutionConfig;
|
|
113
120
|
/** Parsed + dep-sorted issue numbers (pre-state-guard) */
|
|
114
121
|
issueNumbers: number[];
|
|
122
|
+
/**
|
|
123
|
+
* Raw CLI issue order BEFORE `sortByDependencies` reorders it (#762).
|
|
124
|
+
* The chain content pre-flight must compare against this order — comparing
|
|
125
|
+
* against the dep-sorted `issueNumbers` would make the dependency-order and
|
|
126
|
+
* file-overlap-order checks dead code, since the sorter already fixed them.
|
|
127
|
+
*/
|
|
128
|
+
rawIssueOrder: number[];
|
|
115
129
|
/** Resolved batches if --batch specified, else null */
|
|
116
130
|
batches: number[][] | null;
|
|
117
131
|
/** Resolved base branch (CLI → settings → auto-detect → "main") */
|