@yagni-app/code-staging 1.2.6-staging.1952.1 → 1.2.6-staging.1956.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/extension/branding.d.ts +20 -0
- package/dist/extension/branding.js +49 -8
- package/dist/extension/index.js +5 -0
- package/dist/extension/pipeline/changeScan.d.ts +62 -0
- package/dist/extension/pipeline/changeScan.js +307 -0
- package/dist/extension/pipeline/cloud.d.ts +205 -0
- package/dist/extension/pipeline/cloud.js +382 -0
- package/dist/extension/pipeline/continuation.d.ts +7 -3
- package/dist/extension/pipeline/continuation.js +29 -13
- package/dist/extension/pipeline/findings.d.ts +9 -4
- package/dist/extension/pipeline/findings.js +25 -10
- package/dist/extension/pipeline/finish.d.ts +11 -1
- package/dist/extension/pipeline/finish.js +51 -5
- package/dist/extension/pipeline/headlessGo.d.ts +20 -1
- package/dist/extension/pipeline/headlessGo.js +205 -33
- package/dist/extension/pipeline/orchestrator.d.ts +8 -2
- package/dist/extension/pipeline/orchestrator.js +249 -68
- package/dist/extension/pipeline/personas.d.ts +8 -3
- package/dist/extension/pipeline/personas.js +42 -7
- package/dist/extension/pipeline/planOutput.d.ts +42 -0
- package/dist/extension/pipeline/planOutput.js +94 -0
- package/dist/extension/pipeline/stages.d.ts +34 -5
- package/dist/extension/pipeline/stages.js +136 -7
- package/dist/extension/pipeline/tierCap.d.ts +22 -1
- package/dist/extension/pipeline/tierCap.js +48 -1
- package/dist/extension/pipeline/types.d.ts +31 -0
- package/dist/extension/pipeline/types.js +2 -0
- package/dist/extension/pipeline/verify.d.ts +33 -0
- package/dist/extension/pipeline/verify.js +101 -9
- package/package.json +2 -2
|
@@ -34,16 +34,24 @@ export function parseContinuation(raw) {
|
|
|
34
34
|
const obj = data;
|
|
35
35
|
if (obj.version !== 1)
|
|
36
36
|
return { ok: false, reason: `unknown version ${JSON.stringify(obj.version)}` };
|
|
37
|
-
|
|
37
|
+
// The cloud lane's `continue.json` names the same facts `stage` and
|
|
38
|
+
// `roundsDone`; either spelling reads the same.
|
|
39
|
+
const stage = "stage" in obj ? obj.stage : obj.stageAtStop;
|
|
38
40
|
if (stage !== null && !CONTINUATION_STAGES.includes(stage)) {
|
|
39
41
|
return { ok: false, reason: `unknown stageAtStop ${JSON.stringify(stage)}` };
|
|
40
42
|
}
|
|
41
|
-
const rounds = obj.reviewRoundsDone;
|
|
43
|
+
const rounds = "roundsDone" in obj ? obj.roundsDone : obj.reviewRoundsDone;
|
|
42
44
|
if (typeof rounds !== "number" || !Number.isInteger(rounds) || rounds < 0) {
|
|
43
|
-
return {
|
|
45
|
+
return {
|
|
46
|
+
ok: false,
|
|
47
|
+
reason: `reviewRoundsDone is not a whole number (${JSON.stringify(rounds)})`,
|
|
48
|
+
};
|
|
44
49
|
}
|
|
45
50
|
const priorStop = typeof obj.priorStop === "string" ? obj.priorStop.trim() : "";
|
|
46
|
-
return {
|
|
51
|
+
return {
|
|
52
|
+
ok: true,
|
|
53
|
+
value: { stageAtStop: stage, reviewRoundsDone: rounds, priorStop },
|
|
54
|
+
};
|
|
47
55
|
}
|
|
48
56
|
/** True when the earlier attempt got past implement, so the run re-enters the review loop. */
|
|
49
57
|
export function continuesInLoop(continuation) {
|
|
@@ -56,27 +64,35 @@ export function continuesInLoop(continuation) {
|
|
|
56
64
|
* continuation has no summary, so it says where the change is instead and pins
|
|
57
65
|
* the same design context.
|
|
58
66
|
*/
|
|
59
|
-
export function continuationReviewInput(continuation, mission) {
|
|
67
|
+
export function continuationReviewInput(continuation, mission, committed = false) {
|
|
60
68
|
const why = continuation.priorStop ? ` (${continuation.priorStop})` : "";
|
|
61
|
-
const
|
|
62
|
-
|
|
63
|
-
|
|
69
|
+
const stopped = `this build continues an earlier attempt that stopped during its ${continuation.stageAtStop ?? "unknown"} stage${why}. `;
|
|
70
|
+
// A cloud execution starts from the attempt's pushed branch: the earlier
|
|
71
|
+
// work is in its commits, not in the working tree.
|
|
72
|
+
const summary = committed
|
|
73
|
+
? `No implement summary: ${stopped}` +
|
|
74
|
+
"That attempt's change is committed on this branch. Review the whole change against the base commit the ticket names " +
|
|
75
|
+
"(`git diff <base>...HEAD`), plus anything `git status` lists."
|
|
76
|
+
: `No implement summary: ${stopped}` +
|
|
77
|
+
"That attempt's change is already in the working tree as uncommitted edits on top of HEAD. " +
|
|
78
|
+
"Review the whole change against HEAD (`git diff HEAD`, plus any untracked files `git status` lists).";
|
|
64
79
|
const design = missionDesignContext(mission);
|
|
65
80
|
return design ? `${summary}\n\n${design}` : summary;
|
|
66
81
|
}
|
|
67
82
|
/**
|
|
68
83
|
* The resume plan that re-enters the review→fix loop for a continuation that
|
|
69
84
|
* stopped in review, fix or verify; undefined when implement must run again.
|
|
70
|
-
* The round continues the earlier attempt's count, capped at the last
|
|
71
|
-
* a continuation always gets one review.
|
|
85
|
+
* The round continues the earlier attempt's count, capped at the lane's last
|
|
86
|
+
* round so a continuation always gets one review. `committed` is the cloud
|
|
87
|
+
* lane's resume, whose earlier work is on the branch.
|
|
72
88
|
*/
|
|
73
|
-
export function continuationResumePlan(continuation, ctx) {
|
|
89
|
+
export function continuationResumePlan(continuation, ctx, opts = {}) {
|
|
74
90
|
if (!continuesInLoop(continuation))
|
|
75
91
|
return undefined;
|
|
76
92
|
return {
|
|
77
93
|
mode: "resume_loop",
|
|
78
|
-
round: Math.min(continuation.reviewRoundsDone + 1, MAX_REVIEW_ROUNDS),
|
|
79
|
-
reviewInput: continuationReviewInput(continuation, ctx.mission),
|
|
94
|
+
round: Math.min(continuation.reviewRoundsDone + 1, opts.maxRounds ?? MAX_REVIEW_ROUNDS),
|
|
95
|
+
reviewInput: continuationReviewInput(continuation, ctx.mission, opts.committed),
|
|
80
96
|
priorRounds: [],
|
|
81
97
|
ticket: ctx.ticket,
|
|
82
98
|
...(ctx.runId ? { runId: ctx.runId } : {}),
|
|
@@ -24,9 +24,13 @@ export declare function extractFindingsBlock(raw: string): string | null;
|
|
|
24
24
|
/**
|
|
25
25
|
* Parse a reviewer's raw output into structured findings. Tries the JSON-array
|
|
26
26
|
* form first, then the fenced ```findings block, then a tolerant scan of every
|
|
27
|
-
* line. Never throws; never fabricates a finding from prose.
|
|
27
|
+
* line. Never throws; never fabricates a finding from prose. `lensColumn` reads
|
|
28
|
+
* the one reviewer's per-finding lens; `lens` is then the default for a line
|
|
29
|
+
* that names none.
|
|
28
30
|
*/
|
|
29
|
-
export declare function parseFindings(raw: string, lens?: ReviewLens
|
|
31
|
+
export declare function parseFindings(raw: string, lens?: ReviewLens, opts?: {
|
|
32
|
+
lensColumn?: boolean;
|
|
33
|
+
}): Finding[];
|
|
30
34
|
/** Concatenate per-lens findings, preserving order and each lens tag. */
|
|
31
35
|
export declare function unionFindings(perLens: Finding[][]): Finding[];
|
|
32
36
|
/** True when this one finding blocks: Critical or High on a blocking lens. */
|
|
@@ -38,9 +42,10 @@ export declare function hasBlockingFindings(findings: Finding[]): boolean;
|
|
|
38
42
|
* blocks, else stop 'round_cap' once the round hits MAX_REVIEW_ROUNDS, else
|
|
39
43
|
* keep going. Always returns an explicit reason (no silent truncation). So a
|
|
40
44
|
* second round runs only when round one found a blocker; nits, needs-person
|
|
41
|
-
* findings and business-fit findings alone never start it.
|
|
45
|
+
* findings and business-fit findings alone never start it. `maxRounds` is the
|
|
46
|
+
* lane's cap (`PipelineLane.maxReviewRounds`).
|
|
42
47
|
*/
|
|
43
|
-
export declare function shouldStop(findings: Finding[], round: number): {
|
|
48
|
+
export declare function shouldStop(findings: Finding[], round: number, maxRounds?: number): {
|
|
44
49
|
stop: boolean;
|
|
45
50
|
reason: "clean" | "round_cap" | null;
|
|
46
51
|
};
|
|
@@ -18,7 +18,10 @@ const SEVERITIES = ["critical", "high", "medium", "low", "needs_person"];
|
|
|
18
18
|
const DEFAULT_LENS = "correctness";
|
|
19
19
|
/** `NEEDS PERSON`, `needs-person` and `needs_person` all read as `needs_person`. */
|
|
20
20
|
function normalizeSeverity(value) {
|
|
21
|
-
return value
|
|
21
|
+
return value
|
|
22
|
+
.trim()
|
|
23
|
+
.toLowerCase()
|
|
24
|
+
.replace(/[\s-]+/g, "_");
|
|
22
25
|
}
|
|
23
26
|
function isSeverity(value) {
|
|
24
27
|
return SEVERITIES.includes(value);
|
|
@@ -33,14 +36,23 @@ function parseLocation(loc) {
|
|
|
33
36
|
return { file: m[1], line: Number(m[2]) };
|
|
34
37
|
return { file: trimmed };
|
|
35
38
|
}
|
|
36
|
-
/**
|
|
37
|
-
|
|
38
|
-
|
|
39
|
+
/**
|
|
40
|
+
* Parse one `SEVERITY | file:line | message` line, or null if it is not one.
|
|
41
|
+
* With `lensColumn` (the one reviewer of the cloud lane), a lens name after the
|
|
42
|
+
* severity (`SEVERITY | LENS | file:line | message`) tags the finding.
|
|
43
|
+
*/
|
|
44
|
+
function parsePipeLine(line, lens, lensColumn = false) {
|
|
45
|
+
let parts = line.split("|").map((p) => p.trim());
|
|
39
46
|
if (parts.length < 2)
|
|
40
47
|
return null;
|
|
41
48
|
const severity = normalizeSeverity(parts[0]);
|
|
42
49
|
if (!isSeverity(severity))
|
|
43
50
|
return null;
|
|
51
|
+
const named = lensColumn && parts.length >= 3 ? normalizeSeverity(parts[1]) : "";
|
|
52
|
+
if (isLens(named)) {
|
|
53
|
+
lens = named;
|
|
54
|
+
parts = [parts[0], ...parts.slice(2)];
|
|
55
|
+
}
|
|
44
56
|
let finding;
|
|
45
57
|
if (parts.length >= 3) {
|
|
46
58
|
const { file, line: lineNo } = parseLocation(parts[1]);
|
|
@@ -108,9 +120,11 @@ function isLens(value) {
|
|
|
108
120
|
/**
|
|
109
121
|
* Parse a reviewer's raw output into structured findings. Tries the JSON-array
|
|
110
122
|
* form first, then the fenced ```findings block, then a tolerant scan of every
|
|
111
|
-
* line. Never throws; never fabricates a finding from prose.
|
|
123
|
+
* line. Never throws; never fabricates a finding from prose. `lensColumn` reads
|
|
124
|
+
* the one reviewer's per-finding lens; `lens` is then the default for a line
|
|
125
|
+
* that names none.
|
|
112
126
|
*/
|
|
113
|
-
export function parseFindings(raw, lens = DEFAULT_LENS) {
|
|
127
|
+
export function parseFindings(raw, lens = DEFAULT_LENS, opts = {}) {
|
|
114
128
|
if (!raw || !raw.trim())
|
|
115
129
|
return [];
|
|
116
130
|
const json = parseJsonFindings(raw, lens);
|
|
@@ -120,7 +134,7 @@ export function parseFindings(raw, lens = DEFAULT_LENS) {
|
|
|
120
134
|
const source = block ?? raw;
|
|
121
135
|
const findings = [];
|
|
122
136
|
for (const line of source.split("\n")) {
|
|
123
|
-
const parsed = parsePipeLine(line, lens);
|
|
137
|
+
const parsed = parsePipeLine(line, lens, opts.lensColumn === true);
|
|
124
138
|
if (parsed)
|
|
125
139
|
findings.push(parsed);
|
|
126
140
|
}
|
|
@@ -152,12 +166,13 @@ export function hasBlockingFindings(findings) {
|
|
|
152
166
|
* blocks, else stop 'round_cap' once the round hits MAX_REVIEW_ROUNDS, else
|
|
153
167
|
* keep going. Always returns an explicit reason (no silent truncation). So a
|
|
154
168
|
* second round runs only when round one found a blocker; nits, needs-person
|
|
155
|
-
* findings and business-fit findings alone never start it.
|
|
169
|
+
* findings and business-fit findings alone never start it. `maxRounds` is the
|
|
170
|
+
* lane's cap (`PipelineLane.maxReviewRounds`).
|
|
156
171
|
*/
|
|
157
|
-
export function shouldStop(findings, round) {
|
|
172
|
+
export function shouldStop(findings, round, maxRounds = MAX_REVIEW_ROUNDS) {
|
|
158
173
|
if (!hasBlockingFindings(findings))
|
|
159
174
|
return { stop: true, reason: "clean" };
|
|
160
|
-
if (round >=
|
|
175
|
+
if (round >= maxRounds)
|
|
161
176
|
return { stop: true, reason: "round_cap" };
|
|
162
177
|
return { stop: false, reason: null };
|
|
163
178
|
}
|
|
@@ -85,6 +85,8 @@ export interface FinishResult {
|
|
|
85
85
|
/** Honest skip/failure notes, surfaced verbatim in the /go handoff. */
|
|
86
86
|
notes: string[];
|
|
87
87
|
}
|
|
88
|
+
/** FINISH's own git seam, for the cloud session's reads of what it committed. */
|
|
89
|
+
export declare const finishExec: WorktreeExec;
|
|
88
90
|
/** `fix` when the ticket brief (or, blind, the raw arg) says bug; else `feat` (spec §3c). */
|
|
89
91
|
export declare function commitType(ticket: string, ticketBrief?: string): "fix" | "feat";
|
|
90
92
|
/**
|
|
@@ -93,6 +95,12 @@ export declare function commitType(ticket: string, ticketBrief?: string): "fix"
|
|
|
93
95
|
* already carries the identifier), falling back to the raw arg. Capped.
|
|
94
96
|
*/
|
|
95
97
|
export declare function commitTitle(ticket: string, ticketBrief?: string): string;
|
|
98
|
+
/**
|
|
99
|
+
* The title a run with a plan and no ticket title commits under (the cloud
|
|
100
|
+
* session reads its ticket from a file): the line under the plan's Goal
|
|
101
|
+
* heading, else its first line of prose. Undefined when the plan has neither.
|
|
102
|
+
*/
|
|
103
|
+
export declare function planTitle(planText: string | undefined): string | undefined;
|
|
96
104
|
/**
|
|
97
105
|
* A conventional-commit scope from the run's changed paths: the package name
|
|
98
106
|
* when every changed path agrees on one (`packages/<name>/…`, `apps/…`,
|
|
@@ -111,11 +119,13 @@ export declare function summarizePlan(planText: string | undefined, ticket: stri
|
|
|
111
119
|
/**
|
|
112
120
|
* The trailer's YAGNI-Verify value, derived honestly from the pipeline result:
|
|
113
121
|
* `passed (<cmds>)` when the final round's verify gate ran (verify-FAILED can't
|
|
114
|
-
* reach `clean` by construction), else `skipped (<reason>)`.
|
|
122
|
+
* reach `clean` by construction), else `skipped (<reason>)`. A run that did not
|
|
123
|
+
* stop `clean` (the cloud lane commits every run's work) is `not passed (<stop>)`.
|
|
115
124
|
*/
|
|
116
125
|
export declare function verifyTrailerValue(result: {
|
|
117
126
|
verifyCommand?: string;
|
|
118
127
|
verifyNote?: string;
|
|
128
|
+
stopReason?: string;
|
|
119
129
|
}): string;
|
|
120
130
|
/** The exact provenance trailer block (spec §3c), closed by the org's commit attribution. */
|
|
121
131
|
export declare function provenanceTrailer(t: TrailerInputs): string;
|
|
@@ -58,6 +58,8 @@ const defaultExec = (argv, cwd, signal) => new Promise((resolve, reject) => {
|
|
|
58
58
|
// so quitting pi mid-FINISH never leaves it running in the background.
|
|
59
59
|
trackChild(child);
|
|
60
60
|
});
|
|
61
|
+
/** FINISH's own git seam, for the cloud session's reads of what it committed. */
|
|
62
|
+
export const finishExec = defaultExec;
|
|
61
63
|
// ---------------------------------------------------------------------------
|
|
62
64
|
// Pure message derivation
|
|
63
65
|
// ---------------------------------------------------------------------------
|
|
@@ -71,12 +73,30 @@ export function commitType(ticket, ticketBrief) {
|
|
|
71
73
|
* already carries the identifier), falling back to the raw arg. Capped.
|
|
72
74
|
*/
|
|
73
75
|
export function commitTitle(ticket, ticketBrief) {
|
|
74
|
-
let title = ticketBrief
|
|
76
|
+
let title = ticketBrief
|
|
77
|
+
?.split("\n")
|
|
78
|
+
.find((l) => l.trim().length > 0)
|
|
79
|
+
?.trim() ?? ticket;
|
|
75
80
|
const echo = `${ticket.toLowerCase()}:`;
|
|
76
81
|
if (title.toLowerCase().startsWith(echo))
|
|
77
82
|
title = title.slice(echo.length).trim() || ticket;
|
|
78
83
|
return title.length > TITLE_MAX ? title.slice(0, TITLE_MAX).trimEnd() : title;
|
|
79
84
|
}
|
|
85
|
+
/**
|
|
86
|
+
* The title a run with a plan and no ticket title commits under (the cloud
|
|
87
|
+
* session reads its ticket from a file): the line under the plan's Goal
|
|
88
|
+
* heading, else its first line of prose. Undefined when the plan has neither.
|
|
89
|
+
*/
|
|
90
|
+
export function planTitle(planText) {
|
|
91
|
+
const lines = (planText ?? "").split("\n").map((line) => line.trim());
|
|
92
|
+
const prose = (from) => lines.slice(from).find((line) => line && !line.startsWith("#") && !line.startsWith("```"));
|
|
93
|
+
const goal = lines.findIndex((line) => /^#+\s*goal\b/i.test(line));
|
|
94
|
+
const line = (goal >= 0 ? prose(goal + 1) : undefined) ?? prose(0);
|
|
95
|
+
return (line
|
|
96
|
+
?.replace(/^(?:[-*+]|\d+[.)])\s+/, "")
|
|
97
|
+
.replace(/`/g, "")
|
|
98
|
+
.trim() || undefined);
|
|
99
|
+
}
|
|
80
100
|
/**
|
|
81
101
|
* A conventional-commit scope from the run's changed paths: the package name
|
|
82
102
|
* when every changed path agrees on one (`packages/<name>/…`, `apps/…`,
|
|
@@ -125,9 +145,14 @@ export function summarizePlan(planText, ticket) {
|
|
|
125
145
|
/**
|
|
126
146
|
* The trailer's YAGNI-Verify value, derived honestly from the pipeline result:
|
|
127
147
|
* `passed (<cmds>)` when the final round's verify gate ran (verify-FAILED can't
|
|
128
|
-
* reach `clean` by construction), else `skipped (<reason>)`.
|
|
148
|
+
* reach `clean` by construction), else `skipped (<reason>)`. A run that did not
|
|
149
|
+
* stop `clean` (the cloud lane commits every run's work) is `not passed (<stop>)`.
|
|
129
150
|
*/
|
|
130
151
|
export function verifyTrailerValue(result) {
|
|
152
|
+
if (result.stopReason && result.stopReason !== "clean") {
|
|
153
|
+
const ran = result.verifyCommand ? `; ran ${oneLine(result.verifyCommand)}` : "";
|
|
154
|
+
return `not passed (${result.stopReason}${ran})`;
|
|
155
|
+
}
|
|
131
156
|
if (result.verifyCommand)
|
|
132
157
|
return `passed (${oneLine(result.verifyCommand)})`;
|
|
133
158
|
return `skipped (${oneLine(result.verifyNote ?? "verify did not run")})`;
|
|
@@ -204,7 +229,13 @@ export function parsePrUrl(output) {
|
|
|
204
229
|
* delivered as the durable /go handoff, landing in permanent scrollback.
|
|
205
230
|
*/
|
|
206
231
|
function tail(output) {
|
|
207
|
-
const t = scrubSecrets(output)
|
|
232
|
+
const t = scrubSecrets(output)
|
|
233
|
+
.trim()
|
|
234
|
+
.split("\n")
|
|
235
|
+
.slice(-3)
|
|
236
|
+
.join(" ")
|
|
237
|
+
.slice(0, NOTE_TAIL_MAX)
|
|
238
|
+
.trim();
|
|
208
239
|
return t;
|
|
209
240
|
}
|
|
210
241
|
/**
|
|
@@ -311,7 +342,15 @@ export async function runFinish(opts) {
|
|
|
311
342
|
notes.push(`Committed locally; push failed: ${tail(push.output) || "git push failed"}.`);
|
|
312
343
|
return result;
|
|
313
344
|
}
|
|
314
|
-
const pr = await run([
|
|
345
|
+
const pr = await run([
|
|
346
|
+
"gh",
|
|
347
|
+
"pr",
|
|
348
|
+
"create",
|
|
349
|
+
"--title",
|
|
350
|
+
subject,
|
|
351
|
+
"--body",
|
|
352
|
+
buildPrBody(summary, trailer, opts.remainingFindings, opts.prAttribution),
|
|
353
|
+
]);
|
|
315
354
|
if (pr.code !== 0) {
|
|
316
355
|
notes.push(`Committed and pushed ${opts.branch}; PR creation failed: ${tail(pr.output) || "gh failed"}.`);
|
|
317
356
|
return result;
|
|
@@ -327,7 +366,14 @@ export async function runFinish(opts) {
|
|
|
327
366
|
// Belt and braces: nothing above should throw, but the invariant (§0.1)
|
|
328
367
|
// outranks tidiness — a FINISH failure must never fail a clean run.
|
|
329
368
|
const msg = scrubSecrets(err instanceof Error ? err.message : String(err));
|
|
330
|
-
return {
|
|
369
|
+
return {
|
|
370
|
+
committed: false,
|
|
371
|
+
...(opts.branch ? { branch: opts.branch } : {}),
|
|
372
|
+
notes: [
|
|
373
|
+
...notes,
|
|
374
|
+
`FINISH failed: ${msg}. The run itself is still clean; the changes are left in ${opts.cwd}.`,
|
|
375
|
+
],
|
|
376
|
+
};
|
|
331
377
|
}
|
|
332
378
|
}
|
|
333
379
|
//# sourceMappingURL=finish.js.map
|
|
@@ -24,6 +24,17 @@
|
|
|
24
24
|
* run ends at the reviewed candidate and never commits, pushes, opens a PR, or
|
|
25
25
|
* reports one. The mission's own prHandoff owns all of that.
|
|
26
26
|
*
|
|
27
|
+
* CLOUD (`--cloud`, the one-attempt lane; cloud.ts): the session runs the
|
|
28
|
+
* cloud lane (stages.ts `cloudLane`: plan, implement, review, fix, then verify
|
|
29
|
+
* and finish; one writer, one reviewer, four rounds) on the host's input
|
|
30
|
+
* folder beside the ticket and its env (`YAGNI_ATTEMPT_ID`, `YAGNI_EXECUTION_ID`,
|
|
31
|
+
* `YAGNI_GENERATION`, `YAGNI_MISSION_OUT_DIR`, `YAGNI_GO_STAGE_TIERS`). A fresh
|
|
32
|
+
* attempt plans and stops, writing `plan.md` and `plan.json`; a plan file
|
|
33
|
+
* starts implement; a continue file names its stage. Every execution but a
|
|
34
|
+
* plan commits its work locally on the branch the host checked out, whatever
|
|
35
|
+
* its stop, and never pushes. Its last act is `result.json`, mirrored on the
|
|
36
|
+
* last `pipeline_result` line. Mission mode below is untouched by it.
|
|
37
|
+
*
|
|
27
38
|
* CONTINUATION (`--continue-file`, mission mode only): the run continues an
|
|
28
39
|
* earlier attempt whose change is already in the tree as uncommitted edits. The
|
|
29
40
|
* build baselines the clean base, and an attempt that stopped in review, fix or
|
|
@@ -59,13 +70,15 @@
|
|
|
59
70
|
* cannot finish, rather than being killed partway through one (see budget.ts). Such a run gets the sandbox lane's
|
|
60
71
|
* ceilings (`SANDBOX_RUN_BUDGET`), not interactive /go's.
|
|
61
72
|
*/
|
|
73
|
+
import { snapshotWorkspace } from "./workspace.js";
|
|
74
|
+
import type { WorktreeExec } from "./worktree.js";
|
|
62
75
|
import { type TreeSnapshotDeps } from "./treeSnapshot.js";
|
|
63
76
|
import { runPipeline as defaultRunPipeline } from "./orchestrator.js";
|
|
64
77
|
import { makeRunVerify, type VerifyBaselineResult } from "./verify.js";
|
|
65
78
|
import { type RunBudget } from "./budget.js";
|
|
66
79
|
import type { Finding, JsonEvent, ModelTier, PipelineStage, StageId, StageTag, StageUsage, StopReason } from "./types.js";
|
|
67
80
|
/** One-line usage copy, shared by every argument error. */
|
|
68
|
-
export declare const HEADLESS_GO_USAGE = "Usage: yagni go --headless --ticket-file <path> [--plan-file <path>] [--memo-file <path>] [--continue-file <path>] [--run-id <id>] [--cwd <path>] [--json]";
|
|
81
|
+
export declare const HEADLESS_GO_USAGE = "Usage: yagni go --headless [--cloud] --ticket-file <path> [--plan-file <path>] [--memo-file <path>] [--continue-file <path>] [--run-id <id>] [--cwd <path>] [--json]";
|
|
69
82
|
/**
|
|
70
83
|
* Exit codes. `verified` is deliberately narrow: only a `clean` stop means the
|
|
71
84
|
* run produced a reviewed candidate, so a round_cap / no_changes / failed run
|
|
@@ -80,6 +93,8 @@ export declare const HEADLESS_GO_EXIT: {
|
|
|
80
93
|
export interface HeadlessGoArgs {
|
|
81
94
|
headless: boolean;
|
|
82
95
|
json: boolean;
|
|
96
|
+
/** The one-attempt lane's session (cloud.ts): its own stage list and outputs. */
|
|
97
|
+
cloud: boolean;
|
|
83
98
|
ticketFile?: string;
|
|
84
99
|
planFile?: string;
|
|
85
100
|
memoFile?: string;
|
|
@@ -184,6 +199,10 @@ export interface HeadlessGoDeps {
|
|
|
184
199
|
logger?: (event: string, data?: unknown) => void;
|
|
185
200
|
/** Mission runs' working-tree snapshots (see treeSnapshot.ts); injected in tests. */
|
|
186
201
|
startTreeSnapshots?: (cwd: string, snapshotDeps: TreeSnapshotDeps) => () => Promise<void>;
|
|
202
|
+
/** A cloud session's git seam: its commit and the reads behind `result.json`. Defaults to FINISH's. */
|
|
203
|
+
exec?: WorktreeExec;
|
|
204
|
+
/** A cloud session's tree reads (HEAD and porcelain status). Defaults to a real `git`. */
|
|
205
|
+
snapshotWorkspace?: typeof snapshotWorkspace;
|
|
187
206
|
}
|
|
188
207
|
/**
|
|
189
208
|
* Parse the argv remainder after `go`. Accepts both `--flag value` and
|