sequant 2.9.0 → 2.11.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 +20 -5
- package/dist/bin/cli.js +143 -18
- package/dist/marketplace/external_plugins/sequant/.claude-plugin/plugin.json +1 -1
- package/dist/marketplace/external_plugins/sequant/.mcp.json +1 -1
- package/dist/marketplace/external_plugins/sequant/README.md +56 -2
- package/dist/marketplace/external_plugins/sequant/hooks/parallel-marker.sh +50 -0
- package/dist/marketplace/external_plugins/sequant/hooks/post-tool.sh +20 -7
- package/dist/marketplace/external_plugins/sequant/hooks/pre-tool.sh +75 -10
- package/dist/marketplace/external_plugins/sequant/skills/_shared/references/trust-model.md +18 -0
- package/dist/marketplace/external_plugins/sequant/skills/assess/SKILL.md +215 -390
- package/dist/marketplace/external_plugins/sequant/skills/assess/references/predicted-collision-detection.md +1 -1
- package/dist/marketplace/external_plugins/sequant/skills/docs/SKILL.md +4 -4
- package/dist/marketplace/external_plugins/sequant/skills/exec/SKILL.md +26 -6
- package/dist/marketplace/external_plugins/sequant/skills/fullsolve/SKILL.md +3 -1
- package/dist/marketplace/external_plugins/sequant/skills/loop/SKILL.md +38 -2
- package/dist/marketplace/external_plugins/sequant/skills/merger/SKILL.md +2 -2
- package/dist/marketplace/external_plugins/sequant/skills/qa/SKILL.md +337 -57
- package/dist/marketplace/external_plugins/sequant/skills/qa/references/anti-pattern-detection.md +6 -6
- package/dist/marketplace/external_plugins/sequant/skills/qa/references/call-site-review.md +2 -2
- package/dist/marketplace/external_plugins/sequant/skills/qa/references/code-review-checklist.md +2 -2
- package/dist/marketplace/external_plugins/sequant/skills/qa/references/fixtures/injection-issue-body.md +36 -0
- package/dist/marketplace/external_plugins/sequant/skills/qa/references/quality-gates.md +21 -7
- package/dist/marketplace/external_plugins/sequant/skills/qa/references/testing-requirements.md +1 -1
- package/dist/marketplace/external_plugins/sequant/skills/qa/scripts/quality-checks.sh +11 -11
- package/dist/marketplace/external_plugins/sequant/skills/release/SKILL.md +1 -1
- package/dist/marketplace/external_plugins/sequant/skills/setup/SKILL.md +5 -5
- package/dist/marketplace/external_plugins/sequant/skills/spec/SKILL.md +2 -0
- package/dist/marketplace/external_plugins/sequant/skills/test/SKILL.md +1 -1
- package/dist/src/commands/assess-render.d.ts +23 -0
- package/dist/src/commands/assess-render.js +60 -0
- package/dist/src/commands/doctor.js +8 -13
- package/dist/src/commands/init.js +13 -1
- package/dist/src/commands/locks.d.ts +20 -1
- package/dist/src/commands/locks.js +208 -4
- package/dist/src/commands/logs.js +14 -5
- package/dist/src/commands/merge.js +154 -2
- package/dist/src/commands/ready-tui-adapter.js +6 -1
- package/dist/src/commands/ready.d.ts +31 -3
- package/dist/src/commands/ready.js +53 -13
- package/dist/src/commands/resume.d.ts +113 -0
- package/dist/src/commands/resume.js +193 -0
- package/dist/src/commands/run-display.js +38 -4
- package/dist/src/commands/run-flags.d.ts +42 -1
- package/dist/src/commands/run-flags.js +53 -1
- package/dist/src/commands/run-progress.js +26 -1
- package/dist/src/commands/run.js +11 -10
- package/dist/src/commands/state.js +7 -0
- package/dist/src/commands/status.d.ts +9 -0
- package/dist/src/commands/status.js +24 -1
- package/dist/src/commands/sync.js +90 -19
- package/dist/src/commands/update.js +28 -5
- package/dist/src/commands/worktree.d.ts +31 -0
- package/dist/src/commands/worktree.js +95 -0
- package/dist/src/lib/ac-parser.d.ts +14 -0
- package/dist/src/lib/ac-parser.js +99 -5
- package/dist/src/lib/assess/index.d.ts +10 -0
- package/dist/src/lib/assess/index.js +9 -0
- package/dist/src/lib/assess/renderer.d.ts +64 -0
- package/dist/src/lib/assess/renderer.js +481 -0
- package/dist/src/lib/assess/types.d.ts +224 -0
- package/dist/src/lib/assess/types.js +241 -0
- package/dist/src/lib/ci/outputs.d.ts +15 -2
- package/dist/src/lib/ci/outputs.js +17 -5
- package/dist/src/lib/cli-flags.d.ts +56 -0
- package/dist/src/lib/cli-flags.js +89 -0
- package/dist/src/lib/cli-ui/run-renderer-types.d.ts +23 -7
- package/dist/src/lib/cli-ui/run-renderer.d.ts +10 -0
- package/dist/src/lib/cli-ui/run-renderer.js +69 -1
- package/dist/src/lib/errors.d.ts +78 -6
- package/dist/src/lib/errors.js +133 -15
- package/dist/src/lib/locks/checkout-lock.d.ts +193 -0
- package/dist/src/lib/locks/checkout-lock.js +389 -0
- package/dist/src/lib/locks/index.d.ts +7 -4
- package/dist/src/lib/locks/index.js +5 -3
- package/dist/src/lib/locks/lock-manager.d.ts +101 -3
- package/dist/src/lib/locks/lock-manager.js +277 -8
- package/dist/src/lib/locks/types.d.ts +106 -3
- package/dist/src/lib/locks/types.js +44 -0
- package/dist/src/lib/mcp-config.d.ts +45 -0
- package/dist/src/lib/mcp-config.js +77 -1
- package/dist/src/lib/merge-check/combined-branch-test.d.ts +21 -2
- package/dist/src/lib/merge-check/combined-branch-test.js +233 -110
- package/dist/src/lib/merge-check/command-result.d.ts +36 -0
- package/dist/src/lib/merge-check/command-result.js +56 -0
- package/dist/src/lib/merge-check/index.d.ts +4 -0
- package/dist/src/lib/merge-check/index.js +1 -1
- package/dist/src/lib/merge-check/report.js +5 -1
- package/dist/src/lib/merge-check/types.d.ts +9 -0
- package/dist/src/lib/merge-check/watch.d.ts +121 -0
- package/dist/src/lib/merge-check/watch.js +215 -0
- package/dist/src/lib/qa/infra-blocked-ci.d.ts +90 -0
- package/dist/src/lib/qa/infra-blocked-ci.js +80 -0
- package/dist/src/lib/settings.d.ts +87 -0
- package/dist/src/lib/settings.js +106 -8
- package/dist/src/lib/shutdown.d.ts +42 -2
- package/dist/src/lib/shutdown.js +75 -5
- package/dist/src/lib/skills-check.d.ts +26 -0
- package/dist/src/lib/skills-check.js +31 -0
- package/dist/src/lib/stacks.d.ts +110 -1
- package/dist/src/lib/stacks.js +244 -5
- package/dist/src/lib/templates.d.ts +74 -0
- package/dist/src/lib/templates.js +171 -16
- package/dist/src/lib/test-tautology-detector.d.ts +10 -2
- package/dist/src/lib/test-tautology-detector.js +213 -12
- package/dist/src/lib/version-check.d.ts +31 -0
- package/dist/src/lib/version-check.js +45 -2
- package/dist/src/lib/workflow/batch-executor.d.ts +125 -6
- package/dist/src/lib/workflow/batch-executor.js +552 -60
- package/dist/src/lib/workflow/chain-resume.d.ts +13 -11
- package/dist/src/lib/workflow/chain-resume.js +24 -20
- package/dist/src/lib/workflow/completed-status.d.ts +52 -0
- package/dist/src/lib/workflow/completed-status.js +57 -0
- package/dist/src/lib/workflow/config-resolver.d.ts +51 -0
- package/dist/src/lib/workflow/config-resolver.js +143 -2
- package/dist/src/lib/workflow/drivers/agent-driver.d.ts +22 -0
- package/dist/src/lib/workflow/drivers/aider.d.ts +2 -0
- package/dist/src/lib/workflow/drivers/aider.js +2 -0
- package/dist/src/lib/workflow/drivers/claude-code.d.ts +14 -0
- package/dist/src/lib/workflow/drivers/claude-code.js +25 -2
- package/dist/src/lib/workflow/effort-escalation.d.ts +73 -0
- package/dist/src/lib/workflow/effort-escalation.js +82 -0
- package/dist/src/lib/workflow/error-classifier.d.ts +4 -1
- package/dist/src/lib/workflow/error-classifier.js +4 -0
- package/dist/src/lib/workflow/git-diff-utils.d.ts +35 -3
- package/dist/src/lib/workflow/git-diff-utils.js +77 -4
- package/dist/src/lib/workflow/heartbeat.d.ts +38 -0
- package/dist/src/lib/workflow/heartbeat.js +90 -0
- package/dist/src/lib/workflow/log-writer.d.ts +54 -2
- package/dist/src/lib/workflow/log-writer.js +95 -6
- package/dist/src/lib/workflow/metrics-schema.d.ts +52 -9
- package/dist/src/lib/workflow/metrics-schema.js +33 -0
- package/dist/src/lib/workflow/metrics-writer.d.ts +11 -0
- package/dist/src/lib/workflow/phase-detection.d.ts +12 -0
- package/dist/src/lib/workflow/phase-detection.js +5 -1
- package/dist/src/lib/workflow/phase-executor.d.ts +203 -14
- package/dist/src/lib/workflow/phase-executor.js +467 -52
- package/dist/src/lib/workflow/phase-registry.js +2 -1
- package/dist/src/lib/workflow/platforms/github.d.ts +64 -0
- package/dist/src/lib/workflow/platforms/github.js +152 -0
- package/dist/src/lib/workflow/qa-cache.d.ts +6 -1
- package/dist/src/lib/workflow/qa-cache.js +22 -9
- package/dist/src/lib/workflow/ready-gate.d.ts +45 -4
- package/dist/src/lib/workflow/ready-gate.js +49 -12
- package/dist/src/lib/workflow/run-log-schema.d.ts +69 -0
- package/dist/src/lib/workflow/run-log-schema.js +78 -2
- package/dist/src/lib/workflow/run-orchestrator.d.ts +19 -0
- package/dist/src/lib/workflow/run-orchestrator.js +168 -20
- package/dist/src/lib/workflow/skills-preflight.d.ts +89 -0
- package/dist/src/lib/workflow/skills-preflight.js +112 -0
- package/dist/src/lib/workflow/spec-recommendation.d.ts +71 -0
- package/dist/src/lib/workflow/spec-recommendation.js +142 -0
- package/dist/src/lib/workflow/state-cleanup.js +9 -2
- package/dist/src/lib/workflow/state-manager.d.ts +33 -0
- package/dist/src/lib/workflow/state-manager.js +83 -0
- package/dist/src/lib/workflow/state-schema.d.ts +26 -0
- package/dist/src/lib/workflow/state-schema.js +47 -0
- package/dist/src/lib/workflow/types.d.ts +190 -4
- package/dist/src/lib/workflow/types.js +1 -0
- package/dist/src/lib/workflow/worktree-manager.d.ts +36 -3
- package/dist/src/lib/workflow/worktree-manager.js +111 -24
- package/dist/src/lib/workflow/worktree-resolver.d.ts +73 -0
- package/dist/src/lib/workflow/worktree-resolver.js +126 -0
- package/dist/src/mcp/tools/run.d.ts +9 -1
- package/dist/src/mcp/tools/run.js +20 -1
- package/dist/src/mcp/tools/status.js +6 -0
- package/dist/src/ui/tui/ElapsedTimer.d.ts +12 -3
- package/dist/src/ui/tui/ElapsedTimer.js +12 -10
- package/dist/src/ui/tui/IssueBox.js +10 -2
- package/dist/src/ui/tui/row-cap.js +2 -1
- package/dist/src/ui/tui/theme.d.ts +7 -0
- package/dist/src/ui/tui/theme.js +9 -0
- package/package.json +10 -8
- package/templates/hooks/parallel-marker.sh +50 -0
- package/templates/hooks/post-tool.sh +20 -7
- package/templates/hooks/pre-tool.sh +303 -10
- package/templates/scripts/cleanup-worktree.sh +217 -33
- package/templates/scripts/new-feature.sh +289 -35
- package/templates/skills/_shared/references/subagent-types.md +7 -18
- package/templates/skills/_shared/references/trust-model.md +18 -0
- package/templates/skills/assess/SKILL.md +220 -391
- package/templates/skills/assess/references/predicted-collision-detection.md +1 -1
- package/templates/skills/docs/SKILL.md +4 -4
- package/templates/skills/exec/SKILL.md +87 -13
- package/templates/skills/fullsolve/SKILL.md +130 -22
- package/templates/skills/loop/SKILL.md +94 -13
- package/templates/skills/merger/SKILL.md +100 -12
- package/templates/skills/qa/SKILL.md +396 -63
- package/templates/skills/qa/references/anti-pattern-detection.md +6 -6
- package/templates/skills/qa/references/call-site-review.md +2 -2
- package/templates/skills/qa/references/code-review-checklist.md +2 -2
- package/templates/skills/qa/references/fixtures/injection-issue-body.md +36 -0
- package/templates/skills/qa/references/quality-gates.md +21 -7
- package/templates/skills/qa/references/testing-requirements.md +1 -1
- package/templates/skills/qa/scripts/quality-checks.sh +11 -11
- package/templates/skills/release/SKILL.md +80 -1
- package/templates/skills/setup/SKILL.md +5 -5
- package/templates/skills/spec/SKILL.md +33 -15
- package/templates/skills/spec/references/recommended-workflow.md +14 -1
- package/templates/skills/test/SKILL.md +1 -1
- package/templates/skills/testgen/SKILL.md +23 -6
- package/dist/src/lib/phase-spinner.d.ts +0 -146
- package/dist/src/lib/phase-spinner.js +0 -255
- package/dist/src/lib/workflow/pr-operations.d.ts +0 -86
- package/dist/src/lib/workflow/pr-operations.js +0 -326
- package/dist/src/lib/workflow/run-summary.d.ts +0 -36
- package/dist/src/lib/workflow/run-summary.js +0 -142
- package/templates/agents/sequant-explorer.md +0 -24
|
@@ -27,17 +27,27 @@ export class LogWriter {
|
|
|
27
27
|
activeIssues = new Map();
|
|
28
28
|
/** @deprecated Single-issue slot for backwards compatibility — use activeIssues */
|
|
29
29
|
currentIssue = null;
|
|
30
|
+
/**
|
|
31
|
+
* Issue slots forced to `failure` by {@link markIssueFailed} (#879). Keyed on
|
|
32
|
+
* the slot object so it covers both the numbered and legacy `currentIssue`
|
|
33
|
+
* paths. `completeIssue` re-derives status from phases (#856), so a seeded
|
|
34
|
+
* `issue.status = "failure"` alone would be overwritten back to `success`;
|
|
35
|
+
* this marker survives that re-derivation.
|
|
36
|
+
*/
|
|
37
|
+
forcedFailures = new WeakSet();
|
|
30
38
|
logPath;
|
|
31
39
|
writeToUserLogs;
|
|
32
40
|
verbose;
|
|
33
41
|
rotation;
|
|
34
42
|
startCommit;
|
|
43
|
+
startTime;
|
|
35
44
|
constructor(options = {}) {
|
|
36
45
|
this.logPath = options.logPath ?? LOG_PATHS.project;
|
|
37
46
|
this.writeToUserLogs = options.writeToUserLogs ?? false;
|
|
38
47
|
this.verbose = options.verbose ?? false;
|
|
39
48
|
this.rotation = options.rotation ?? DEFAULT_ROTATION_SETTINGS;
|
|
40
49
|
this.startCommit = options.startCommit;
|
|
50
|
+
this.startTime = options.startTime;
|
|
41
51
|
}
|
|
42
52
|
/**
|
|
43
53
|
* Initialize a new run log
|
|
@@ -45,7 +55,10 @@ export class LogWriter {
|
|
|
45
55
|
* @param config - Run configuration
|
|
46
56
|
*/
|
|
47
57
|
async initialize(config) {
|
|
48
|
-
this.runLog = createEmptyRunLog(config, {
|
|
58
|
+
this.runLog = createEmptyRunLog(config, {
|
|
59
|
+
startCommit: this.startCommit,
|
|
60
|
+
startTime: this.startTime,
|
|
61
|
+
});
|
|
49
62
|
// Ensure log directory exists
|
|
50
63
|
await this.ensureLogDirectory(this.logPath);
|
|
51
64
|
if (this.writeToUserLogs) {
|
|
@@ -67,12 +80,17 @@ export class LogWriter {
|
|
|
67
80
|
if (!this.runLog) {
|
|
68
81
|
throw new Error("LogWriter not initialized. Call initialize() first.");
|
|
69
82
|
}
|
|
83
|
+
// #856: seed pessimistically. This slot is only revised by `logPhase`, so
|
|
84
|
+
// an optimistic `"success"` seed became the persisted verdict for any
|
|
85
|
+
// issue whose first phase never completed — the exact shape of a run
|
|
86
|
+
// killed mid-flight. `completeIssue` re-derives from `phases` regardless,
|
|
87
|
+
// but the seed should not itself assert a pass that never happened.
|
|
70
88
|
const issueData = {
|
|
71
89
|
issueNumber,
|
|
72
90
|
title,
|
|
73
91
|
labels,
|
|
74
92
|
phases: [],
|
|
75
|
-
status: "
|
|
93
|
+
status: "failure",
|
|
76
94
|
totalDurationSeconds: 0,
|
|
77
95
|
};
|
|
78
96
|
this.activeIssues.set(issueNumber, issueData);
|
|
@@ -116,10 +134,53 @@ export class LogWriter {
|
|
|
116
134
|
issue.prNumber = prNumber;
|
|
117
135
|
issue.prUrl = prUrl;
|
|
118
136
|
}
|
|
137
|
+
/**
|
|
138
|
+
* Record how the spec→run phase recommendation was resolved (#921 AC-4).
|
|
139
|
+
*
|
|
140
|
+
* Called right after `resolveSpecRecommendation` runs, before the next
|
|
141
|
+
* phase starts — mirrors {@link setPRInfo}'s post-hoc-setter shape because
|
|
142
|
+
* the spec `PhaseLog` is already written by the time resolution (which does
|
|
143
|
+
* its own comment-fetch I/O) completes.
|
|
144
|
+
*/
|
|
145
|
+
setSpecRecommendation(recommendation, issueNumber) {
|
|
146
|
+
const issue = issueNumber
|
|
147
|
+
? (this.activeIssues.get(issueNumber) ?? this.currentIssue)
|
|
148
|
+
: this.currentIssue;
|
|
149
|
+
if (!issue) {
|
|
150
|
+
return;
|
|
151
|
+
}
|
|
152
|
+
issue.specRecommendation = recommendation;
|
|
153
|
+
}
|
|
154
|
+
/**
|
|
155
|
+
* Force the in-flight issue's status to `failure` (#879).
|
|
156
|
+
*
|
|
157
|
+
* `deriveIssueLogStatus` runs at phase-log time, so an issue whose phases all
|
|
158
|
+
* passed but whose PR creation then failed would otherwise be recorded as
|
|
159
|
+
* `success`. Call this after the last phase is logged and before
|
|
160
|
+
* {@link completeIssue} to count it under `failed`. No-op if the issue is not
|
|
161
|
+
* active. Reuses the existing `failure` enum — no schema change.
|
|
162
|
+
*/
|
|
163
|
+
markIssueFailed(issueNumber) {
|
|
164
|
+
const issue = issueNumber
|
|
165
|
+
? (this.activeIssues.get(issueNumber) ?? this.currentIssue)
|
|
166
|
+
: this.currentIssue;
|
|
167
|
+
if (!issue) {
|
|
168
|
+
return;
|
|
169
|
+
}
|
|
170
|
+
issue.status = "failure";
|
|
171
|
+
// completeIssue re-derives status from the phase list (#856), so the seed
|
|
172
|
+
// above is not enough on its own — mark the slot so completion honors it.
|
|
173
|
+
this.forcedFailures.add(issue);
|
|
174
|
+
}
|
|
119
175
|
/**
|
|
120
176
|
* Complete the current issue and add it to the run log
|
|
177
|
+
*
|
|
178
|
+
* @param issueNumber - Issue to complete (defaults to the legacy single slot)
|
|
179
|
+
* @param abort - Set when the run is being torn down by an external signal
|
|
180
|
+
* (#856). Marks the issue aborted with its cause instead of persisting
|
|
181
|
+
* whatever verdict the incomplete phase list happens to imply.
|
|
121
182
|
*/
|
|
122
|
-
completeIssue(issueNumber) {
|
|
183
|
+
completeIssue(issueNumber, abort) {
|
|
123
184
|
if (!this.runLog) {
|
|
124
185
|
throw new Error("No run log. Call initialize() first.");
|
|
125
186
|
}
|
|
@@ -134,19 +195,40 @@ export class LogWriter {
|
|
|
134
195
|
}
|
|
135
196
|
// Calculate total duration from phases
|
|
136
197
|
const totalDurationSeconds = issue.phases?.reduce((sum, p) => sum + p.durationSeconds, 0) ?? 0;
|
|
198
|
+
// #856: derive from the phase list rather than trusting the slot's seed.
|
|
199
|
+
// An issue with no completed phase is a failure — the run was cut short.
|
|
200
|
+
//
|
|
201
|
+
// An abort forces `failure` even when every phase logged so far passed:
|
|
202
|
+
// the issue was still in flight when the signal arrived, so its pipeline
|
|
203
|
+
// never reached a terminal state and a partial prefix of green phases is
|
|
204
|
+
// not a pass. `abortReason` records why, so the log names its own cause
|
|
205
|
+
// instead of leaving a silently truncated record.
|
|
206
|
+
// #879: markIssueFailed forces `failure` when every phase passed but a
|
|
207
|
+
// later step (e.g. PR creation) failed — same override intent as `abort`,
|
|
208
|
+
// so it must win over the phase-derived status too.
|
|
209
|
+
const status = abort || this.forcedFailures.has(issue)
|
|
210
|
+
? "failure"
|
|
211
|
+
: deriveIssueLogStatus(issue.phases ?? []);
|
|
137
212
|
const issueLog = {
|
|
138
213
|
issueNumber: issue.issueNumber,
|
|
139
214
|
title: issue.title,
|
|
140
215
|
labels: issue.labels,
|
|
141
|
-
status
|
|
216
|
+
status,
|
|
142
217
|
phases: issue.phases,
|
|
143
218
|
totalDurationSeconds,
|
|
219
|
+
...(abort && {
|
|
220
|
+
aborted: true,
|
|
221
|
+
abortReason: abort.reason,
|
|
222
|
+
}),
|
|
144
223
|
...(issue.prNumber != null && {
|
|
145
224
|
prNumber: issue.prNumber,
|
|
146
225
|
}),
|
|
147
226
|
...(issue.prUrl != null && {
|
|
148
227
|
prUrl: issue.prUrl,
|
|
149
228
|
}),
|
|
229
|
+
...(issue.specRecommendation != null && {
|
|
230
|
+
specRecommendation: issue.specRecommendation,
|
|
231
|
+
}),
|
|
150
232
|
};
|
|
151
233
|
this.runLog.issues.push(issueLog);
|
|
152
234
|
// Clean up from activeIssues map
|
|
@@ -168,22 +250,29 @@ export class LogWriter {
|
|
|
168
250
|
*
|
|
169
251
|
* @param options - Optional finalization options
|
|
170
252
|
* @param options.endCommit - Git commit SHA at run end (AC-2)
|
|
253
|
+
* @param options.aborted - Set when finalizing because the run was
|
|
254
|
+
* terminated by an external signal (#856). Every still-in-flight issue is
|
|
255
|
+
* recorded as an abort naming its cause, and the run log carries
|
|
256
|
+
* `abortedBy`. Without it, a killed run's log is indistinguishable from a
|
|
257
|
+
* clean one that happened to do nothing.
|
|
171
258
|
* @returns Path to the written log file
|
|
172
259
|
*/
|
|
173
260
|
async finalize(options) {
|
|
174
261
|
if (!this.runLog) {
|
|
175
262
|
throw new Error("LogWriter not initialized.");
|
|
176
263
|
}
|
|
264
|
+
const abort = options?.aborted;
|
|
177
265
|
// Complete any pending issues (Map-based concurrent tracking)
|
|
178
266
|
for (const issueNum of [...this.activeIssues.keys()]) {
|
|
179
|
-
this.completeIssue(issueNum);
|
|
267
|
+
this.completeIssue(issueNum, abort);
|
|
180
268
|
}
|
|
181
269
|
// Fallback: complete legacy currentIssue if not already handled
|
|
182
270
|
if (this.currentIssue) {
|
|
183
|
-
this.completeIssue();
|
|
271
|
+
this.completeIssue(undefined, abort);
|
|
184
272
|
}
|
|
185
273
|
const finalLog = finalizeRunLog(this.runLog, {
|
|
186
274
|
endCommit: options?.endCommit,
|
|
275
|
+
abortedBy: abort?.signal,
|
|
187
276
|
});
|
|
188
277
|
const filename = generateLogFilename(finalLog.runId, new Date(finalLog.startTime));
|
|
189
278
|
// Write to project logs
|
|
@@ -41,19 +41,20 @@ export declare const FailureCategorySchema: z.ZodEnum<{
|
|
|
41
41
|
build_error: "build_error";
|
|
42
42
|
rate_limit: "rate_limit";
|
|
43
43
|
billing: "billing";
|
|
44
|
+
pr_creation: "pr_creation";
|
|
44
45
|
}>;
|
|
45
46
|
export type FailureCategory = z.infer<typeof FailureCategorySchema>;
|
|
46
47
|
/**
|
|
47
48
|
* Available phases (aligned with run-log-schema.ts)
|
|
48
49
|
*/
|
|
49
50
|
export declare const MetricPhaseSchema: z.ZodEnum<{
|
|
50
|
-
|
|
51
|
-
loop: "loop";
|
|
51
|
+
exec: "exec";
|
|
52
52
|
spec: "spec";
|
|
53
53
|
"security-review": "security-review";
|
|
54
|
-
exec: "exec";
|
|
55
54
|
testgen: "testgen";
|
|
56
55
|
test: "test";
|
|
56
|
+
qa: "qa";
|
|
57
|
+
loop: "loop";
|
|
57
58
|
}>;
|
|
58
59
|
export type MetricPhase = z.infer<typeof MetricPhaseSchema>;
|
|
59
60
|
/**
|
|
@@ -85,13 +86,13 @@ export declare const MetricRunSchema: z.ZodObject<{
|
|
|
85
86
|
date: z.ZodString;
|
|
86
87
|
issues: z.ZodArray<z.ZodNumber>;
|
|
87
88
|
phases: z.ZodArray<z.ZodEnum<{
|
|
88
|
-
|
|
89
|
-
loop: "loop";
|
|
89
|
+
exec: "exec";
|
|
90
90
|
spec: "spec";
|
|
91
91
|
"security-review": "security-review";
|
|
92
|
-
exec: "exec";
|
|
93
92
|
testgen: "testgen";
|
|
94
93
|
test: "test";
|
|
94
|
+
qa: "qa";
|
|
95
|
+
loop: "loop";
|
|
95
96
|
}>>;
|
|
96
97
|
outcome: z.ZodEnum<{
|
|
97
98
|
success: "success";
|
|
@@ -110,7 +111,17 @@ export declare const MetricRunSchema: z.ZodObject<{
|
|
|
110
111
|
build_error: "build_error";
|
|
111
112
|
rate_limit: "rate_limit";
|
|
112
113
|
billing: "billing";
|
|
114
|
+
pr_creation: "pr_creation";
|
|
113
115
|
}>>;
|
|
116
|
+
phasePolicies: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodObject<{
|
|
117
|
+
model: z.ZodOptional<z.ZodString>;
|
|
118
|
+
effort: z.ZodOptional<z.ZodString>;
|
|
119
|
+
}, z.core.$strip>>>;
|
|
120
|
+
effortEscalations: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
121
|
+
phase: z.ZodString;
|
|
122
|
+
base: z.ZodString;
|
|
123
|
+
escalated: z.ZodString;
|
|
124
|
+
}, z.core.$strip>>>;
|
|
114
125
|
metrics: z.ZodObject<{
|
|
115
126
|
tokensUsed: z.ZodNumber;
|
|
116
127
|
filesChanged: z.ZodNumber;
|
|
@@ -135,13 +146,13 @@ export declare const MetricsSchema: z.ZodObject<{
|
|
|
135
146
|
date: z.ZodString;
|
|
136
147
|
issues: z.ZodArray<z.ZodNumber>;
|
|
137
148
|
phases: z.ZodArray<z.ZodEnum<{
|
|
138
|
-
|
|
139
|
-
loop: "loop";
|
|
149
|
+
exec: "exec";
|
|
140
150
|
spec: "spec";
|
|
141
151
|
"security-review": "security-review";
|
|
142
|
-
exec: "exec";
|
|
143
152
|
testgen: "testgen";
|
|
144
153
|
test: "test";
|
|
154
|
+
qa: "qa";
|
|
155
|
+
loop: "loop";
|
|
145
156
|
}>>;
|
|
146
157
|
outcome: z.ZodEnum<{
|
|
147
158
|
success: "success";
|
|
@@ -160,7 +171,17 @@ export declare const MetricsSchema: z.ZodObject<{
|
|
|
160
171
|
build_error: "build_error";
|
|
161
172
|
rate_limit: "rate_limit";
|
|
162
173
|
billing: "billing";
|
|
174
|
+
pr_creation: "pr_creation";
|
|
163
175
|
}>>;
|
|
176
|
+
phasePolicies: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodObject<{
|
|
177
|
+
model: z.ZodOptional<z.ZodString>;
|
|
178
|
+
effort: z.ZodOptional<z.ZodString>;
|
|
179
|
+
}, z.core.$strip>>>;
|
|
180
|
+
effortEscalations: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
181
|
+
phase: z.ZodString;
|
|
182
|
+
base: z.ZodString;
|
|
183
|
+
escalated: z.ZodString;
|
|
184
|
+
}, z.core.$strip>>>;
|
|
164
185
|
metrics: z.ZodObject<{
|
|
165
186
|
tokensUsed: z.ZodNumber;
|
|
166
187
|
filesChanged: z.ZodNumber;
|
|
@@ -193,6 +214,28 @@ export declare function createMetricRun(options: {
|
|
|
193
214
|
model?: string;
|
|
194
215
|
flags?: string[];
|
|
195
216
|
failureCategory?: FailureCategory;
|
|
217
|
+
/**
|
|
218
|
+
* Resolved per-phase model/effort overrides (#914), keyed by phase name.
|
|
219
|
+
* Pass only the phases that actually had a configured override — a phase
|
|
220
|
+
* that inherited the CLI default should not appear here at all. See
|
|
221
|
+
* `resolvePhasePolicies` in `config-resolver.ts`, which already produces
|
|
222
|
+
* a map shaped this way.
|
|
223
|
+
*/
|
|
224
|
+
phasePolicies?: Record<string, {
|
|
225
|
+
model?: string;
|
|
226
|
+
effort?: string;
|
|
227
|
+
}>;
|
|
228
|
+
/**
|
|
229
|
+
* Effort escalations applied during this run (#915), one entry per
|
|
230
|
+
* escalated phase execution. Pass only executions that actually escalated
|
|
231
|
+
* — see `MetricRunSchema.effortEscalations`'s doc comment for why this is
|
|
232
|
+
* a sibling array rather than an extension of `phasePolicies`.
|
|
233
|
+
*/
|
|
234
|
+
effortEscalations?: Array<{
|
|
235
|
+
phase: string;
|
|
236
|
+
base: string;
|
|
237
|
+
escalated: string;
|
|
238
|
+
}>;
|
|
196
239
|
metrics?: Partial<RunMetrics>;
|
|
197
240
|
}): MetricRun;
|
|
198
241
|
/**
|
|
@@ -96,6 +96,33 @@ export const MetricRunSchema = z.object({
|
|
|
96
96
|
* existed (additive — no `version` bump required).
|
|
97
97
|
*/
|
|
98
98
|
failureCategory: FailureCategorySchema.optional(),
|
|
99
|
+
/**
|
|
100
|
+
* Resolved per-phase `model`/`effort` overrides (#914), keyed by phase
|
|
101
|
+
* name. Only phases with a configured override get an entry — a phase
|
|
102
|
+
* that inherited the CLI default is omitted entirely, not recorded with
|
|
103
|
+
* undefined fields. Enum/alias strings only, consistent with this
|
|
104
|
+
* schema's no-file-paths/no-content privacy contract. Optional and
|
|
105
|
+
* additive — absent on records written before this field existed.
|
|
106
|
+
*/
|
|
107
|
+
phasePolicies: z
|
|
108
|
+
.record(z.string(), z.object({ model: z.string().optional(), effort: z.string().optional() }))
|
|
109
|
+
.optional(),
|
|
110
|
+
/**
|
|
111
|
+
* Effort escalations applied during this run (#915), one entry per
|
|
112
|
+
* escalated phase execution — distinct from `phasePolicies`, which is a
|
|
113
|
+
* flat phase→policy map recorded once per run and can't express a value
|
|
114
|
+
* that changes per retry. Only populated when at least one execution
|
|
115
|
+
* escalated; omitted entirely (not an empty array) otherwise, matching
|
|
116
|
+
* `phasePolicies`'s omit-when-empty convention. Phase names and enum
|
|
117
|
+
* effort strings only, consistent with this schema's privacy contract.
|
|
118
|
+
*/
|
|
119
|
+
effortEscalations: z
|
|
120
|
+
.array(z.object({
|
|
121
|
+
phase: z.string(),
|
|
122
|
+
base: z.string(),
|
|
123
|
+
escalated: z.string(),
|
|
124
|
+
}))
|
|
125
|
+
.optional(),
|
|
99
126
|
/** Aggregate metrics */
|
|
100
127
|
metrics: RunMetricsSchema,
|
|
101
128
|
});
|
|
@@ -137,6 +164,12 @@ export function createMetricRun(options) {
|
|
|
137
164
|
model: options.model ?? "unknown",
|
|
138
165
|
flags: options.flags ?? [],
|
|
139
166
|
failureCategory: options.failureCategory,
|
|
167
|
+
...(options.phasePolicies && Object.keys(options.phasePolicies).length > 0
|
|
168
|
+
? { phasePolicies: options.phasePolicies }
|
|
169
|
+
: {}),
|
|
170
|
+
...(options.effortEscalations && options.effortEscalations.length > 0
|
|
171
|
+
? { effortEscalations: options.effortEscalations }
|
|
172
|
+
: {}),
|
|
140
173
|
metrics: {
|
|
141
174
|
tokensUsed: options.metrics?.tokensUsed ?? 0,
|
|
142
175
|
filesChanged: options.metrics?.filesChanged ?? 0,
|
|
@@ -70,6 +70,17 @@ export declare class MetricsWriter {
|
|
|
70
70
|
model?: string;
|
|
71
71
|
flags?: string[];
|
|
72
72
|
failureCategory?: FailureCategory;
|
|
73
|
+
/** Resolved per-phase model/effort overrides (#914). See `createMetricRun`. */
|
|
74
|
+
phasePolicies?: Record<string, {
|
|
75
|
+
model?: string;
|
|
76
|
+
effort?: string;
|
|
77
|
+
}>;
|
|
78
|
+
/** Effort escalations applied during this run (#915). See `createMetricRun`. */
|
|
79
|
+
effortEscalations?: Array<{
|
|
80
|
+
phase: string;
|
|
81
|
+
base: string;
|
|
82
|
+
escalated: string;
|
|
83
|
+
}>;
|
|
73
84
|
metrics?: Partial<RunMetrics>;
|
|
74
85
|
}): Promise<MetricRun>;
|
|
75
86
|
/**
|
|
@@ -11,6 +11,18 @@
|
|
|
11
11
|
* ```
|
|
12
12
|
*/
|
|
13
13
|
import { type Phase, type PhaseMarker } from "./state-schema.js";
|
|
14
|
+
/**
|
|
15
|
+
* Strip markdown code blocks and inline code from text.
|
|
16
|
+
* This prevents phase markers inside code examples from being parsed.
|
|
17
|
+
*
|
|
18
|
+
* Exported for reuse by `spec-recommendation.ts` (#921), which applies the
|
|
19
|
+
* same code-fence stripping to the `SEQUANT_SPEC` marker so documentation
|
|
20
|
+
* examples embedding that marker can't false-positive either.
|
|
21
|
+
*
|
|
22
|
+
* @param text - The text to strip code from
|
|
23
|
+
* @returns Text with code blocks and inline code removed
|
|
24
|
+
*/
|
|
25
|
+
export declare function stripMarkdownCode(text: string): string;
|
|
14
26
|
/**
|
|
15
27
|
* Format a phase marker as an HTML comment string for embedding in GitHub comments.
|
|
16
28
|
*
|
|
@@ -25,10 +25,14 @@ const INLINE_CODE_REGEX = /`[^`\n]+`/g;
|
|
|
25
25
|
* Strip markdown code blocks and inline code from text.
|
|
26
26
|
* This prevents phase markers inside code examples from being parsed.
|
|
27
27
|
*
|
|
28
|
+
* Exported for reuse by `spec-recommendation.ts` (#921), which applies the
|
|
29
|
+
* same code-fence stripping to the `SEQUANT_SPEC` marker so documentation
|
|
30
|
+
* examples embedding that marker can't false-positive either.
|
|
31
|
+
*
|
|
28
32
|
* @param text - The text to strip code from
|
|
29
33
|
* @returns Text with code blocks and inline code removed
|
|
30
34
|
*/
|
|
31
|
-
function stripMarkdownCode(text) {
|
|
35
|
+
export function stripMarkdownCode(text) {
|
|
32
36
|
// First remove fenced code blocks (multi-line)
|
|
33
37
|
let result = text.replace(FENCED_CODE_BLOCK_REGEX, "");
|
|
34
38
|
// Then remove inline code
|
|
@@ -11,7 +11,7 @@ import { ShutdownManager } from "../shutdown.js";
|
|
|
11
11
|
import { Phase, ExecutionConfig, PhaseResult, QaVerdict, PhasePauseHandle } from "./types.js";
|
|
12
12
|
import type { QaSummary } from "./run-log-schema.js";
|
|
13
13
|
import type { AgentPhaseResult, ResumeHandle } from "./drivers/index.js";
|
|
14
|
-
import { SequantError } from "../errors.js";
|
|
14
|
+
import { RateLimitError, SequantError } from "../errors.js";
|
|
15
15
|
/**
|
|
16
16
|
* Leading + trailing throttle. Fires the wrapped callback immediately on the
|
|
17
17
|
* first call, drops subsequent calls that arrive inside `intervalMs` but
|
|
@@ -67,7 +67,150 @@ export declare const RATE_LIMIT_RETRY_BACKOFF_MS = 5000;
|
|
|
67
67
|
* @internal Exported for testing only
|
|
68
68
|
*/
|
|
69
69
|
export declare function isWindowExhaustedRateLimit(error: SequantError | undefined, now?: number): boolean;
|
|
70
|
+
/**
|
|
71
|
+
* Buffer added to a rate-limit reset before waking (#804 AC-5). `resetsAt` is a
|
|
72
|
+
* floor, not an exact moment: clock skew between this host and the API, plus
|
|
73
|
+
* server-side rounding, otherwise produce an immediate second rejection on
|
|
74
|
+
* wake. One minute is negligible against the five-hour/seven-day windows this
|
|
75
|
+
* exists for.
|
|
76
|
+
*
|
|
77
|
+
* @internal Exported for testing only
|
|
78
|
+
*/
|
|
79
|
+
export declare const AUTO_WAIT_BUFFER_MS: number;
|
|
80
|
+
/**
|
|
81
|
+
* Hard cap on auto-waits per issue (#804 AC-6), independent of the minutes
|
|
82
|
+
* budget. A window that is still closed on wake must not produce an unbounded
|
|
83
|
+
* pause loop, so the count bounds the *number* of pauses while
|
|
84
|
+
* `autoWaitMinutes` bounds their *total duration*. Either bound being spent
|
|
85
|
+
* halts with today's labeled message.
|
|
86
|
+
*
|
|
87
|
+
* @internal Exported for testing only
|
|
88
|
+
*/
|
|
89
|
+
export declare const AUTO_WAIT_MAX_WAITS = 2;
|
|
90
|
+
/**
|
|
91
|
+
* Granularity of the auto-wait sleep (#804 AC-7). The wait is performed as a
|
|
92
|
+
* series of ticks rather than one multi-hour `delayFn` call so that (a) the
|
|
93
|
+
* renderer/heartbeat can be refreshed with the remaining time, and (b) a
|
|
94
|
+
* Ctrl-C is observed promptly instead of at the wake time.
|
|
95
|
+
*
|
|
96
|
+
* @internal Exported for testing only
|
|
97
|
+
*/
|
|
98
|
+
export declare const AUTO_WAIT_TICK_MS: number;
|
|
99
|
+
/**
|
|
100
|
+
* Mutable per-issue accounting for auto-wait (#804 AC-6).
|
|
101
|
+
*
|
|
102
|
+
* Deliberately per-ISSUE, not per-phase: `executePhaseWithRetry` runs once per
|
|
103
|
+
* phase, so a ledger created inside it would grant every phase its own full
|
|
104
|
+
* budget and bound. `runIssueWithLogging` creates one and threads it through
|
|
105
|
+
* all of an issue's phases.
|
|
106
|
+
*/
|
|
107
|
+
export interface AutoWaitLedger {
|
|
108
|
+
/** Total wait budget in ms. `0` disables auto-wait entirely. */
|
|
109
|
+
budgetMs: number;
|
|
110
|
+
/** Number of waits already granted for this issue. */
|
|
111
|
+
waits: number;
|
|
112
|
+
/** Cumulative ms already spent waiting for this issue. */
|
|
113
|
+
spentMs: number;
|
|
114
|
+
}
|
|
115
|
+
/**
|
|
116
|
+
* Build a fresh ledger from a minutes budget. A missing, negative or
|
|
117
|
+
* non-finite budget yields a disabled ledger (`budgetMs: 0`) — the default,
|
|
118
|
+
* which preserves pre-#804 behavior exactly.
|
|
119
|
+
*/
|
|
120
|
+
export declare function createAutoWaitLedger(budgetMinutes?: number): AutoWaitLedger;
|
|
121
|
+
/**
|
|
122
|
+
* A granted auto-wait: how long to sleep, when to wake, and the narrowed
|
|
123
|
+
* rate-limit error that justified it (carried so callers need no cast).
|
|
124
|
+
*/
|
|
125
|
+
export interface AutoWaitDecision {
|
|
126
|
+
/** Ms to sleep. Always > 0. */
|
|
127
|
+
waitMs: number;
|
|
128
|
+
/** Epoch ms to wake at — `resetsAt` normalized to ms, plus the buffer. */
|
|
129
|
+
wakeAtMs: number;
|
|
130
|
+
/** The rate limit that triggered the wait. Never a `BillingError`. */
|
|
131
|
+
error: RateLimitError;
|
|
132
|
+
}
|
|
133
|
+
/**
|
|
134
|
+
* Decide whether to wait out an exhausted rate-limit window (#804 AC-3).
|
|
135
|
+
*
|
|
136
|
+
* Deliberately separate from {@link isWindowExhaustedRateLimit} and
|
|
137
|
+
* {@link RATE_LIMIT_WINDOW_SKIP_THRESHOLD_MS}, which answer *"is this transient
|
|
138
|
+
* or exhausted?"*. This answers a different question — *"am I willing to wait
|
|
139
|
+
* that long?"* — and fires ONLY once the former is already true. Keeping them
|
|
140
|
+
* apart is what leaves `autoWaitMinutes` the single user-facing dial for the
|
|
141
|
+
* wait-vs-halt outcome.
|
|
142
|
+
*
|
|
143
|
+
* Returns `null` (no wait — halt as before) when:
|
|
144
|
+
* - auto-wait is off, or the per-issue wait count is spent;
|
|
145
|
+
* - the failure is not a window-exhausted `RateLimitError`. A `BillingError`
|
|
146
|
+
* lands here: it is a sibling class, not a subclass, so the `instanceof`
|
|
147
|
+
* check inside `isWindowExhaustedRateLimit` excludes it. That is load-bearing
|
|
148
|
+
* — a `BillingError` may still carry `resetsAt` (an explicit
|
|
149
|
+
* `credits_required`, or a window that has already passed — see the #860
|
|
150
|
+
* narrowing in `isBillingFailure`), so gating on the timestamp's presence
|
|
151
|
+
* instead of the error type would wait out a credits failure that no amount
|
|
152
|
+
* of waiting can heal (AC-4). Since #860, a *live* recognized window
|
|
153
|
+
* (`five_hour`/`seven_day*` + future reset) classifies as `RateLimitError`
|
|
154
|
+
* upstream even when `out_of_credits` is present, which is what lets the
|
|
155
|
+
* real captured subscription payloads reach this decision at all;
|
|
156
|
+
* - the reset has already passed (nothing to wait for);
|
|
157
|
+
* - the required wait exceeds the budget REMAINING, not the total (AC-6).
|
|
158
|
+
*
|
|
159
|
+
* @internal Exported for testing only
|
|
160
|
+
*/
|
|
161
|
+
export declare function shouldAutoWaitForReset(error: SequantError | undefined, ledger: AutoWaitLedger, now?: number): AutoWaitDecision | null;
|
|
162
|
+
/**
|
|
163
|
+
* Sleep until an auto-wait's wake time, in ticks (#804 AC-7).
|
|
164
|
+
*
|
|
165
|
+
* Chunking the sleep is what makes a multi-hour pause survivable:
|
|
166
|
+
* - `onTick` refreshes the live display so the wait is visible rather than a
|
|
167
|
+
* silent stall (the #574 complaint at 60x scale);
|
|
168
|
+
* - each tick races the injected `delayFn` against the abort signal, so Ctrl-C
|
|
169
|
+
* returns immediately instead of blocking until the wake.
|
|
170
|
+
*
|
|
171
|
+
* Reuses the caller's `delayFn` so the wait stays fully test-injectable.
|
|
172
|
+
*
|
|
173
|
+
* Returns the ms actually slept and whether the wait was aborted.
|
|
174
|
+
*
|
|
175
|
+
* @internal Exported for testing only
|
|
176
|
+
*/
|
|
177
|
+
export declare function waitForWindowReset(waitMs: number, options: {
|
|
178
|
+
delayFn: (ms: number) => Promise<void>;
|
|
179
|
+
signal?: AbortSignal;
|
|
180
|
+
onTick?: (remainingMs: number) => void;
|
|
181
|
+
now?: () => number;
|
|
182
|
+
tickMs?: number;
|
|
183
|
+
}): Promise<{
|
|
184
|
+
sleptMs: number;
|
|
185
|
+
aborted: boolean;
|
|
186
|
+
}>;
|
|
187
|
+
/**
|
|
188
|
+
* Run one granted auto-wait end to end (#804): emit the live notices, sleep,
|
|
189
|
+
* update the ledger, and report whether Ctrl-C interrupted it.
|
|
190
|
+
*
|
|
191
|
+
* Shared by both arms of the retry ladder (the cold-start loop and the
|
|
192
|
+
* `skipColdStartRetry` single-attempt path) so the two cannot drift on
|
|
193
|
+
* bookkeeping or messaging.
|
|
194
|
+
*
|
|
195
|
+
* @internal Exported for testing only
|
|
196
|
+
*/
|
|
197
|
+
export declare function performAutoWait(issueNumber: number, phase: Phase, config: ExecutionConfig, decision: AutoWaitDecision, ledger: AutoWaitLedger, delayFn: (ms: number) => Promise<void>, shutdownManager?: ShutdownManager, spinner?: PhasePauseHandle): Promise<{
|
|
198
|
+
aborted: boolean;
|
|
199
|
+
}>;
|
|
70
200
|
export declare function parseQaVerdict(output: string): QaVerdict | null;
|
|
201
|
+
/**
|
|
202
|
+
* Distinguish a QA turn that produced *no verdict at all* from one whose output
|
|
203
|
+
* was present but unparseable (#853). Returns true when the output is empty /
|
|
204
|
+
* whitespace, or when its tail contains deferral language — the agent treating
|
|
205
|
+
* its one-shot phase as if a later turn were available.
|
|
206
|
+
*
|
|
207
|
+
* Both cases are hard failures either way; this only refines which message is
|
|
208
|
+
* emitted, so a false positive is harmless (it swaps one failing message for
|
|
209
|
+
* another). Kept deliberately conservative and literal to stay ReDoS-safe.
|
|
210
|
+
*
|
|
211
|
+
* @internal Exported for testing only.
|
|
212
|
+
*/
|
|
213
|
+
export declare function endedWithoutVerdict(output: string | undefined): boolean;
|
|
71
214
|
/**
|
|
72
215
|
* Parse condensed QA summary from QA phase output (#434).
|
|
73
216
|
*
|
|
@@ -109,22 +252,59 @@ export declare function formatDuration(seconds: number): string;
|
|
|
109
252
|
*/
|
|
110
253
|
export declare function resolveBaseRef(cwd: string): string;
|
|
111
254
|
/**
|
|
112
|
-
*
|
|
113
|
-
* Returns true if HEAD has commits unique to it relative to the resolved
|
|
114
|
-
* base ref (see {@link resolveBaseRef}) OR uncommitted work is present.
|
|
255
|
+
* Three-way classification of what the exec phase left in the worktree (#879).
|
|
115
256
|
*
|
|
116
|
-
*
|
|
117
|
-
*
|
|
118
|
-
*
|
|
119
|
-
*
|
|
120
|
-
*
|
|
257
|
+
* - `commits`: HEAD has commits unique to it relative to the base ref — real,
|
|
258
|
+
* deliverable work that can rebase, push, and become a PR.
|
|
259
|
+
* - `uncommitted`: no such commits, but the tree is dirty. This is NOT a
|
|
260
|
+
* deliverable: uncommitted work cannot rebase, push, or become a PR (#879's
|
|
261
|
+
* defect — a dirty tree used to be counted as exec success, producing a run
|
|
262
|
+
* that "passed" with no commits and no PR). `paths` names the dirty files.
|
|
263
|
+
* - `none`: no commits and a clean tree — exec produced literally nothing
|
|
264
|
+
* (#534's original empty-branch class).
|
|
265
|
+
* - `unknown`: a git command failed. Callers fail OPEN on this (treat as work)
|
|
266
|
+
* — a transient git error is better diagnosed as a real run than as a
|
|
267
|
+
* spurious phase failure on every exec.
|
|
268
|
+
*/
|
|
269
|
+
export type ExecChangeState = {
|
|
270
|
+
kind: "commits";
|
|
271
|
+
} | {
|
|
272
|
+
kind: "uncommitted";
|
|
273
|
+
paths: string[];
|
|
274
|
+
} | {
|
|
275
|
+
kind: "none";
|
|
276
|
+
} | {
|
|
277
|
+
kind: "unknown";
|
|
278
|
+
};
|
|
279
|
+
/**
|
|
280
|
+
* Classify what the exec phase produced in the worktree (#879).
|
|
281
|
+
*
|
|
282
|
+
* Uses `git rev-list --count <base>..HEAD` (commits reachable from HEAD but not
|
|
283
|
+
* the base) instead of `git diff <base>..HEAD`, because the two-dot diff also
|
|
284
|
+
* fires in reverse when the base has advanced past HEAD — on stale branches
|
|
285
|
+
* that would falsely report "has commits" even when exec produced nothing,
|
|
286
|
+
* reintroducing the bug #534 is fixing.
|
|
121
287
|
*
|
|
122
288
|
* The base ref defaults to `origin/main` but is overridden to the worktree's
|
|
123
|
-
* recorded base (see #537) so zero-diff execs are still detected on
|
|
124
|
-
*
|
|
289
|
+
* recorded base (see #537) so zero-diff execs are still detected on custom-base
|
|
290
|
+
* worktrees (e.g. those created with `--base feature/epic`).
|
|
125
291
|
*
|
|
126
|
-
*
|
|
127
|
-
*
|
|
292
|
+
* Read-only: runs only `git rev-list` and `git status --porcelain`, so it never
|
|
293
|
+
* mutates the worktree — an exec phase that fails on an `uncommitted` result
|
|
294
|
+
* leaves the dirty files exactly where the agent left them (#879 AC-3).
|
|
295
|
+
*
|
|
296
|
+
* @internal Exported for testing only.
|
|
297
|
+
*/
|
|
298
|
+
export declare function classifyExecChanges(cwd: string): ExecChangeState;
|
|
299
|
+
/**
|
|
300
|
+
* Check whether the exec phase produced deliverable work in the worktree.
|
|
301
|
+
*
|
|
302
|
+
* Thin boolean wrapper over {@link classifyExecChanges}: only `commits` (real
|
|
303
|
+
* work) and `unknown` (git error — fail open) count as "has changes". Note the
|
|
304
|
+
* #879 behaviour change: an `uncommitted`-only tree now returns **false**, since
|
|
305
|
+
* uncommitted work is not a deliverable. Both callers (the exec guard in
|
|
306
|
+
* {@link mapAgentSuccessToPhaseResult} and the ready gate) want this stricter
|
|
307
|
+
* semantics.
|
|
128
308
|
*
|
|
129
309
|
* @internal Exported for testing only.
|
|
130
310
|
*/
|
|
@@ -195,7 +375,16 @@ export declare function executePhaseWithRetry(issueNumber: number, phase: Phase,
|
|
|
195
375
|
/** @internal Injected for testing — defaults to module-level executePhase */
|
|
196
376
|
executePhaseFn?: typeof executePhase,
|
|
197
377
|
/** @internal Injected for testing — defaults to setTimeout-based delay */
|
|
198
|
-
delayFn?: (ms: number) => Promise<void
|
|
378
|
+
delayFn?: (ms: number) => Promise<void>,
|
|
379
|
+
/**
|
|
380
|
+
* Per-issue auto-wait accounting (#804). Deliberately the LAST parameter:
|
|
381
|
+
* the #761/#799 regression tests call this function positionally with
|
|
382
|
+
* `executePhaseFn` at 8 and `delayFn` at 9, and AC-2 requires those tests to
|
|
383
|
+
* pass unmodified. Defaults to a fresh disabled-or-config-derived ledger, so
|
|
384
|
+
* a caller that does not thread one still gets correct (bounded) behavior —
|
|
385
|
+
* just scoped to this phase rather than the issue.
|
|
386
|
+
*/
|
|
387
|
+
autoWaitLedger?: AutoWaitLedger): Promise<PhaseResult & {
|
|
199
388
|
sessionId?: string;
|
|
200
389
|
resumeHandle?: ResumeHandle;
|
|
201
390
|
}>;
|