sequant 2.8.0 → 2.10.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 +23 -4
- package/dist/bin/cli.js +99 -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 +58 -2
- package/dist/marketplace/external_plugins/sequant/hooks/parallel-marker.sh +50 -0
- package/dist/marketplace/external_plugins/sequant/hooks/post-tool.sh +38 -10
- package/dist/marketplace/external_plugins/sequant/hooks/pre-tool.sh +405 -67
- package/dist/marketplace/external_plugins/sequant/skills/_shared/references/trust-model.md +18 -0
- package/dist/marketplace/external_plugins/sequant/skills/assess/SKILL.md +250 -344
- package/dist/marketplace/external_plugins/sequant/skills/assess/references/predicted-collision-detection.md +10 -7
- 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 +4 -2
- 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/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 +11 -3
- 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/spec/references/verification-criteria.md +1 -1
- 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.js +2 -0
- package/dist/src/commands/logs.js +20 -6
- 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 +25 -3
- package/dist/src/commands/ready.js +39 -13
- package/dist/src/commands/resume.d.ts +113 -0
- package/dist/src/commands/resume.js +193 -0
- package/dist/src/commands/run-display.d.ts +20 -0
- package/dist/src/commands/run-display.js +117 -5
- 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/stats.js +47 -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/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/assess-collision-detect.d.ts +19 -2
- package/dist/src/lib/assess-collision-detect.js +68 -4
- 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 +33 -0
- package/dist/src/lib/cli-flags.js +46 -0
- package/dist/src/lib/cli-ui/run-renderer-types.d.ts +21 -7
- package/dist/src/lib/cli-ui/run-renderer.d.ts +10 -0
- package/dist/src/lib/cli-ui/run-renderer.js +79 -9
- package/dist/src/lib/errors.d.ts +84 -6
- package/dist/src/lib/errors.js +142 -17
- package/dist/src/lib/locks/index.d.ts +3 -3
- package/dist/src/lib/locks/index.js +2 -2
- package/dist/src/lib/locks/lock-manager.d.ts +20 -2
- package/dist/src/lib/locks/lock-manager.js +47 -3
- package/dist/src/lib/locks/types.d.ts +34 -3
- package/dist/src/lib/locks/types.js +16 -0
- package/dist/src/lib/manifest.js +1 -17
- 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 +14 -0
- package/dist/src/lib/settings.js +61 -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 +9 -2
- package/dist/src/lib/test-tautology-detector.js +153 -12
- package/dist/src/lib/version-check.d.ts +31 -0
- package/dist/src/lib/version-check.js +46 -7
- package/dist/src/lib/workflow/batch-executor.d.ts +138 -6
- package/dist/src/lib/workflow/batch-executor.js +550 -54
- 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 +118 -0
- package/dist/src/lib/workflow/chain-resume.js +170 -0
- 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 +26 -0
- package/dist/src/lib/workflow/config-resolver.js +54 -2
- 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/agent-driver.d.ts +7 -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 +21 -0
- package/dist/src/lib/workflow/drivers/claude-code.js +50 -8
- 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/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 +44 -1
- package/dist/src/lib/workflow/log-writer.js +81 -14
- package/dist/src/lib/workflow/metrics-schema.d.ts +42 -3
- 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 +234 -13
- package/dist/src/lib/workflow/phase-executor.js +533 -56
- 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 +17 -4
- package/dist/src/lib/workflow/ready-gate.js +25 -9
- package/dist/src/lib/workflow/run-log-schema.d.ts +37 -0
- package/dist/src/lib/workflow/run-log-schema.js +92 -2
- package/dist/src/lib/workflow/run-orchestrator.d.ts +33 -0
- package/dist/src/lib/workflow/run-orchestrator.js +429 -47
- 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/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/status-derivation.d.ts +30 -0
- package/dist/src/lib/workflow/status-derivation.js +27 -0
- package/dist/src/lib/workflow/types.d.ts +149 -4
- package/dist/src/lib/workflow/types.js +1 -0
- package/dist/src/lib/workflow/worktree-manager.d.ts +70 -2
- package/dist/src/lib/workflow/worktree-manager.js +201 -52
- package/dist/src/mcp/tools/run.d.ts +11 -1
- package/dist/src/mcp/tools/run.js +22 -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 +9 -10
- package/templates/hooks/parallel-marker.sh +50 -0
- package/templates/hooks/post-tool.sh +38 -10
- package/templates/hooks/pre-tool.sh +405 -67
- package/templates/scripts/cleanup-worktree.sh +282 -30
- package/templates/scripts/new-feature.sh +264 -16
- package/templates/skills/_shared/references/trust-model.md +18 -0
- package/templates/skills/assess/SKILL.md +250 -344
- package/templates/skills/assess/references/predicted-collision-detection.md +10 -7
- package/templates/skills/docs/SKILL.md +4 -4
- package/templates/skills/exec/SKILL.md +26 -6
- package/templates/skills/fullsolve/SKILL.md +4 -2
- package/templates/skills/loop/SKILL.md +38 -2
- package/templates/skills/merger/SKILL.md +2 -2
- package/templates/skills/qa/SKILL.md +337 -57
- 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/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 +11 -3
- package/templates/skills/setup/SKILL.md +5 -5
- package/templates/skills/spec/SKILL.md +2 -0
- package/templates/skills/spec/references/verification-criteria.md +1 -1
- package/templates/skills/test/SKILL.md +1 -1
- 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
|
@@ -190,13 +190,14 @@ Run relevant commands and capture their output for review.`,
|
|
|
190
190
|
phaseRegistry.register({
|
|
191
191
|
name: "qa",
|
|
192
192
|
skill: "qa",
|
|
193
|
-
promptTemplate: "Review the implementation for GitHub issue #{issue} against acceptance criteria. Run the /qa {issue} workflow.",
|
|
193
|
+
promptTemplate: "Review the implementation for GitHub issue #{issue} against acceptance criteria. Run the /qa {issue} workflow. Your turn is one-shot: you must emit a `### Verdict:` line before the turn ends — deferring to a later turn or a background CI poll is not available.",
|
|
194
194
|
requiresWorktree: true,
|
|
195
195
|
driverOverrides: {
|
|
196
196
|
aider: {
|
|
197
197
|
promptTemplate: `Review the changes for GitHub issue #{issue}.
|
|
198
198
|
Run 'npm test' and 'npm run build' to verify everything works.
|
|
199
199
|
Check each acceptance criterion from the issue comments.
|
|
200
|
+
Your turn is one-shot: emit a verdict now — do not defer to a later turn or a background CI poll. If the tree is unreviewable, emit AC_NOT_MET (code-caused red CI) or NEEDS_VERIFICATION (pending/infra-blocked CI); never end without a verdict.
|
|
200
201
|
Output a verdict: READY_FOR_MERGE, AC_MET_BUT_NOT_A_PLUS, or AC_NOT_MET
|
|
201
202
|
with format "### Verdict: <VERDICT>" followed by an explanation.`,
|
|
202
203
|
},
|
|
@@ -9,6 +9,7 @@
|
|
|
9
9
|
* Async interface methods delegate to the sync implementations.
|
|
10
10
|
*/
|
|
11
11
|
import type { PlatformProvider, Issue, CreatePROptions, PRInfo, PRStatus, Comment } from "./platform-provider.js";
|
|
12
|
+
import type { AnnotatedCheck } from "../../qa/infra-blocked-ci.js";
|
|
12
13
|
/**
|
|
13
14
|
* PR merge status values (matches the casing returned by `gh pr view`).
|
|
14
15
|
*/
|
|
@@ -55,6 +56,30 @@ export interface BatchGitHubResult {
|
|
|
55
56
|
pullRequests: Record<number, BatchPRInfo>;
|
|
56
57
|
error?: string;
|
|
57
58
|
}
|
|
59
|
+
/**
|
|
60
|
+
* A single entry in a PR's `statusCheckRollup`.
|
|
61
|
+
*
|
|
62
|
+
* `gh pr view N --json statusCheckRollup` returns a heterogeneous array of two
|
|
63
|
+
* shapes, discriminated by `__typename`:
|
|
64
|
+
* - `CheckRun` (GitHub Actions / Checks API): progress in `status`
|
|
65
|
+
* (QUEUED | IN_PROGRESS | COMPLETED) with the outcome in `conclusion`.
|
|
66
|
+
* - `StatusContext` (legacy commit statuses): combined state in `state`
|
|
67
|
+
* (EXPECTED | PENDING | SUCCESS | FAILURE | ERROR).
|
|
68
|
+
* Only the fields the watch loop consumes are typed; unknown fields are ignored.
|
|
69
|
+
*/
|
|
70
|
+
export interface RollupEntry {
|
|
71
|
+
__typename?: string;
|
|
72
|
+
name?: string;
|
|
73
|
+
context?: string;
|
|
74
|
+
/** CheckRun progress. */
|
|
75
|
+
status?: string;
|
|
76
|
+
/** CheckRun outcome. */
|
|
77
|
+
conclusion?: string;
|
|
78
|
+
/** StatusContext combined state. */
|
|
79
|
+
state?: string;
|
|
80
|
+
}
|
|
81
|
+
/** PR mergeability as reported by `gh pr view --json mergeable`. */
|
|
82
|
+
export type MergeableState = "MERGEABLE" | "CONFLICTING" | "UNKNOWN";
|
|
58
83
|
export declare class GitHubProvider implements PlatformProvider {
|
|
59
84
|
name: string;
|
|
60
85
|
/**
|
|
@@ -151,6 +176,45 @@ export declare class GitHubProvider implements PlatformProvider {
|
|
|
151
176
|
* Used by upstream/issues.ts.
|
|
152
177
|
*/
|
|
153
178
|
commentOnIssueWithBodyFileSync(repo: string, issueNumber: number, bodyFile: string): boolean;
|
|
179
|
+
/**
|
|
180
|
+
* Fetch a PR's status-check rollup via `statusCheckRollup`.
|
|
181
|
+
*
|
|
182
|
+
* Uses `statusCheckRollup`, NOT the known-broken `--json checks` field
|
|
183
|
+
* (#443 / #818). Returns the raw heterogeneous entries; callers classify
|
|
184
|
+
* terminal-ness with the helpers in `merge-check/watch.ts`. Returns `[]` on
|
|
185
|
+
* any error so a transient `gh` failure degrades to "no checks yet" rather
|
|
186
|
+
* than crashing a poll loop.
|
|
187
|
+
*/
|
|
188
|
+
getStatusCheckRollupSync(prNumber: number): RollupEntry[];
|
|
189
|
+
/**
|
|
190
|
+
* Fetch a PR's mergeability state via `--json mergeable`.
|
|
191
|
+
*
|
|
192
|
+
* Right after a push GitHub reports `UNKNOWN` while it recomputes, then
|
|
193
|
+
* settles to `MERGEABLE` or `CONFLICTING`. `merge --watch` treats
|
|
194
|
+
* `CONFLICTING` as a dispatch block: CI never starts against an unmergeable
|
|
195
|
+
* ref. Returns `UNKNOWN` on any error — the safe default that keeps the loop
|
|
196
|
+
* polling rather than falsely reporting a conflict.
|
|
197
|
+
*/
|
|
198
|
+
getMergeableStateSync(prNumber: number): MergeableState;
|
|
199
|
+
/**
|
|
200
|
+
* Get a PR's head commit SHA via `--json headRefOid`.
|
|
201
|
+
*
|
|
202
|
+
* Needed to query the commit's check-run annotations for the billing-lockout
|
|
203
|
+
* signature. Returns `null` on error.
|
|
204
|
+
*/
|
|
205
|
+
getPRHeadShaSync(prNumber: number): string | null;
|
|
206
|
+
/**
|
|
207
|
+
* Fetch check-run annotations for a commit, shaped for `detectInfraBlockedCi`.
|
|
208
|
+
*
|
|
209
|
+
* Lists the commit's check runs (per_page=100, no pagination needed for the
|
|
210
|
+
* billing-lockout case — every run carries the same annotation, so the first
|
|
211
|
+
* page is representative), then fetches annotations for each run that reports
|
|
212
|
+
* any. Used only when the whole board is failing, to recognise the "job was
|
|
213
|
+
* not started" signature (#820). Degrades to `[]` on any error (404, rate
|
|
214
|
+
* limit, revoked token) so a broken-CI situation never crashes the watch
|
|
215
|
+
* loop — `detectInfraBlockedCi` then reads that as "not infra-blocked".
|
|
216
|
+
*/
|
|
217
|
+
getCheckRunAnnotationsSync(headSha: string): AnnotatedCheck[];
|
|
154
218
|
fetchIssue(id: string): Promise<Issue>;
|
|
155
219
|
postComment(issueId: string, body: string): Promise<void>;
|
|
156
220
|
addLabel(issueId: string, label: string): Promise<void>;
|
|
@@ -414,6 +414,158 @@ export class GitHubProvider {
|
|
|
414
414
|
return false;
|
|
415
415
|
}
|
|
416
416
|
}
|
|
417
|
+
// ─── Watch / CI-rollup helpers (for `merge --watch`, #818) ─────────
|
|
418
|
+
/**
|
|
419
|
+
* Fetch a PR's status-check rollup via `statusCheckRollup`.
|
|
420
|
+
*
|
|
421
|
+
* Uses `statusCheckRollup`, NOT the known-broken `--json checks` field
|
|
422
|
+
* (#443 / #818). Returns the raw heterogeneous entries; callers classify
|
|
423
|
+
* terminal-ness with the helpers in `merge-check/watch.ts`. Returns `[]` on
|
|
424
|
+
* any error so a transient `gh` failure degrades to "no checks yet" rather
|
|
425
|
+
* than crashing a poll loop.
|
|
426
|
+
*/
|
|
427
|
+
getStatusCheckRollupSync(prNumber) {
|
|
428
|
+
try {
|
|
429
|
+
const result = spawnSync("gh", [
|
|
430
|
+
"pr",
|
|
431
|
+
"view",
|
|
432
|
+
String(prNumber),
|
|
433
|
+
"--json",
|
|
434
|
+
"statusCheckRollup",
|
|
435
|
+
"-q",
|
|
436
|
+
".statusCheckRollup",
|
|
437
|
+
], { encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"], timeout: 15000 });
|
|
438
|
+
if (result.status !== 0 || !result.stdout)
|
|
439
|
+
return [];
|
|
440
|
+
const parsed = JSON.parse(result.stdout);
|
|
441
|
+
return Array.isArray(parsed) ? parsed : [];
|
|
442
|
+
}
|
|
443
|
+
catch {
|
|
444
|
+
return [];
|
|
445
|
+
}
|
|
446
|
+
}
|
|
447
|
+
/**
|
|
448
|
+
* Fetch a PR's mergeability state via `--json mergeable`.
|
|
449
|
+
*
|
|
450
|
+
* Right after a push GitHub reports `UNKNOWN` while it recomputes, then
|
|
451
|
+
* settles to `MERGEABLE` or `CONFLICTING`. `merge --watch` treats
|
|
452
|
+
* `CONFLICTING` as a dispatch block: CI never starts against an unmergeable
|
|
453
|
+
* ref. Returns `UNKNOWN` on any error — the safe default that keeps the loop
|
|
454
|
+
* polling rather than falsely reporting a conflict.
|
|
455
|
+
*/
|
|
456
|
+
getMergeableStateSync(prNumber) {
|
|
457
|
+
try {
|
|
458
|
+
const result = spawnSync("gh", [
|
|
459
|
+
"pr",
|
|
460
|
+
"view",
|
|
461
|
+
String(prNumber),
|
|
462
|
+
"--json",
|
|
463
|
+
"mergeable",
|
|
464
|
+
"-q",
|
|
465
|
+
".mergeable",
|
|
466
|
+
], { encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"], timeout: 10000 });
|
|
467
|
+
if (result.status === 0 && result.stdout) {
|
|
468
|
+
const state = result.stdout.toString().trim().toUpperCase();
|
|
469
|
+
if (state === "MERGEABLE" ||
|
|
470
|
+
state === "CONFLICTING" ||
|
|
471
|
+
state === "UNKNOWN") {
|
|
472
|
+
return state;
|
|
473
|
+
}
|
|
474
|
+
}
|
|
475
|
+
}
|
|
476
|
+
catch {
|
|
477
|
+
// fall through to UNKNOWN
|
|
478
|
+
}
|
|
479
|
+
return "UNKNOWN";
|
|
480
|
+
}
|
|
481
|
+
/**
|
|
482
|
+
* Get a PR's head commit SHA via `--json headRefOid`.
|
|
483
|
+
*
|
|
484
|
+
* Needed to query the commit's check-run annotations for the billing-lockout
|
|
485
|
+
* signature. Returns `null` on error.
|
|
486
|
+
*/
|
|
487
|
+
getPRHeadShaSync(prNumber) {
|
|
488
|
+
try {
|
|
489
|
+
const result = spawnSync("gh", [
|
|
490
|
+
"pr",
|
|
491
|
+
"view",
|
|
492
|
+
String(prNumber),
|
|
493
|
+
"--json",
|
|
494
|
+
"headRefOid",
|
|
495
|
+
"-q",
|
|
496
|
+
".headRefOid",
|
|
497
|
+
], { encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"], timeout: 10000 });
|
|
498
|
+
if (result.status === 0 && result.stdout) {
|
|
499
|
+
const sha = result.stdout.toString().trim();
|
|
500
|
+
return sha || null;
|
|
501
|
+
}
|
|
502
|
+
}
|
|
503
|
+
catch {
|
|
504
|
+
// fall through to null
|
|
505
|
+
}
|
|
506
|
+
return null;
|
|
507
|
+
}
|
|
508
|
+
/**
|
|
509
|
+
* Fetch check-run annotations for a commit, shaped for `detectInfraBlockedCi`.
|
|
510
|
+
*
|
|
511
|
+
* Lists the commit's check runs (per_page=100, no pagination needed for the
|
|
512
|
+
* billing-lockout case — every run carries the same annotation, so the first
|
|
513
|
+
* page is representative), then fetches annotations for each run that reports
|
|
514
|
+
* any. Used only when the whole board is failing, to recognise the "job was
|
|
515
|
+
* not started" signature (#820). Degrades to `[]` on any error (404, rate
|
|
516
|
+
* limit, revoked token) so a broken-CI situation never crashes the watch
|
|
517
|
+
* loop — `detectInfraBlockedCi` then reads that as "not infra-blocked".
|
|
518
|
+
*/
|
|
519
|
+
getCheckRunAnnotationsSync(headSha) {
|
|
520
|
+
try {
|
|
521
|
+
const listResult = spawnSync("gh", [
|
|
522
|
+
"api",
|
|
523
|
+
`repos/{owner}/{repo}/commits/${headSha}/check-runs?per_page=100`,
|
|
524
|
+
"-q",
|
|
525
|
+
".check_runs",
|
|
526
|
+
], { encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"], timeout: 15000 });
|
|
527
|
+
if (listResult.status !== 0 || !listResult.stdout)
|
|
528
|
+
return [];
|
|
529
|
+
const runs = JSON.parse(listResult.stdout);
|
|
530
|
+
if (!Array.isArray(runs))
|
|
531
|
+
return [];
|
|
532
|
+
const annotated = [];
|
|
533
|
+
for (const run of runs) {
|
|
534
|
+
const checkName = typeof run?.name === "string" ? run.name : "unknown";
|
|
535
|
+
const id = run?.id;
|
|
536
|
+
if (typeof id !== "number" && typeof id !== "string")
|
|
537
|
+
continue;
|
|
538
|
+
// Skip runs that explicitly report zero annotations; keep fetching when
|
|
539
|
+
// the count is unknown, so a missing field never hides the signature.
|
|
540
|
+
const count = run?.output?.annotations_count;
|
|
541
|
+
if (typeof count === "number" && count === 0)
|
|
542
|
+
continue;
|
|
543
|
+
const annResult = spawnSync("gh", ["api", `repos/{owner}/{repo}/check-runs/${id}/annotations`], {
|
|
544
|
+
encoding: "utf-8",
|
|
545
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
546
|
+
timeout: 15000,
|
|
547
|
+
});
|
|
548
|
+
if (annResult.status !== 0 || !annResult.stdout) {
|
|
549
|
+
annotated.push({ checkName, annotations: [] });
|
|
550
|
+
continue;
|
|
551
|
+
}
|
|
552
|
+
try {
|
|
553
|
+
const annotations = JSON.parse(annResult.stdout);
|
|
554
|
+
annotated.push({
|
|
555
|
+
checkName,
|
|
556
|
+
annotations: Array.isArray(annotations) ? annotations : [],
|
|
557
|
+
});
|
|
558
|
+
}
|
|
559
|
+
catch {
|
|
560
|
+
annotated.push({ checkName, annotations: [] });
|
|
561
|
+
}
|
|
562
|
+
}
|
|
563
|
+
return annotated;
|
|
564
|
+
}
|
|
565
|
+
catch {
|
|
566
|
+
return [];
|
|
567
|
+
}
|
|
568
|
+
}
|
|
417
569
|
// ─── Async interface methods (PlatformProvider) ────────────────────
|
|
418
570
|
async fetchIssue(id) {
|
|
419
571
|
const result = spawnSync("gh", ["issue", "view", id, "--json", "number,title,body,labels,state"], { encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"], timeout: 15000 });
|
|
@@ -130,6 +130,8 @@ export interface QACacheOptions {
|
|
|
130
130
|
defaultTtl?: number;
|
|
131
131
|
/** Enable verbose logging */
|
|
132
132
|
verbose?: boolean;
|
|
133
|
+
/** Repository directory for git operations (default: process.cwd()) */
|
|
134
|
+
cwd?: string;
|
|
133
135
|
}
|
|
134
136
|
/**
|
|
135
137
|
* Result of cache lookup
|
|
@@ -153,6 +155,7 @@ export declare class QACache {
|
|
|
153
155
|
private cacheDir;
|
|
154
156
|
private defaultTtl;
|
|
155
157
|
private verbose;
|
|
158
|
+
private cwd;
|
|
156
159
|
private cachedState;
|
|
157
160
|
constructor(options?: QACacheOptions);
|
|
158
161
|
/**
|
|
@@ -160,9 +163,11 @@ export declare class QACache {
|
|
|
160
163
|
*/
|
|
161
164
|
getCachePath(): string;
|
|
162
165
|
/**
|
|
163
|
-
* Compute hash of git diff between
|
|
166
|
+
* Compute hash of git diff between the resolved base and HEAD
|
|
164
167
|
*
|
|
165
168
|
* This provides a content-based cache key that changes when code changes.
|
|
169
|
+
* The base is resolved via resolveDiffBase so a stale local main does not
|
|
170
|
+
* leak merged-upstream content into the cache key (#890).
|
|
166
171
|
*/
|
|
167
172
|
computeDiffHash(): string;
|
|
168
173
|
/**
|
|
@@ -27,8 +27,9 @@
|
|
|
27
27
|
import * as fs from "fs";
|
|
28
28
|
import * as path from "path";
|
|
29
29
|
import * as crypto from "crypto";
|
|
30
|
-
import {
|
|
30
|
+
import { execFileSync } from "child_process";
|
|
31
31
|
import { z } from "zod";
|
|
32
|
+
import { resolveDiffBase } from "./git-diff-utils.js";
|
|
32
33
|
/**
|
|
33
34
|
* Check types that can be cached
|
|
34
35
|
*/
|
|
@@ -74,7 +75,7 @@ export const CachedCheckResultSchema = z.object({
|
|
|
74
75
|
export const QARunContextSchema = z.object({
|
|
75
76
|
/** Git HEAD SHA at the time of QA completion */
|
|
76
77
|
lastQACommitSHA: z.string(),
|
|
77
|
-
/** Diff hash (
|
|
78
|
+
/** Diff hash (resolved base...HEAD) at the time of QA */
|
|
78
79
|
lastQADiffHash: z.string(),
|
|
79
80
|
/** Map of AC ID → status from the last QA run */
|
|
80
81
|
acStatuses: z.record(z.string(), z.enum(["met", "not_met", "partially_met", "pending", "blocked"])),
|
|
@@ -139,11 +140,13 @@ export class QACache {
|
|
|
139
140
|
cacheDir;
|
|
140
141
|
defaultTtl;
|
|
141
142
|
verbose;
|
|
143
|
+
cwd;
|
|
142
144
|
cachedState = null;
|
|
143
145
|
constructor(options = {}) {
|
|
144
146
|
this.cacheDir = options.cacheDir ?? DEFAULT_CACHE_DIR;
|
|
145
147
|
this.defaultTtl = options.defaultTtl ?? DEFAULT_TTL;
|
|
146
148
|
this.verbose = options.verbose ?? false;
|
|
149
|
+
this.cwd = options.cwd ?? process.cwd();
|
|
147
150
|
}
|
|
148
151
|
/**
|
|
149
152
|
* Get the cache file path
|
|
@@ -152,14 +155,18 @@ export class QACache {
|
|
|
152
155
|
return path.join(this.cacheDir, "cache.json");
|
|
153
156
|
}
|
|
154
157
|
/**
|
|
155
|
-
* Compute hash of git diff between
|
|
158
|
+
* Compute hash of git diff between the resolved base and HEAD
|
|
156
159
|
*
|
|
157
160
|
* This provides a content-based cache key that changes when code changes.
|
|
161
|
+
* The base is resolved via resolveDiffBase so a stale local main does not
|
|
162
|
+
* leak merged-upstream content into the cache key (#890).
|
|
158
163
|
*/
|
|
159
164
|
computeDiffHash() {
|
|
160
165
|
try {
|
|
161
|
-
// Get the diff content between
|
|
162
|
-
const
|
|
166
|
+
// Get the diff content between the resolved base and HEAD
|
|
167
|
+
const base = resolveDiffBase(this.cwd, "main");
|
|
168
|
+
const diff = execFileSync("git", ["diff", `${base}...HEAD`], {
|
|
169
|
+
cwd: this.cwd,
|
|
163
170
|
encoding: "utf-8",
|
|
164
171
|
maxBuffer: 10 * 1024 * 1024, // 10MB buffer
|
|
165
172
|
});
|
|
@@ -236,7 +243,9 @@ export class QACache {
|
|
|
236
243
|
*/
|
|
237
244
|
async checkGlobalInvalidation() {
|
|
238
245
|
try {
|
|
239
|
-
const
|
|
246
|
+
const base = resolveDiffBase(this.cwd, "main");
|
|
247
|
+
const changedFiles = execFileSync("git", ["diff", `${base}...HEAD`, "--name-only"], {
|
|
248
|
+
cwd: this.cwd,
|
|
240
249
|
encoding: "utf-8",
|
|
241
250
|
})
|
|
242
251
|
.trim()
|
|
@@ -263,7 +272,9 @@ export class QACache {
|
|
|
263
272
|
return false;
|
|
264
273
|
}
|
|
265
274
|
try {
|
|
266
|
-
const
|
|
275
|
+
const base = resolveDiffBase(this.cwd, "main");
|
|
276
|
+
const changedFiles = execFileSync("git", ["diff", `${base}...HEAD`, "--name-only"], {
|
|
277
|
+
cwd: this.cwd,
|
|
267
278
|
encoding: "utf-8",
|
|
268
279
|
})
|
|
269
280
|
.trim()
|
|
@@ -432,7 +443,8 @@ export class QACache {
|
|
|
432
443
|
return null;
|
|
433
444
|
}
|
|
434
445
|
try {
|
|
435
|
-
const diff =
|
|
446
|
+
const diff = execFileSync("git", ["diff", `${sinceCommit}...HEAD`], {
|
|
447
|
+
cwd: this.cwd,
|
|
436
448
|
encoding: "utf-8",
|
|
437
449
|
maxBuffer: 10 * 1024 * 1024,
|
|
438
450
|
});
|
|
@@ -460,7 +472,8 @@ export class QACache {
|
|
|
460
472
|
return null;
|
|
461
473
|
}
|
|
462
474
|
try {
|
|
463
|
-
const output =
|
|
475
|
+
const output = execFileSync("git", ["diff", `${sinceCommit}...HEAD`, "--name-only"], {
|
|
476
|
+
cwd: this.cwd,
|
|
464
477
|
encoding: "utf-8",
|
|
465
478
|
});
|
|
466
479
|
return output.trim().split("\n").filter(Boolean);
|
|
@@ -26,6 +26,7 @@ import type { QaVerdict } from "./run-log-schema.js";
|
|
|
26
26
|
import type { ReadyPolicy } from "../settings.js";
|
|
27
27
|
import type { IssueStatus } from "./state-schema.js";
|
|
28
28
|
import { type LoopProgressSnapshot } from "./qa-stagnation.js";
|
|
29
|
+
import { type ExecChangeState } from "./phase-executor.js";
|
|
29
30
|
export type { ReadyPolicy } from "../settings.js";
|
|
30
31
|
/**
|
|
31
32
|
* Why the gate stopped. Drives `ready`, the persisted issue status, and the
|
|
@@ -44,8 +45,20 @@ export type ReadyTerminalReason =
|
|
|
44
45
|
| "LOOP_NO_DIFF"
|
|
45
46
|
/** Guard: `/loop` phase itself failed. Needs human. */
|
|
46
47
|
| "LOOP_FAILED"
|
|
47
|
-
/** #534: zero-diff exec
|
|
48
|
-
| "NO_IMPLEMENTATION"
|
|
48
|
+
/** #534: zero-diff exec worktree — nothing was built. Not ready. */
|
|
49
|
+
| "NO_IMPLEMENTATION"
|
|
50
|
+
/**
|
|
51
|
+
* #879: the worktree is dirty but has no commits. Distinct from
|
|
52
|
+
* NO_IMPLEMENTATION — work exists, but uncommitted work cannot rebase, push,
|
|
53
|
+
* or become a PR, so it is not certifiable as-is. Re-run once it is committed.
|
|
54
|
+
*/
|
|
55
|
+
| "UNCOMMITTED_ONLY"
|
|
56
|
+
/**
|
|
57
|
+
* #853: QA ran but produced no verdict (deferred its one-shot turn, or
|
|
58
|
+
* output was unparseable). Distinct from NO_IMPLEMENTATION — the
|
|
59
|
+
* implementation may be complete; it is the *review* that is missing.
|
|
60
|
+
*/
|
|
61
|
+
| "NO_VERDICT";
|
|
49
62
|
/** A single gap surfaced by QA, classified for the report. */
|
|
50
63
|
export interface ReadyGapItem {
|
|
51
64
|
/** Gap description as surfaced by QA. */
|
|
@@ -110,8 +123,8 @@ export interface RunReadyGateOptions {
|
|
|
110
123
|
onProgress?: ProgressCallback;
|
|
111
124
|
/** Injectable token reader — defaults to reading `<worktree>/.sequant`. */
|
|
112
125
|
readTokensUsed?: (worktreePath: string) => number;
|
|
113
|
-
/** Injectable change
|
|
114
|
-
|
|
126
|
+
/** Injectable change classifier — defaults to {@link classifyExecChanges}. */
|
|
127
|
+
classifyChangesFn?: (cwd: string) => ExecChangeState;
|
|
115
128
|
/** Injectable loop-progress snapshot — defaults to {@link snapshotLoopProgress}. */
|
|
116
129
|
snapshotFn?: (cwd: string) => LoopProgressSnapshot;
|
|
117
130
|
}
|
|
@@ -22,7 +22,7 @@
|
|
|
22
22
|
* lives in `src/commands/ready.ts`.
|
|
23
23
|
*/
|
|
24
24
|
import { snapshotLoopProgress, compareLoopProgress, } from "./qa-stagnation.js";
|
|
25
|
-
import {
|
|
25
|
+
import { classifyExecChanges } from "./phase-executor.js";
|
|
26
26
|
import { readTokenUsageFiles, aggregateTokenUsage, TOKEN_USAGE_DIR, } from "./token-utils.js";
|
|
27
27
|
import * as path from "path";
|
|
28
28
|
/**
|
|
@@ -180,7 +180,11 @@ export function formatReadyReport(result) {
|
|
|
180
180
|
? "✅ READY — awaiting human merge decision"
|
|
181
181
|
: result.reason === "NO_IMPLEMENTATION"
|
|
182
182
|
? "⛔ NOT READY — no implementation detected"
|
|
183
|
-
:
|
|
183
|
+
: result.reason === "UNCOMMITTED_ONLY"
|
|
184
|
+
? "⛔ NOT READY — work is uncommitted"
|
|
185
|
+
: result.reason === "NO_VERDICT"
|
|
186
|
+
? "⛔ NOT READY — QA produced no verdict"
|
|
187
|
+
: "⚠️ NOT READY — needs human intervention";
|
|
184
188
|
const reasonText = {
|
|
185
189
|
AC_MET: "Acceptance Criteria are objectively met (no AC_NOT_MET). Remaining gaps are quality-only and reported below (policy `ac` does not auto-fix them).",
|
|
186
190
|
READY_FOR_MERGE: "QA returned READY_FOR_MERGE.",
|
|
@@ -188,7 +192,9 @@ export function formatReadyReport(result) {
|
|
|
188
192
|
TOKEN_BUDGET: "Token budget exhausted before reaching the policy threshold. A human should review the remaining gaps.",
|
|
189
193
|
LOOP_NO_DIFF: "The fix loop made no diff (stagnation guard). Manual intervention required.",
|
|
190
194
|
LOOP_FAILED: "The fix loop phase failed. A human should investigate before merging.",
|
|
191
|
-
NO_IMPLEMENTATION: "Zero-diff worktree
|
|
195
|
+
NO_IMPLEMENTATION: "Zero-diff worktree — there is nothing to certify (#534 guard).",
|
|
196
|
+
UNCOMMITTED_ONLY: "The worktree has uncommitted changes but no commits (#879 guard). Uncommitted work cannot rebase, push, or become a PR — commit it, then re-run the gate.",
|
|
197
|
+
NO_VERDICT: "QA ran but produced no verdict (deferred one-shot turn or unparseable output) — there is nothing to certify (#534/#853 guard). The implementation may exist; re-run the gate once QA emits a verdict.",
|
|
192
198
|
};
|
|
193
199
|
const lines = [];
|
|
194
200
|
lines.push(`## sequant ready — Issue #${result.issueNumber}`);
|
|
@@ -231,7 +237,7 @@ export function formatReadyReport(result) {
|
|
|
231
237
|
export async function runReadyGate(opts) {
|
|
232
238
|
const { issueNumber, worktreePath, policy, maxIterations, tokenBudget, nonGoals = [], } = opts;
|
|
233
239
|
const readTokensUsed = opts.readTokensUsed ?? defaultReadTokensUsed;
|
|
234
|
-
const
|
|
240
|
+
const classifyChangesFn = opts.classifyChangesFn ?? classifyExecChanges;
|
|
235
241
|
const snapshotFn = opts.snapshotFn ?? snapshotLoopProgress;
|
|
236
242
|
// #697: run a phase while emitting live-progress events around it. `iteration`
|
|
237
243
|
// is the 1-based QA-pass index so the renderer can render `loop N/M`. Emits
|
|
@@ -296,15 +302,25 @@ export async function runReadyGate(opts) {
|
|
|
296
302
|
const qaResult = await runPhaseTracked("qa", buildPhaseConfig(opts, { fullQa: true }), iterations);
|
|
297
303
|
tokensUsed = readTokensUsed(worktreePath);
|
|
298
304
|
const verdict = qaResult.verdict ?? null;
|
|
299
|
-
// #534 guard: a null
|
|
305
|
+
// #534 guard: a null verdict is never "ready". #853: report it as
|
|
306
|
+
// NO_VERDICT, not NO_IMPLEMENTATION — a deferred/unparseable QA turn says
|
|
307
|
+
// nothing about whether an implementation exists, and the misleading label
|
|
308
|
+
// sent debuggers to the wrong place (same class as the phase-executor
|
|
309
|
+
// "unparseable verdict" split).
|
|
300
310
|
if (!verdict) {
|
|
301
|
-
return finish("
|
|
311
|
+
return finish("NO_VERDICT");
|
|
302
312
|
}
|
|
303
|
-
// #534 guard:
|
|
304
|
-
//
|
|
305
|
-
|
|
313
|
+
// #534/#879 guard: a worktree with no commits is never "ready". Split by
|
|
314
|
+
// cause so the report is not misleading: `none` is the empty-branch class
|
|
315
|
+
// (#529/#570), `uncommitted` is a dirty tree whose work cannot become a PR
|
|
316
|
+
// until committed (#879). `commits`/`unknown` continue (unknown fails open).
|
|
317
|
+
const changes = classifyChangesFn(worktreePath);
|
|
318
|
+
if (changes.kind === "none") {
|
|
306
319
|
return finish("NO_IMPLEMENTATION");
|
|
307
320
|
}
|
|
321
|
+
if (changes.kind === "uncommitted") {
|
|
322
|
+
return finish("UNCOMMITTED_ONLY");
|
|
323
|
+
}
|
|
308
324
|
finalVerdict = verdict;
|
|
309
325
|
const gaps = qaResult.summary?.gaps ?? [];
|
|
310
326
|
remaining = classifyGaps(gaps, nonGoals);
|
|
@@ -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>>;
|
|
@@ -254,6 +260,8 @@ export declare const IssueLogSchema: z.ZodObject<{
|
|
|
254
260
|
}, z.core.$strip>>;
|
|
255
261
|
}, z.core.$strip>>;
|
|
256
262
|
totalDurationSeconds: z.ZodNumber;
|
|
263
|
+
aborted: z.ZodOptional<z.ZodBoolean>;
|
|
264
|
+
abortReason: z.ZodOptional<z.ZodString>;
|
|
257
265
|
prNumber: z.ZodOptional<z.ZodNumber>;
|
|
258
266
|
prUrl: z.ZodOptional<z.ZodString>;
|
|
259
267
|
}, z.core.$strip>;
|
|
@@ -277,6 +285,8 @@ export declare const RunSummarySchema: z.ZodObject<{
|
|
|
277
285
|
totalIssues: z.ZodNumber;
|
|
278
286
|
passed: z.ZodNumber;
|
|
279
287
|
failed: z.ZodNumber;
|
|
288
|
+
partial: z.ZodDefault<z.ZodNumber>;
|
|
289
|
+
aborted: z.ZodDefault<z.ZodNumber>;
|
|
280
290
|
totalDurationSeconds: z.ZodNumber;
|
|
281
291
|
}, z.core.$strip>;
|
|
282
292
|
export type RunSummary = z.infer<typeof RunSummarySchema>;
|
|
@@ -365,6 +375,8 @@ export declare const RunLogSchema: z.ZodObject<{
|
|
|
365
375
|
api_error: "api_error";
|
|
366
376
|
hook_failure: "hook_failure";
|
|
367
377
|
build_error: "build_error";
|
|
378
|
+
rate_limit: "rate_limit";
|
|
379
|
+
billing: "billing";
|
|
368
380
|
}>;
|
|
369
381
|
errorType: z.ZodOptional<z.ZodString>;
|
|
370
382
|
errorMetadata: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
|
|
@@ -372,6 +384,8 @@ export declare const RunLogSchema: z.ZodObject<{
|
|
|
372
384
|
}, z.core.$strip>>;
|
|
373
385
|
}, z.core.$strip>>;
|
|
374
386
|
totalDurationSeconds: z.ZodNumber;
|
|
387
|
+
aborted: z.ZodOptional<z.ZodBoolean>;
|
|
388
|
+
abortReason: z.ZodOptional<z.ZodString>;
|
|
375
389
|
prNumber: z.ZodOptional<z.ZodNumber>;
|
|
376
390
|
prUrl: z.ZodOptional<z.ZodString>;
|
|
377
391
|
}, z.core.$strip>>;
|
|
@@ -379,10 +393,13 @@ export declare const RunLogSchema: z.ZodObject<{
|
|
|
379
393
|
totalIssues: z.ZodNumber;
|
|
380
394
|
passed: z.ZodNumber;
|
|
381
395
|
failed: z.ZodNumber;
|
|
396
|
+
partial: z.ZodDefault<z.ZodNumber>;
|
|
397
|
+
aborted: z.ZodDefault<z.ZodNumber>;
|
|
382
398
|
totalDurationSeconds: z.ZodNumber;
|
|
383
399
|
}, z.core.$strip>;
|
|
384
400
|
startCommit: z.ZodOptional<z.ZodString>;
|
|
385
401
|
endCommit: z.ZodOptional<z.ZodString>;
|
|
402
|
+
abortedBy: z.ZodOptional<z.ZodString>;
|
|
386
403
|
}, z.core.$strip>;
|
|
387
404
|
export type RunLog = z.infer<typeof RunLogSchema>;
|
|
388
405
|
/**
|
|
@@ -411,6 +428,7 @@ export declare function generateLogFilename(runId: string, startTime: Date): str
|
|
|
411
428
|
*/
|
|
412
429
|
export declare function createEmptyRunLog(config: RunConfig, options?: {
|
|
413
430
|
startCommit?: string;
|
|
431
|
+
startTime?: Date;
|
|
414
432
|
}): Omit<RunLog, "endTime">;
|
|
415
433
|
/**
|
|
416
434
|
* Create a phase log entry
|
|
@@ -438,4 +456,23 @@ export declare function completePhaseLog(phaseLog: Omit<PhaseLog, "endTime" | "d
|
|
|
438
456
|
*/
|
|
439
457
|
export declare function finalizeRunLog(runLog: Omit<RunLog, "endTime">, options?: {
|
|
440
458
|
endCommit?: string;
|
|
459
|
+
abortedBy?: string;
|
|
441
460
|
}): RunLog;
|
|
461
|
+
/**
|
|
462
|
+
* Derive an issue's overall log status from its phase log entries (#766).
|
|
463
|
+
*
|
|
464
|
+
* Phases are appended in execution order, so the last entry for a given phase
|
|
465
|
+
* name is its latest attempt — that attempt wins. This lets a `timeout`/
|
|
466
|
+
* `failure` that a later quality-loop iteration recovers from de-escalate to
|
|
467
|
+
* `success`, keeping the JSON log consistent with the live card and summary
|
|
468
|
+
* table (AC-3/AC-5). `loop` is auxiliary recovery and never determines the
|
|
469
|
+
* verdict (mirrors the live-card rule); an unrecovered failure still leaves a
|
|
470
|
+
* non-loop phase failed. Priority among latest attempts: failure > timeout >
|
|
471
|
+
* success.
|
|
472
|
+
*
|
|
473
|
+
* An issue with no phase entries is a `failure`, not a `success` (#856). The
|
|
474
|
+
* only way to reach that state is for `startIssue` to have run while no phase
|
|
475
|
+
* ever completed — i.e. the run was cut short. Returning `success` there is
|
|
476
|
+
* what let a SIGTERM'd run persist as a pass with an empty `phases[]`.
|
|
477
|
+
*/
|
|
478
|
+
export declare function deriveIssueLogStatus(phases: PhaseLog[]): IssueStatus;
|