sequant 2.10.0 → 2.12.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 +19 -2
- package/dist/bin/cli.js +47 -2
- 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/hooks/pre-tool.sh +331 -12
- package/dist/marketplace/external_plugins/sequant/skills/_shared/references/subagent-types.md +7 -18
- package/dist/marketplace/external_plugins/sequant/skills/assess/SKILL.md +5 -1
- package/dist/marketplace/external_plugins/sequant/skills/exec/SKILL.md +62 -8
- package/dist/marketplace/external_plugins/sequant/skills/fullsolve/SKILL.md +187 -28
- package/dist/marketplace/external_plugins/sequant/skills/loop/SKILL.md +127 -23
- package/dist/marketplace/external_plugins/sequant/skills/merger/SKILL.md +130 -13
- package/dist/marketplace/external_plugins/sequant/skills/qa/SKILL.md +306 -8
- package/dist/marketplace/external_plugins/sequant/skills/release/SKILL.md +79 -0
- package/dist/marketplace/external_plugins/sequant/skills/spec/SKILL.md +40 -20
- package/dist/marketplace/external_plugins/sequant/skills/spec/references/recommended-workflow.md +14 -1
- package/dist/marketplace/external_plugins/sequant/skills/test/SKILL.md +1 -1
- package/dist/marketplace/external_plugins/sequant/skills/testgen/SKILL.md +23 -6
- package/dist/src/commands/doctor.js +20 -18
- package/dist/src/commands/locks.d.ts +20 -1
- package/dist/src/commands/locks.js +206 -4
- package/dist/src/commands/ready.d.ts +6 -0
- package/dist/src/commands/ready.js +19 -1
- package/dist/src/commands/run-display.js +1 -0
- package/dist/src/commands/worktree.d.ts +31 -0
- package/dist/src/commands/worktree.js +95 -0
- package/dist/src/lib/ac-linter.js +26 -0
- package/dist/src/lib/ac-parser.d.ts +40 -0
- package/dist/src/lib/ac-parser.js +202 -16
- package/dist/src/lib/cli-flags.d.ts +23 -0
- package/dist/src/lib/cli-flags.js +43 -0
- package/dist/src/lib/cli-ui/run-renderer-types.d.ts +2 -0
- package/dist/src/lib/cli-ui/run-renderer.js +7 -1
- 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 +6 -3
- package/dist/src/lib/locks/index.js +4 -2
- package/dist/src/lib/locks/lock-manager.d.ts +81 -1
- package/dist/src/lib/locks/lock-manager.js +230 -5
- package/dist/src/lib/locks/types.d.ts +72 -0
- package/dist/src/lib/locks/types.js +28 -0
- package/dist/src/lib/markdown-fence.d.ts +24 -0
- package/dist/src/lib/markdown-fence.js +51 -0
- package/dist/src/lib/mcp-config.d.ts +24 -0
- package/dist/src/lib/mcp-config.js +51 -0
- package/dist/src/lib/scope/analyzer.d.ts +4 -0
- package/dist/src/lib/scope/analyzer.js +7 -1
- package/dist/src/lib/settings.d.ts +111 -1
- package/dist/src/lib/settings.js +59 -0
- package/dist/src/lib/system.d.ts +7 -3
- package/dist/src/lib/system.js +7 -3
- package/dist/src/lib/test-tautology-detector.d.ts +4 -3
- package/dist/src/lib/test-tautology-detector.js +147 -40
- package/dist/src/lib/workflow/batch-executor.d.ts +20 -1
- package/dist/src/lib/workflow/batch-executor.js +154 -23
- package/dist/src/lib/workflow/config-resolver.d.ts +25 -0
- package/dist/src/lib/workflow/config-resolver.js +90 -0
- package/dist/src/lib/workflow/drivers/agent-driver.d.ts +22 -0
- package/dist/src/lib/workflow/drivers/claude-code.js +14 -3
- 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/log-writer.d.ts +10 -1
- package/dist/src/lib/workflow/log-writer.js +20 -0
- package/dist/src/lib/workflow/metrics-schema.d.ts +49 -6
- 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/mutation-marker.d.ts +86 -0
- package/dist/src/lib/workflow/mutation-marker.js +97 -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 +17 -0
- package/dist/src/lib/workflow/phase-executor.js +60 -4
- package/dist/src/lib/workflow/qa-gaps-marker.d.ts +38 -0
- package/dist/src/lib/workflow/qa-gaps-marker.js +66 -0
- package/dist/src/lib/workflow/ready-gate.d.ts +53 -1
- package/dist/src/lib/workflow/ready-gate.js +105 -14
- package/dist/src/lib/workflow/run-log-schema.d.ts +175 -0
- package/dist/src/lib/workflow/run-log-schema.js +71 -1
- package/dist/src/lib/workflow/run-orchestrator.js +27 -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-schema.d.ts +5 -1
- package/dist/src/lib/workflow/state-schema.js +8 -1
- package/dist/src/lib/workflow/types.d.ts +78 -0
- package/dist/src/lib/workflow/worktree-manager.d.ts +8 -1
- package/dist/src/lib/workflow/worktree-manager.js +9 -1
- package/dist/src/lib/workflow/worktree-resolver.d.ts +73 -0
- package/dist/src/lib/workflow/worktree-resolver.js +126 -0
- package/package.json +4 -3
- package/templates/hooks/pre-tool.sh +331 -12
- package/templates/scripts/cleanup-worktree.sh +36 -15
- package/templates/scripts/new-feature.sh +25 -19
- package/templates/skills/_shared/references/subagent-types.md +7 -18
- package/templates/skills/assess/SKILL.md +5 -1
- package/templates/skills/exec/SKILL.md +62 -8
- package/templates/skills/fullsolve/SKILL.md +187 -28
- package/templates/skills/loop/SKILL.md +127 -23
- package/templates/skills/merger/SKILL.md +130 -13
- package/templates/skills/qa/SKILL.md +306 -8
- package/templates/skills/release/SKILL.md +79 -0
- package/templates/skills/spec/SKILL.md +40 -20
- 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/templates/agents/sequant-explorer.md +0 -24
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Parseable structured gap-finding markers for `/qa` (#937).
|
|
3
|
+
*
|
|
4
|
+
* `parseQaSummary`'s prose scrape (`parseListSection` against a
|
|
5
|
+
* `**Issues:**`/`**Gaps**` header) is lossy — it misses AC-table NOT_MET
|
|
6
|
+
* rows, §6d Adversarial Re-Read findings, and §5 Risk Assessment. QA output
|
|
7
|
+
* templates now end with a structured marker:
|
|
8
|
+
*
|
|
9
|
+
* ```
|
|
10
|
+
* <!-- SEQUANT_QA_GAPS: {"findings":[{"category":"test_gap","evidence":"...",
|
|
11
|
+
* "description":"...","recommendedAction":"fix_now"}]} -->
|
|
12
|
+
* ```
|
|
13
|
+
*
|
|
14
|
+
* Same durable-marker idiom as `SEQUANT_SPEC` (spec-recommendation.ts),
|
|
15
|
+
* `SEQUANT_PHASE` (phase-detection.ts), and `SEQUANT_MUTATION`
|
|
16
|
+
* (mutation-marker.ts) — but unlike those three, this marker's payload is
|
|
17
|
+
* an array of objects, not a flat record, so it cannot reuse their
|
|
18
|
+
* `{[^}]+}` regex (that stops at the FIRST `}`, truncating mid-JSON on any
|
|
19
|
+
* nested object). Instead this matches lazily up to the closing `-->`,
|
|
20
|
+
* which the emitting template guarantees appears on the same line as the
|
|
21
|
+
* marker (single-line JSON, no `-->` inside string values).
|
|
22
|
+
*/
|
|
23
|
+
import { type GapFinding } from "./run-log-schema.js";
|
|
24
|
+
/**
|
|
25
|
+
* Parse every `SEQUANT_QA_GAPS` marker from a QA comment/output and return
|
|
26
|
+
* the findings from the last valid one (latest-wins, matching
|
|
27
|
+
* `resolveSpecRecommendation`'s idiom for `SEQUANT_SPEC`).
|
|
28
|
+
*
|
|
29
|
+
* Markers inside fenced code blocks or inline code (e.g. a doc example
|
|
30
|
+
* showing the marker format) are ignored. Malformed JSON or a
|
|
31
|
+
* schema-invalid payload is skipped rather than thrown — an unparseable
|
|
32
|
+
* marker degrades to "no marker found", not a crash.
|
|
33
|
+
*
|
|
34
|
+
* @param output - QA comment body or agent output text
|
|
35
|
+
* @returns The findings array from the last valid marker, or `null` if no
|
|
36
|
+
* valid marker was found
|
|
37
|
+
*/
|
|
38
|
+
export declare function parseQaGapsMarker(output: string): GapFinding[] | null;
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Parseable structured gap-finding markers for `/qa` (#937).
|
|
3
|
+
*
|
|
4
|
+
* `parseQaSummary`'s prose scrape (`parseListSection` against a
|
|
5
|
+
* `**Issues:**`/`**Gaps**` header) is lossy — it misses AC-table NOT_MET
|
|
6
|
+
* rows, §6d Adversarial Re-Read findings, and §5 Risk Assessment. QA output
|
|
7
|
+
* templates now end with a structured marker:
|
|
8
|
+
*
|
|
9
|
+
* ```
|
|
10
|
+
* <!-- SEQUANT_QA_GAPS: {"findings":[{"category":"test_gap","evidence":"...",
|
|
11
|
+
* "description":"...","recommendedAction":"fix_now"}]} -->
|
|
12
|
+
* ```
|
|
13
|
+
*
|
|
14
|
+
* Same durable-marker idiom as `SEQUANT_SPEC` (spec-recommendation.ts),
|
|
15
|
+
* `SEQUANT_PHASE` (phase-detection.ts), and `SEQUANT_MUTATION`
|
|
16
|
+
* (mutation-marker.ts) — but unlike those three, this marker's payload is
|
|
17
|
+
* an array of objects, not a flat record, so it cannot reuse their
|
|
18
|
+
* `{[^}]+}` regex (that stops at the FIRST `}`, truncating mid-JSON on any
|
|
19
|
+
* nested object). Instead this matches lazily up to the closing `-->`,
|
|
20
|
+
* which the emitting template guarantees appears on the same line as the
|
|
21
|
+
* marker (single-line JSON, no `-->` inside string values).
|
|
22
|
+
*/
|
|
23
|
+
import { z } from "zod";
|
|
24
|
+
import { GapFindingSchema } from "./run-log-schema.js";
|
|
25
|
+
import { stripMarkdownCode } from "./phase-detection.js";
|
|
26
|
+
/** Regex to extract the SEQUANT_QA_GAPS marker JSON from an HTML comment. */
|
|
27
|
+
const QA_GAPS_MARKER_REGEX = /<!-- SEQUANT_QA_GAPS: (\{[\s\S]*?\}) -->/g;
|
|
28
|
+
const QaGapsMarkerPayloadSchema = z.object({
|
|
29
|
+
findings: z.array(GapFindingSchema),
|
|
30
|
+
});
|
|
31
|
+
/**
|
|
32
|
+
* Parse every `SEQUANT_QA_GAPS` marker from a QA comment/output and return
|
|
33
|
+
* the findings from the last valid one (latest-wins, matching
|
|
34
|
+
* `resolveSpecRecommendation`'s idiom for `SEQUANT_SPEC`).
|
|
35
|
+
*
|
|
36
|
+
* Markers inside fenced code blocks or inline code (e.g. a doc example
|
|
37
|
+
* showing the marker format) are ignored. Malformed JSON or a
|
|
38
|
+
* schema-invalid payload is skipped rather than thrown — an unparseable
|
|
39
|
+
* marker degrades to "no marker found", not a crash.
|
|
40
|
+
*
|
|
41
|
+
* @param output - QA comment body or agent output text
|
|
42
|
+
* @returns The findings array from the last valid marker, or `null` if no
|
|
43
|
+
* valid marker was found
|
|
44
|
+
*/
|
|
45
|
+
export function parseQaGapsMarker(output) {
|
|
46
|
+
if (!output)
|
|
47
|
+
return null;
|
|
48
|
+
const stripped = stripMarkdownCode(output);
|
|
49
|
+
QA_GAPS_MARKER_REGEX.lastIndex = 0;
|
|
50
|
+
let latest = null;
|
|
51
|
+
let match;
|
|
52
|
+
while ((match = QA_GAPS_MARKER_REGEX.exec(stripped)) !== null) {
|
|
53
|
+
let parsed;
|
|
54
|
+
try {
|
|
55
|
+
parsed = JSON.parse(match[1]);
|
|
56
|
+
}
|
|
57
|
+
catch {
|
|
58
|
+
continue;
|
|
59
|
+
}
|
|
60
|
+
const result = QaGapsMarkerPayloadSchema.safeParse(parsed);
|
|
61
|
+
if (result.success) {
|
|
62
|
+
latest = result.data.findings;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
return latest;
|
|
66
|
+
}
|
|
@@ -22,7 +22,8 @@
|
|
|
22
22
|
* lives in `src/commands/ready.ts`.
|
|
23
23
|
*/
|
|
24
24
|
import type { ExecutionConfig, PhaseResult, ProgressCallback } from "./types.js";
|
|
25
|
-
import type
|
|
25
|
+
import { type EscalationRecord } from "./effort-escalation.js";
|
|
26
|
+
import type { QaVerdict, GapCategory, GapAction } from "./run-log-schema.js";
|
|
26
27
|
import type { ReadyPolicy } from "../settings.js";
|
|
27
28
|
import type { IssueStatus } from "./state-schema.js";
|
|
28
29
|
import { type LoopProgressSnapshot } from "./qa-stagnation.js";
|
|
@@ -68,6 +69,15 @@ export interface ReadyGapItem {
|
|
|
68
69
|
* mode these are explicitly report-only (never fed to the fix loop).
|
|
69
70
|
*/
|
|
70
71
|
nonGoal: boolean;
|
|
72
|
+
/**
|
|
73
|
+
* Structured taxonomy fields from the `SEQUANT_QA_GAPS` marker (#937),
|
|
74
|
+
* present only when this gap's description matched a marker finding.
|
|
75
|
+
* A gap surfaced only via the legacy prose scrape carries none of these.
|
|
76
|
+
*/
|
|
77
|
+
category?: GapCategory;
|
|
78
|
+
evidence?: string;
|
|
79
|
+
recommendedAction?: GapAction;
|
|
80
|
+
affectedAcs?: string[];
|
|
71
81
|
}
|
|
72
82
|
/** Structured outcome of a ready-gate run. */
|
|
73
83
|
export interface ReadyResult {
|
|
@@ -90,6 +100,12 @@ export interface ReadyResult {
|
|
|
90
100
|
tokensUsed: number;
|
|
91
101
|
/** Human-readable markdown gap report (AC-4). */
|
|
92
102
|
report: string;
|
|
103
|
+
/**
|
|
104
|
+
* Effort escalations applied during this gate's QA-pass loop (#915),
|
|
105
|
+
* base+escalated tier per escalated `qa`/`loop` dispatch. Empty when
|
|
106
|
+
* `effortEscalation` is off or no dispatch escalated.
|
|
107
|
+
*/
|
|
108
|
+
effortEscalations: EscalationRecord[];
|
|
93
109
|
}
|
|
94
110
|
/**
|
|
95
111
|
* Thin phase-runner abstraction so the engine can be unit-tested without the
|
|
@@ -110,6 +126,13 @@ export interface RunReadyGateOptions {
|
|
|
110
126
|
phaseTimeout: number;
|
|
111
127
|
/** Whether MCP servers are enabled for phase execution. */
|
|
112
128
|
mcp: boolean;
|
|
129
|
+
/**
|
|
130
|
+
* Claude Desktop MCP server names explicitly opted in (#936). Callers
|
|
131
|
+
* (`commands/ready.ts`) resolve this from `settings.run.mcpAllowlist` —
|
|
132
|
+
* see the doc comment on `ExecutionConfig.mcpAllowlist` for why this
|
|
133
|
+
* producer cannot drift from `buildExecutionConfig`'s own assignment.
|
|
134
|
+
*/
|
|
135
|
+
mcpAllowlist?: string[];
|
|
113
136
|
verbose?: boolean;
|
|
114
137
|
/** Injectable phase runner — defaults to the real executePhaseWithRetry wrapper. */
|
|
115
138
|
runPhase: ReadyPhaseRunner;
|
|
@@ -127,6 +150,35 @@ export interface RunReadyGateOptions {
|
|
|
127
150
|
classifyChangesFn?: (cwd: string) => ExecChangeState;
|
|
128
151
|
/** Injectable loop-progress snapshot — defaults to {@link snapshotLoopProgress}. */
|
|
129
152
|
snapshotFn?: (cwd: string) => LoopProgressSnapshot;
|
|
153
|
+
/**
|
|
154
|
+
* Resolved per-phase `model`/`effort` overrides (#914), keyed by phase
|
|
155
|
+
* name. Callers (e.g. `commands/ready.ts`) resolve this via
|
|
156
|
+
* `resolvePhasePolicies` — the same shared resolver `buildExecutionConfig`
|
|
157
|
+
* uses — so this producer cannot drift from that one (#833 class).
|
|
158
|
+
* `buildPhaseConfig` spreads it onto every `ExecutionConfig` it builds;
|
|
159
|
+
* `phase-executor.ts` applies the entry for the phase actually running.
|
|
160
|
+
*/
|
|
161
|
+
phasePolicies?: Record<string, {
|
|
162
|
+
model?: string;
|
|
163
|
+
effort?: string;
|
|
164
|
+
}>;
|
|
165
|
+
/**
|
|
166
|
+
* Evidence-based effort escalation on quality-loop retries (#915). Callers
|
|
167
|
+
* (e.g. `commands/ready.ts`) resolve this CLI > settings > `false`, the same
|
|
168
|
+
* precedence `buildExecutionConfig` uses for the `run` path (#833 class).
|
|
169
|
+
* `buildPhaseConfig` spreads it onto every `ExecutionConfig` it builds;
|
|
170
|
+
* `withEscalatedEffort` (`effort-escalation.ts`) reads it at each QA-pass
|
|
171
|
+
* dispatch to decide whether that specific `qa`/`loop` call escalates.
|
|
172
|
+
*/
|
|
173
|
+
effortEscalation?: boolean;
|
|
174
|
+
/**
|
|
175
|
+
* Persist the final gap report as an issue comment when the gate reaches
|
|
176
|
+
* a terminal state with a QA verdict (#937 AC-4) — callers wire this to
|
|
177
|
+
* `GitHubProvider.postComment`. Best-effort: a failure here is caught and
|
|
178
|
+
* swallowed, never failing the gate itself — `result.report` (returned to
|
|
179
|
+
* the caller either way) is the primary channel.
|
|
180
|
+
*/
|
|
181
|
+
postReport?: (body: string) => Promise<void>;
|
|
130
182
|
}
|
|
131
183
|
/**
|
|
132
184
|
* Pure exit predicate. Given a policy and a QA verdict, has the loop reached
|
|
@@ -21,6 +21,7 @@
|
|
|
21
21
|
* (out of scope for #683) can reuse `runReadyGate` directly. The command shell
|
|
22
22
|
* lives in `src/commands/ready.ts`.
|
|
23
23
|
*/
|
|
24
|
+
import { withEscalatedEffort, } from "./effort-escalation.js";
|
|
24
25
|
import { snapshotLoopProgress, compareLoopProgress, } from "./qa-stagnation.js";
|
|
25
26
|
import { classifyExecChanges } from "./phase-executor.js";
|
|
26
27
|
import { readTokenUsageFiles, aggregateTokenUsage, TOKEN_USAGE_DIR, } from "./token-utils.js";
|
|
@@ -137,11 +138,28 @@ export function parseNonGoals(issueBody) {
|
|
|
137
138
|
}
|
|
138
139
|
return items;
|
|
139
140
|
}
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
141
|
+
/**
|
|
142
|
+
* Classify each gap description for the report, enriching with structured
|
|
143
|
+
* taxonomy fields (#937) when the gap matches a `SEQUANT_QA_GAPS` marker
|
|
144
|
+
* finding. `findings` are matched to `gaps` by description (trimmed,
|
|
145
|
+
* case-folded) since `parseQaSummary` already unions marker findings into
|
|
146
|
+
* `gaps` in document order — every marker finding's description is present.
|
|
147
|
+
*/
|
|
148
|
+
function classifyGaps(gaps, nonGoals, findings) {
|
|
149
|
+
const byDescription = new Map((findings ?? []).map((f) => [f.description.trim().toLowerCase(), f]));
|
|
150
|
+
return gaps.map((g) => {
|
|
151
|
+
const finding = byDescription.get(g.trim().toLowerCase());
|
|
152
|
+
return {
|
|
153
|
+
description: g,
|
|
154
|
+
nonGoal: finding?.nonGoal ?? gapTouchesNonGoals(g, nonGoals),
|
|
155
|
+
...(finding && {
|
|
156
|
+
category: finding.category,
|
|
157
|
+
evidence: finding.evidence,
|
|
158
|
+
recommendedAction: finding.recommendedAction,
|
|
159
|
+
...(finding.affectedAcs && { affectedAcs: finding.affectedAcs }),
|
|
160
|
+
}),
|
|
161
|
+
};
|
|
162
|
+
});
|
|
145
163
|
}
|
|
146
164
|
function defaultReadTokensUsed(worktreePath) {
|
|
147
165
|
const dir = path.join(worktreePath, TOKEN_USAGE_DIR);
|
|
@@ -166,7 +184,13 @@ function buildPhaseConfig(opts, extra) {
|
|
|
166
184
|
noSmartTests: false,
|
|
167
185
|
dryRun: false,
|
|
168
186
|
mcp: opts.mcp,
|
|
187
|
+
mcpAllowlist: opts.mcpAllowlist,
|
|
169
188
|
retry: true,
|
|
189
|
+
// #914: producer 2 (see the doc comment on RunReadyGateOptions.phasePolicies
|
|
190
|
+
// for why this can't drift from buildExecutionConfig's own assignment).
|
|
191
|
+
phasePolicies: opts.phasePolicies,
|
|
192
|
+
// #915: producer 2 (see RunReadyGateOptions.effortEscalation).
|
|
193
|
+
effortEscalation: opts.effortEscalation,
|
|
170
194
|
...extra,
|
|
171
195
|
};
|
|
172
196
|
}
|
|
@@ -223,14 +247,44 @@ export function formatReadyReport(result) {
|
|
|
223
247
|
}
|
|
224
248
|
else {
|
|
225
249
|
for (const item of result.remaining) {
|
|
226
|
-
const
|
|
227
|
-
|
|
250
|
+
const tags = [
|
|
251
|
+
item.category && item.recommendedAction
|
|
252
|
+
? `\`${item.category} · ${item.recommendedAction}\``
|
|
253
|
+
: undefined,
|
|
254
|
+
item.nonGoal ? "_(Non-Goal — report-only)_" : undefined,
|
|
255
|
+
].filter(Boolean);
|
|
256
|
+
const suffix = tags.length > 0 ? ` ${tags.join(" ")}` : "";
|
|
257
|
+
lines.push(`- ${item.description}${suffix}`);
|
|
228
258
|
}
|
|
229
259
|
}
|
|
230
260
|
lines.push("");
|
|
231
261
|
lines.push("> The human merge gate is intentional: `sequant ready` never merges. Review the gaps above, then merge manually when satisfied.");
|
|
262
|
+
lines.push("");
|
|
263
|
+
lines.push(formatReadyGapsMarker(result.remaining));
|
|
232
264
|
return lines.join("\n");
|
|
233
265
|
}
|
|
266
|
+
/**
|
|
267
|
+
* Render `remaining` as a `SEQUANT_QA_GAPS` marker so the persisted ready
|
|
268
|
+
* report (#937 AC-4) carries the same machine-readable channel `/qa` itself
|
|
269
|
+
* emits — only items that came from a structured finding (have a
|
|
270
|
+
* `category`) round-trip; legacy prose-only gaps are already in the prose
|
|
271
|
+
* list above and are not re-encoded here.
|
|
272
|
+
*/
|
|
273
|
+
function formatReadyGapsMarker(remaining) {
|
|
274
|
+
const findings = remaining
|
|
275
|
+
.filter((g) => g.category !== undefined &&
|
|
276
|
+
g.evidence !== undefined &&
|
|
277
|
+
g.recommendedAction !== undefined)
|
|
278
|
+
.map((g) => ({
|
|
279
|
+
category: g.category,
|
|
280
|
+
evidence: g.evidence,
|
|
281
|
+
description: g.description,
|
|
282
|
+
recommendedAction: g.recommendedAction,
|
|
283
|
+
...(g.affectedAcs && { affectedAcs: g.affectedAcs }),
|
|
284
|
+
...(g.nonGoal && { nonGoal: g.nonGoal }),
|
|
285
|
+
}));
|
|
286
|
+
return `<!-- SEQUANT_QA_GAPS: ${JSON.stringify({ findings })} -->`;
|
|
287
|
+
}
|
|
234
288
|
/**
|
|
235
289
|
* Drive the policy-bounded `qa → loop → qa` ready gate.
|
|
236
290
|
*/
|
|
@@ -268,7 +322,10 @@ export async function runReadyGate(opts) {
|
|
|
268
322
|
const autoFixed = [];
|
|
269
323
|
let remaining = [];
|
|
270
324
|
let tokensUsed = 0;
|
|
271
|
-
|
|
325
|
+
// #915: escalated (base, escalated) tiers, one entry per QA-pass dispatch
|
|
326
|
+
// that actually escalated. Populated at the two dispatch sites below.
|
|
327
|
+
const effortEscalations = [];
|
|
328
|
+
const finish = async (reason) => {
|
|
272
329
|
const ready = reason === "AC_MET" || reason === "READY_FOR_MERGE";
|
|
273
330
|
const issueStatus = ready
|
|
274
331
|
? "waiting_for_human_merge"
|
|
@@ -285,8 +342,25 @@ export async function runReadyGate(opts) {
|
|
|
285
342
|
remaining,
|
|
286
343
|
tokensUsed,
|
|
287
344
|
report: "",
|
|
345
|
+
effortEscalations,
|
|
288
346
|
};
|
|
289
347
|
result.report = formatReadyReport(result);
|
|
348
|
+
// #937 AC-4: persist the gap report as an issue comment once the gate
|
|
349
|
+
// reaches a terminal state carrying a QA verdict. `finalVerdict === null`
|
|
350
|
+
// covers NO_IMPLEMENTATION/UNCOMMITTED_ONLY/NO_VERDICT and the guard
|
|
351
|
+
// TOKEN_BUDGET check before the first QA pass — none of those have a gap
|
|
352
|
+
// report worth persisting. Best-effort: a post failure must never fail
|
|
353
|
+
// the gate — `result.report` (returned either way) is the primary
|
|
354
|
+
// channel, same rationale as `sequant ready`'s existing state-persistence
|
|
355
|
+
// try/catch.
|
|
356
|
+
if (result.finalVerdict !== null && opts.postReport) {
|
|
357
|
+
try {
|
|
358
|
+
await opts.postReport(result.report);
|
|
359
|
+
}
|
|
360
|
+
catch {
|
|
361
|
+
// Non-fatal — see comment above.
|
|
362
|
+
}
|
|
363
|
+
}
|
|
290
364
|
return result;
|
|
291
365
|
};
|
|
292
366
|
const budgetExceeded = () => typeof tokenBudget === "number" &&
|
|
@@ -299,7 +373,12 @@ export async function runReadyGate(opts) {
|
|
|
299
373
|
return finish("TOKEN_BUDGET");
|
|
300
374
|
}
|
|
301
375
|
iterations++;
|
|
302
|
-
|
|
376
|
+
// #915: iterations > 1 means this QA pass is a retry of a prior
|
|
377
|
+
// unsatisfied verdict — the ready-gate's retry signal.
|
|
378
|
+
const qaEscalation = withEscalatedEffort(buildPhaseConfig(opts, { fullQa: true }), "qa", iterations > 1);
|
|
379
|
+
if (qaEscalation.record)
|
|
380
|
+
effortEscalations.push(qaEscalation.record);
|
|
381
|
+
const qaResult = await runPhaseTracked("qa", qaEscalation.config, iterations);
|
|
303
382
|
tokensUsed = readTokensUsed(worktreePath);
|
|
304
383
|
const verdict = qaResult.verdict ?? null;
|
|
305
384
|
// #534 guard: a null verdict is never "ready". #853: report it as
|
|
@@ -323,7 +402,7 @@ export async function runReadyGate(opts) {
|
|
|
323
402
|
}
|
|
324
403
|
finalVerdict = verdict;
|
|
325
404
|
const gaps = qaResult.summary?.gaps ?? [];
|
|
326
|
-
remaining = classifyGaps(gaps, nonGoals);
|
|
405
|
+
remaining = classifyGaps(gaps, nonGoals, qaResult.summary?.findings);
|
|
327
406
|
// Policy threshold reached → stop at the human merge gate.
|
|
328
407
|
if (isAtThreshold(policy, verdict)) {
|
|
329
408
|
return finish(verdict === "READY_FOR_MERGE" ? "READY_FOR_MERGE" : "AC_MET");
|
|
@@ -338,16 +417,28 @@ export async function runReadyGate(opts) {
|
|
|
338
417
|
// Run one fix loop. In `ac` mode we only reach here on AC_NOT_MET, so the
|
|
339
418
|
// gaps are AC gaps — feeding them via failedAcs keeps the loop scoped to
|
|
340
419
|
// the AC boundary (quality gaps are never fixed under `ac`). Non-Goal-
|
|
341
|
-
// touching findings are excluded from what we ask the loop to fix
|
|
420
|
+
// touching findings are excluded from what we ask the loop to fix, as are
|
|
421
|
+
// `SEQUANT_QA_GAPS` findings explicitly marked `document` or
|
|
422
|
+
// `pause_for_human` (#937 AC-3) — a gap with no `recommendedAction` (the
|
|
423
|
+
// legacy prose-only path) is still treated as fixable, unchanged from
|
|
424
|
+
// pre-#937 behavior.
|
|
342
425
|
const fixableGaps = remaining
|
|
343
|
-
.filter((g) => !g.nonGoal
|
|
426
|
+
.filter((g) => !g.nonGoal &&
|
|
427
|
+
g.recommendedAction !== "document" &&
|
|
428
|
+
g.recommendedAction !== "pause_for_human")
|
|
344
429
|
.map((g) => g.description);
|
|
345
430
|
const before = snapshotFn(worktreePath);
|
|
346
|
-
|
|
431
|
+
// #915: iterations > 1 means this fix pass follows a QA pass that was
|
|
432
|
+
// itself a retry — the same ready-gate retry signal as the qa dispatch
|
|
433
|
+
// above.
|
|
434
|
+
const loopEscalation = withEscalatedEffort(buildPhaseConfig(opts, {
|
|
347
435
|
lastVerdict: verdict,
|
|
348
436
|
failedAcs: fixableGaps.join("; ") || undefined,
|
|
349
437
|
promptContext: buildLoopContext(policy, verdict, fixableGaps),
|
|
350
|
-
}), iterations);
|
|
438
|
+
}), "loop", iterations > 1);
|
|
439
|
+
if (loopEscalation.record)
|
|
440
|
+
effortEscalations.push(loopEscalation.record);
|
|
441
|
+
const loopResult = await runPhaseTracked("loop", loopEscalation.config, iterations);
|
|
351
442
|
tokensUsed = readTokensUsed(worktreePath);
|
|
352
443
|
if (!loopResult.success) {
|
|
353
444
|
return finish("LOOP_FAILED");
|
|
@@ -48,6 +48,37 @@ export declare const QaVerdictSchema: z.ZodEnum<{
|
|
|
48
48
|
NEEDS_VERIFICATION: "NEEDS_VERIFICATION";
|
|
49
49
|
}>;
|
|
50
50
|
export type QaVerdict = z.infer<typeof QaVerdictSchema>;
|
|
51
|
+
/**
|
|
52
|
+
* Source that produced the resolved spec→run phase recommendation (#921).
|
|
53
|
+
*
|
|
54
|
+
* Ordered by resolution priority: a durable structured marker in the spec's
|
|
55
|
+
* GitHub comment beats the same comment's prose section, which beats the
|
|
56
|
+
* spec agent's ephemeral chat text, which beats label-based guessing.
|
|
57
|
+
*/
|
|
58
|
+
export declare const SpecRecommendationSourceSchema: z.ZodEnum<{
|
|
59
|
+
marker: "marker";
|
|
60
|
+
"comment-prose": "comment-prose";
|
|
61
|
+
chat: "chat";
|
|
62
|
+
"label-fallback": "label-fallback";
|
|
63
|
+
}>;
|
|
64
|
+
export type SpecRecommendationSource = z.infer<typeof SpecRecommendationSourceSchema>;
|
|
65
|
+
/**
|
|
66
|
+
* Resolved spec→run phase recommendation, recorded on the issue log so
|
|
67
|
+
* fallback frequency is auditable (#921 AC-4). Additive/optional — absent on
|
|
68
|
+
* runs that never reached spec resolution (e.g. spec failed) or predate this
|
|
69
|
+
* field, keeping the persisted-log schema stable at `version: 1`.
|
|
70
|
+
*/
|
|
71
|
+
export declare const SpecRecommendationSchema: z.ZodObject<{
|
|
72
|
+
source: z.ZodEnum<{
|
|
73
|
+
marker: "marker";
|
|
74
|
+
"comment-prose": "comment-prose";
|
|
75
|
+
chat: "chat";
|
|
76
|
+
"label-fallback": "label-fallback";
|
|
77
|
+
}>;
|
|
78
|
+
phases: z.ZodArray<z.ZodString>;
|
|
79
|
+
qualityLoop: z.ZodBoolean;
|
|
80
|
+
}, z.core.$strip>;
|
|
81
|
+
export type SpecRecommendation = z.infer<typeof SpecRecommendationSchema>;
|
|
51
82
|
/**
|
|
52
83
|
* File diff statistics for a single file (AC-3)
|
|
53
84
|
*/
|
|
@@ -91,12 +122,57 @@ export declare const ErrorContextSchema: z.ZodObject<{
|
|
|
91
122
|
build_error: "build_error";
|
|
92
123
|
rate_limit: "rate_limit";
|
|
93
124
|
billing: "billing";
|
|
125
|
+
pr_creation: "pr_creation";
|
|
94
126
|
}>;
|
|
95
127
|
errorType: z.ZodOptional<z.ZodString>;
|
|
96
128
|
errorMetadata: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
|
|
97
129
|
isRetryable: z.ZodOptional<z.ZodBoolean>;
|
|
98
130
|
}, z.core.$strip>;
|
|
99
131
|
export type ErrorContext = z.infer<typeof ErrorContextSchema>;
|
|
132
|
+
/**
|
|
133
|
+
* Finite taxonomy for a single gap finding surfaced by `/qa` (#937).
|
|
134
|
+
*
|
|
135
|
+
* Replaces the lossy prose-scrape gap channel (`parseListSection` against
|
|
136
|
+
* `**Issues:**`/`**Gaps**` headers, which misses AC-table NOT_MET rows, §6d
|
|
137
|
+
* Adversarial Re-Read findings, and §5 Risk Assessment). `evidence` is
|
|
138
|
+
* required so a finding can't be speculative — see #608's 0%-action-rate
|
|
139
|
+
* result for open-ended "what might we be missing" findings.
|
|
140
|
+
*/
|
|
141
|
+
export declare const GapCategorySchema: z.ZodEnum<{
|
|
142
|
+
requirement_gap: "requirement_gap";
|
|
143
|
+
dependency_gap: "dependency_gap";
|
|
144
|
+
test_gap: "test_gap";
|
|
145
|
+
repository_gap: "repository_gap";
|
|
146
|
+
risk_gap: "risk_gap";
|
|
147
|
+
execution_gap: "execution_gap";
|
|
148
|
+
}>;
|
|
149
|
+
export type GapCategory = z.infer<typeof GapCategorySchema>;
|
|
150
|
+
export declare const GapActionSchema: z.ZodEnum<{
|
|
151
|
+
fix_now: "fix_now";
|
|
152
|
+
document: "document";
|
|
153
|
+
pause_for_human: "pause_for_human";
|
|
154
|
+
}>;
|
|
155
|
+
export type GapAction = z.infer<typeof GapActionSchema>;
|
|
156
|
+
export declare const GapFindingSchema: z.ZodObject<{
|
|
157
|
+
category: z.ZodEnum<{
|
|
158
|
+
requirement_gap: "requirement_gap";
|
|
159
|
+
dependency_gap: "dependency_gap";
|
|
160
|
+
test_gap: "test_gap";
|
|
161
|
+
repository_gap: "repository_gap";
|
|
162
|
+
risk_gap: "risk_gap";
|
|
163
|
+
execution_gap: "execution_gap";
|
|
164
|
+
}>;
|
|
165
|
+
evidence: z.ZodString;
|
|
166
|
+
description: z.ZodString;
|
|
167
|
+
recommendedAction: z.ZodEnum<{
|
|
168
|
+
fix_now: "fix_now";
|
|
169
|
+
document: "document";
|
|
170
|
+
pause_for_human: "pause_for_human";
|
|
171
|
+
}>;
|
|
172
|
+
affectedAcs: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
173
|
+
nonGoal: z.ZodOptional<z.ZodBoolean>;
|
|
174
|
+
}, z.core.$strip>;
|
|
175
|
+
export type GapFinding = z.infer<typeof GapFindingSchema>;
|
|
100
176
|
/**
|
|
101
177
|
* Condensed QA verdict summary for structured log output (#434).
|
|
102
178
|
*
|
|
@@ -109,6 +185,25 @@ export declare const QaSummarySchema: z.ZodObject<{
|
|
|
109
185
|
acTotal: z.ZodNumber;
|
|
110
186
|
gaps: z.ZodArray<z.ZodString>;
|
|
111
187
|
suggestions: z.ZodArray<z.ZodString>;
|
|
188
|
+
findings: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
189
|
+
category: z.ZodEnum<{
|
|
190
|
+
requirement_gap: "requirement_gap";
|
|
191
|
+
dependency_gap: "dependency_gap";
|
|
192
|
+
test_gap: "test_gap";
|
|
193
|
+
repository_gap: "repository_gap";
|
|
194
|
+
risk_gap: "risk_gap";
|
|
195
|
+
execution_gap: "execution_gap";
|
|
196
|
+
}>;
|
|
197
|
+
evidence: z.ZodString;
|
|
198
|
+
description: z.ZodString;
|
|
199
|
+
recommendedAction: z.ZodEnum<{
|
|
200
|
+
fix_now: "fix_now";
|
|
201
|
+
document: "document";
|
|
202
|
+
pause_for_human: "pause_for_human";
|
|
203
|
+
}>;
|
|
204
|
+
affectedAcs: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
205
|
+
nonGoal: z.ZodOptional<z.ZodBoolean>;
|
|
206
|
+
}, z.core.$strip>>>;
|
|
112
207
|
}, z.core.$strip>;
|
|
113
208
|
export type QaSummary = z.infer<typeof QaSummarySchema>;
|
|
114
209
|
/**
|
|
@@ -143,6 +238,25 @@ export declare const PhaseLogSchema: z.ZodObject<{
|
|
|
143
238
|
acTotal: z.ZodNumber;
|
|
144
239
|
gaps: z.ZodArray<z.ZodString>;
|
|
145
240
|
suggestions: z.ZodArray<z.ZodString>;
|
|
241
|
+
findings: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
242
|
+
category: z.ZodEnum<{
|
|
243
|
+
requirement_gap: "requirement_gap";
|
|
244
|
+
dependency_gap: "dependency_gap";
|
|
245
|
+
test_gap: "test_gap";
|
|
246
|
+
repository_gap: "repository_gap";
|
|
247
|
+
risk_gap: "risk_gap";
|
|
248
|
+
execution_gap: "execution_gap";
|
|
249
|
+
}>;
|
|
250
|
+
evidence: z.ZodString;
|
|
251
|
+
description: z.ZodString;
|
|
252
|
+
recommendedAction: z.ZodEnum<{
|
|
253
|
+
fix_now: "fix_now";
|
|
254
|
+
document: "document";
|
|
255
|
+
pause_for_human: "pause_for_human";
|
|
256
|
+
}>;
|
|
257
|
+
affectedAcs: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
258
|
+
nonGoal: z.ZodOptional<z.ZodBoolean>;
|
|
259
|
+
}, z.core.$strip>>>;
|
|
146
260
|
}, z.core.$strip>>;
|
|
147
261
|
commitHash: z.ZodOptional<z.ZodString>;
|
|
148
262
|
fileDiffStats: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
@@ -174,6 +288,7 @@ export declare const PhaseLogSchema: z.ZodObject<{
|
|
|
174
288
|
build_error: "build_error";
|
|
175
289
|
rate_limit: "rate_limit";
|
|
176
290
|
billing: "billing";
|
|
291
|
+
pr_creation: "pr_creation";
|
|
177
292
|
}>;
|
|
178
293
|
errorType: z.ZodOptional<z.ZodString>;
|
|
179
294
|
errorMetadata: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
|
|
@@ -222,6 +337,25 @@ export declare const IssueLogSchema: z.ZodObject<{
|
|
|
222
337
|
acTotal: z.ZodNumber;
|
|
223
338
|
gaps: z.ZodArray<z.ZodString>;
|
|
224
339
|
suggestions: z.ZodArray<z.ZodString>;
|
|
340
|
+
findings: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
341
|
+
category: z.ZodEnum<{
|
|
342
|
+
requirement_gap: "requirement_gap";
|
|
343
|
+
dependency_gap: "dependency_gap";
|
|
344
|
+
test_gap: "test_gap";
|
|
345
|
+
repository_gap: "repository_gap";
|
|
346
|
+
risk_gap: "risk_gap";
|
|
347
|
+
execution_gap: "execution_gap";
|
|
348
|
+
}>;
|
|
349
|
+
evidence: z.ZodString;
|
|
350
|
+
description: z.ZodString;
|
|
351
|
+
recommendedAction: z.ZodEnum<{
|
|
352
|
+
fix_now: "fix_now";
|
|
353
|
+
document: "document";
|
|
354
|
+
pause_for_human: "pause_for_human";
|
|
355
|
+
}>;
|
|
356
|
+
affectedAcs: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
357
|
+
nonGoal: z.ZodOptional<z.ZodBoolean>;
|
|
358
|
+
}, z.core.$strip>>>;
|
|
225
359
|
}, z.core.$strip>>;
|
|
226
360
|
commitHash: z.ZodOptional<z.ZodString>;
|
|
227
361
|
fileDiffStats: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
@@ -253,6 +387,7 @@ export declare const IssueLogSchema: z.ZodObject<{
|
|
|
253
387
|
build_error: "build_error";
|
|
254
388
|
rate_limit: "rate_limit";
|
|
255
389
|
billing: "billing";
|
|
390
|
+
pr_creation: "pr_creation";
|
|
256
391
|
}>;
|
|
257
392
|
errorType: z.ZodOptional<z.ZodString>;
|
|
258
393
|
errorMetadata: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
|
|
@@ -264,6 +399,16 @@ export declare const IssueLogSchema: z.ZodObject<{
|
|
|
264
399
|
abortReason: z.ZodOptional<z.ZodString>;
|
|
265
400
|
prNumber: z.ZodOptional<z.ZodNumber>;
|
|
266
401
|
prUrl: z.ZodOptional<z.ZodString>;
|
|
402
|
+
specRecommendation: z.ZodOptional<z.ZodObject<{
|
|
403
|
+
source: z.ZodEnum<{
|
|
404
|
+
marker: "marker";
|
|
405
|
+
"comment-prose": "comment-prose";
|
|
406
|
+
chat: "chat";
|
|
407
|
+
"label-fallback": "label-fallback";
|
|
408
|
+
}>;
|
|
409
|
+
phases: z.ZodArray<z.ZodString>;
|
|
410
|
+
qualityLoop: z.ZodBoolean;
|
|
411
|
+
}, z.core.$strip>>;
|
|
267
412
|
}, z.core.$strip>;
|
|
268
413
|
export type IssueLog = z.infer<typeof IssueLogSchema>;
|
|
269
414
|
/**
|
|
@@ -346,6 +491,25 @@ export declare const RunLogSchema: z.ZodObject<{
|
|
|
346
491
|
acTotal: z.ZodNumber;
|
|
347
492
|
gaps: z.ZodArray<z.ZodString>;
|
|
348
493
|
suggestions: z.ZodArray<z.ZodString>;
|
|
494
|
+
findings: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
495
|
+
category: z.ZodEnum<{
|
|
496
|
+
requirement_gap: "requirement_gap";
|
|
497
|
+
dependency_gap: "dependency_gap";
|
|
498
|
+
test_gap: "test_gap";
|
|
499
|
+
repository_gap: "repository_gap";
|
|
500
|
+
risk_gap: "risk_gap";
|
|
501
|
+
execution_gap: "execution_gap";
|
|
502
|
+
}>;
|
|
503
|
+
evidence: z.ZodString;
|
|
504
|
+
description: z.ZodString;
|
|
505
|
+
recommendedAction: z.ZodEnum<{
|
|
506
|
+
fix_now: "fix_now";
|
|
507
|
+
document: "document";
|
|
508
|
+
pause_for_human: "pause_for_human";
|
|
509
|
+
}>;
|
|
510
|
+
affectedAcs: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
511
|
+
nonGoal: z.ZodOptional<z.ZodBoolean>;
|
|
512
|
+
}, z.core.$strip>>>;
|
|
349
513
|
}, z.core.$strip>>;
|
|
350
514
|
commitHash: z.ZodOptional<z.ZodString>;
|
|
351
515
|
fileDiffStats: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
@@ -377,6 +541,7 @@ export declare const RunLogSchema: z.ZodObject<{
|
|
|
377
541
|
build_error: "build_error";
|
|
378
542
|
rate_limit: "rate_limit";
|
|
379
543
|
billing: "billing";
|
|
544
|
+
pr_creation: "pr_creation";
|
|
380
545
|
}>;
|
|
381
546
|
errorType: z.ZodOptional<z.ZodString>;
|
|
382
547
|
errorMetadata: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
|
|
@@ -388,6 +553,16 @@ export declare const RunLogSchema: z.ZodObject<{
|
|
|
388
553
|
abortReason: z.ZodOptional<z.ZodString>;
|
|
389
554
|
prNumber: z.ZodOptional<z.ZodNumber>;
|
|
390
555
|
prUrl: z.ZodOptional<z.ZodString>;
|
|
556
|
+
specRecommendation: z.ZodOptional<z.ZodObject<{
|
|
557
|
+
source: z.ZodEnum<{
|
|
558
|
+
marker: "marker";
|
|
559
|
+
"comment-prose": "comment-prose";
|
|
560
|
+
chat: "chat";
|
|
561
|
+
"label-fallback": "label-fallback";
|
|
562
|
+
}>;
|
|
563
|
+
phases: z.ZodArray<z.ZodString>;
|
|
564
|
+
qualityLoop: z.ZodBoolean;
|
|
565
|
+
}, z.core.$strip>>;
|
|
391
566
|
}, z.core.$strip>>;
|
|
392
567
|
summary: z.ZodObject<{
|
|
393
568
|
totalIssues: z.ZodNumber;
|