@clemrig/factory 0.8.0 → 0.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +24 -8
- package/dist/app/board.js +44 -9
- package/dist/app/board.js.map +1 -1
- package/dist/app/define.d.ts +3 -3
- package/dist/app/define.js +15 -9
- package/dist/app/define.js.map +1 -1
- package/dist/app/run.d.ts +1 -1
- package/dist/app/run.js +4 -3
- package/dist/app/run.js.map +1 -1
- package/dist/app/workflows/implement.d.ts +1 -6
- package/dist/app/workflows/implement.js +40 -592
- package/dist/app/workflows/implement.js.map +1 -1
- package/dist/app/workflows/stages/agents.d.ts +34 -0
- package/dist/app/workflows/stages/agents.js +93 -0
- package/dist/app/workflows/stages/agents.js.map +1 -0
- package/dist/app/workflows/stages/choose.d.ts +13 -0
- package/dist/app/workflows/stages/choose.js +107 -0
- package/dist/app/workflows/stages/choose.js.map +1 -0
- package/dist/app/workflows/stages/deliver.d.ts +18 -0
- package/dist/app/workflows/stages/deliver.js +167 -0
- package/dist/app/workflows/stages/deliver.js.map +1 -0
- package/dist/app/workflows/stages/implement.d.ts +5 -0
- package/dist/app/workflows/stages/implement.js +42 -0
- package/dist/app/workflows/stages/implement.js.map +1 -0
- package/dist/app/workflows/stages/plan.d.ts +7 -0
- package/dist/app/workflows/stages/plan.js +34 -0
- package/dist/app/workflows/stages/plan.js.map +1 -0
- package/dist/app/workflows/stages/prepare.d.ts +14 -0
- package/dist/app/workflows/stages/prepare.js +195 -0
- package/dist/app/workflows/stages/prepare.js.map +1 -0
- package/dist/app/workflows/stages/resume.d.ts +7 -0
- package/dist/app/workflows/stages/resume.js +54 -0
- package/dist/app/workflows/stages/resume.js.map +1 -0
- package/dist/app/workflows/stages/review.d.ts +16 -0
- package/dist/app/workflows/stages/review.js +103 -0
- package/dist/app/workflows/stages/review.js.map +1 -0
- package/dist/app/workflows/stages/run.d.ts +60 -0
- package/dist/app/workflows/stages/run.js +16 -0
- package/dist/app/workflows/stages/run.js.map +1 -0
- package/dist/app/workflows/stages/verify.d.ts +11 -0
- package/dist/app/workflows/stages/verify.js +47 -0
- package/dist/app/workflows/stages/verify.js.map +1 -0
- package/dist/app/workflows/tidy.d.ts +7 -0
- package/dist/app/workflows/tidy.js +79 -0
- package/dist/app/workflows/tidy.js.map +1 -0
- package/dist/cli/help.d.ts +1 -1
- package/dist/cli/help.js +4 -3
- package/dist/cli/help.js.map +1 -1
- package/dist/domain/board.d.ts +18 -3
- package/dist/domain/board.js +24 -10
- package/dist/domain/board.js.map +1 -1
- package/dist/domain/definition.d.ts +6 -1
- package/dist/domain/definition.js +3 -0
- package/dist/domain/definition.js.map +1 -1
- package/dist/domain/plan.d.ts +1 -1
- package/dist/domain/plan.js +1 -1
- package/dist/domain/spec.d.ts +1 -0
- package/dist/domain/spec.js +3 -0
- package/dist/domain/spec.js.map +1 -1
- package/dist/domain/tidy.d.ts +58 -0
- package/dist/domain/tidy.js +57 -0
- package/dist/domain/tidy.js.map +1 -0
- package/dist/system/factory.js +4 -3
- package/dist/system/factory.js.map +1 -1
- package/dist/system/git.d.ts +16 -2
- package/dist/system/git.js +52 -3
- package/dist/system/git.js.map +1 -1
- package/dist/system/github.d.ts +14 -0
- package/dist/system/github.js +38 -0
- package/dist/system/github.js.map +1 -1
- package/dist/system/lock.js +1 -1
- package/dist/system/lock.js.map +1 -1
- package/package.json +2 -2
- package/prompts/scout.md +53 -0
- /package/prompts/{implement/describe.md → describe.md} +0 -0
- /package/prompts/{implement/implement.md → implement.md} +0 -0
- /package/prompts/{implement/plan.md → plan.md} +0 -0
- /package/prompts/{implement/review.md → review.md} +0 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"plan.js","sourceRoot":"","sources":["../../../../src/app/workflows/stages/plan.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,KAAK,EAEL,UAAU,EACV,IAAI,GACL,MAAM,eAAe,CAAC;AACvB,OAAO,EACL,iBAAiB,EACjB,UAAU,EACV,WAAW,EACX,UAAU,GACX,MAAM,yBAAyB,CAAC;AACjC,OAAO,KAAK,GAAG,MAAM,wBAAwB,CAAC;AAC9C,OAAO,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,2BAA2B,CAAC;AACxE,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,YAAY,EAAE,MAAM,UAAU,CAAC;AAEtD,oGAAoG;AACpG,MAAM,UAAU,OAAO,CAAC,OAAqB;IAC3C,OAAO,KAAK,CAAC;QACX,OAAO;QACP,MAAM,EAAE,UAAU;QAClB,OAAO,EAAE,CAAC,EAAE,KAAK,EAAE,EAAE,EAAE,CAAC,CAAC;YACvB,KAAK,EAAE,KAAK,CAAC,KAAK,CAAC,CAAC,IAAI;YACxB,IAAI,EAAE,KAAK,CAAC,KAAK,CAAC,CAAC,IAAI;SACxB,CAAC;KACH,CAAC,CAAC;AACL,CAAC;AAED,wEAAwE;AACxE,MAAM,CAAC,MAAM,SAAS,GAAG,UAAU,CAAC;IAClC,MAAM,EAAE,UAAU;IAClB,IAAI,EAAE,MAAM;IACZ,KAAK,EAAE,CAAC,IAAI,EAAE,EAAE,KAAK,EAAE,EAAE,EAAE,CAAC,WAAW,CAAC,IAAI,EAAE,KAAK,CAAC,KAAK,CAAC,CAAC,QAAQ,CAAC;CACrE,CAAC,CAAC;AAEH,uEAAuE;AACvE,MAAM,CAAC,MAAM,UAAU,GAAG,IAAI,CAAC,KAAK,EAAE,EAAE,GAAG,EAAE,KAAK,EAAE,GAAG,EAAE,EAAE,EAAE;IAC3D,MAAM,CAAC,GAAG,KAAK,CAAC,KAAK,CAAC,CAAC;IACvB,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,QAAQ,EAAE,IAAI,EAAE,GAAG,CAAC,CAAC;IAC1C,MAAM,aAAa,CACjB,GAAG,EACH,IAAI,EACJ,MAAM,EACN,UAAU,CAAC,IAAI,EAAE,EAAE,KAAK,EAAE,QAAQ,EAAE,UAAU,EAAE,YAAY,CAAC,CAAC,CAAC,EAAE,CAAC,CACnE,CAAC;IACF,MAAM,UAAU,GAAG,MAAM,GAAG,CAAC,MAAM,CAAC,GAAG,EAAE,iBAAiB,CAAC,IAAI,CAAC,EAAE;QAChE,YAAY,CAAC,IAAI,EAAE,MAAM,CAAC;KAC3B,CAAC,CAAC;IACH,GAAG,CAAC,sBAAsB,IAAI,CAAC,KAAK,CAAC,MAAM,cAAc,KAAK,CAAC,UAAU,CAAC,EAAE,CAAC,CAAC;IAC9E,OAAO,EAAE,UAAU,EAAE,CAAC;AACxB,CAAC,CAAC,CAAC"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { type Stage } from "@clemrig/flow";
|
|
2
|
+
import { type VerifyCommand } from "../../../domain/verification.js";
|
|
3
|
+
/**
|
|
4
|
+
* Every reason not to deliver the issue, found without changing anything; then
|
|
5
|
+
* the base must pass its checks, and the run takes the issue's branch.
|
|
6
|
+
*/
|
|
7
|
+
export declare function prepareIssue(checks: VerifyCommand[]): Stage<"ok">;
|
|
8
|
+
/**
|
|
9
|
+
* The same starting conditions as an issue's; then the base must pass its
|
|
10
|
+
* checks, and the run takes its own branch, `tidy-<run>`, for the cleanup it
|
|
11
|
+
* will choose. Unless a cleanup waits for the operator: one at a time, and
|
|
12
|
+
* the run ends there.
|
|
13
|
+
*/
|
|
14
|
+
export declare function prepareTidy(checks: VerifyCommand[]): Stage<"ready" | "waiting">;
|
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
import { code, step } from "@clemrig/flow";
|
|
2
|
+
import { ghVersionProblem } from "../../../domain/pull-request.js";
|
|
3
|
+
import { parseSpec } from "../../../domain/spec.js";
|
|
4
|
+
import { isTidyBranch, tidySlug } from "../../../domain/tidy.js";
|
|
5
|
+
import { failureFeedback, } from "../../../domain/verification.js";
|
|
6
|
+
import { runBrowser } from "../../../system/browser.js";
|
|
7
|
+
import { runChecks } from "../../../system/checks.js";
|
|
8
|
+
import * as git from "../../../system/git.js";
|
|
9
|
+
import * as github from "../../../system/github.js";
|
|
10
|
+
import { artifactPath, hasArtifact, isIssue, readArtifact, } from "../../../system/issues.js";
|
|
11
|
+
import { withTreeLock } from "../../../system/lock.js";
|
|
12
|
+
import { screenshotsFolder } from "../../../system/run-files.js";
|
|
13
|
+
import { message, short } from "./run.js";
|
|
14
|
+
import { journalChecks } from "./verify.js";
|
|
15
|
+
/** Why the repository cannot start a run: it must be on its main branch, without changes, as on origin. */
|
|
16
|
+
async function baseProblems(dir, base) {
|
|
17
|
+
const problems = [];
|
|
18
|
+
if ((await git.currentBranch(dir)) !== base)
|
|
19
|
+
problems.push(`Switch to ${base} first`);
|
|
20
|
+
if (await git.hasChanges(dir))
|
|
21
|
+
problems.push("Commit or discard the changes in progress first");
|
|
22
|
+
await git.fetchBranch(dir, base);
|
|
23
|
+
if ((await git.commitOf(dir, "HEAD")) !==
|
|
24
|
+
(await git.commitOf(dir, git.remoteRef(base))))
|
|
25
|
+
problems.push(`${base} must match origin/${base}: push or pull first`);
|
|
26
|
+
return problems;
|
|
27
|
+
}
|
|
28
|
+
/** The repository on GitHub, when gh is recent enough and logged in to open its pull request; otherwise why not. */
|
|
29
|
+
async function gitHubProblems(dir) {
|
|
30
|
+
const problems = [];
|
|
31
|
+
const version = await github
|
|
32
|
+
.version(dir)
|
|
33
|
+
.then(ghVersionProblem, (error) => message(error));
|
|
34
|
+
if (version)
|
|
35
|
+
problems.push(version);
|
|
36
|
+
if (!(await github.isAuthenticated(dir))) {
|
|
37
|
+
problems.push("gh is not logged in: run gh auth login");
|
|
38
|
+
return { problems };
|
|
39
|
+
}
|
|
40
|
+
try {
|
|
41
|
+
return { repository: await github.repository(dir), problems };
|
|
42
|
+
}
|
|
43
|
+
catch (error) {
|
|
44
|
+
problems.push(`gh cannot read the repository on GitHub: ${message(error)}`);
|
|
45
|
+
return { problems };
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
/** The dependencies that are not delivered: no plan.md on the main branch, which is checked out. */
|
|
49
|
+
async function dependencyProblems(dir, slug, base, dependencies) {
|
|
50
|
+
const problems = [];
|
|
51
|
+
for (const dependency of dependencies) {
|
|
52
|
+
if (await hasArtifact(dir, dependency, "plan"))
|
|
53
|
+
continue;
|
|
54
|
+
if (await isIssue(dir, dependency))
|
|
55
|
+
problems.push(`Issue ${slug} depends on ${dependency}, which is not delivered (its plan.md is not on ${base}): deliver it first, or revise the dependency with the issue skill (/issue ${slug})`);
|
|
56
|
+
else
|
|
57
|
+
problems.push(`Issue ${slug} depends on ${dependency}, which is no issue of .factory/issues/: fix spec.md with the issue skill (/issue ${slug})`);
|
|
58
|
+
}
|
|
59
|
+
return problems;
|
|
60
|
+
}
|
|
61
|
+
function cannotStart(problems) {
|
|
62
|
+
return new Error(["Cannot start:", ...problems.map((problem) => `- ${problem}`)].join("\n"));
|
|
63
|
+
}
|
|
64
|
+
/** The main branch must pass its own checks before any agent; the checks with `when` wait for a change. */
|
|
65
|
+
async function checkBase(dir, checks, base, log) {
|
|
66
|
+
const result = await runChecks(dir, checks, [], journalChecks(log, ` on ${base}`));
|
|
67
|
+
if (!result.ok)
|
|
68
|
+
throw new Error(`${base} does not pass its own checks:\n${failureFeedback(result.check, result.cause, result.output)}`);
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* The agents' browser and screenshots folder, kept in the run's folder,
|
|
72
|
+
* outside the repository: made before the run takes the tree, so that a
|
|
73
|
+
* failure leaves it on the main branch.
|
|
74
|
+
*/
|
|
75
|
+
async function agentWorkspace(dir, runDir) {
|
|
76
|
+
const browser = await runBrowser(dir, runDir);
|
|
77
|
+
const screenshotsDir = await screenshotsFolder(runDir);
|
|
78
|
+
return { ...(browser ? { browser } : {}), screenshotsDir };
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* Runs `work`, which takes the tree, once it is found on the main branch at
|
|
82
|
+
* the base commit the run approved. One run at a time per working tree:
|
|
83
|
+
* another one started `meanwhile` may have moved it. Checking and taking the
|
|
84
|
+
* tree happen under one lock, so that two runs never both find it free; the
|
|
85
|
+
* run starts from the approved commit, never from theirs.
|
|
86
|
+
*/
|
|
87
|
+
async function withFreeTree(dir, { base, baseCommit }, meanwhile, work) {
|
|
88
|
+
return withTreeLock(dir, async () => {
|
|
89
|
+
const branch = await git.currentBranch(dir);
|
|
90
|
+
const head = await git.commitOf(dir, "HEAD");
|
|
91
|
+
if (branch !== base || head !== baseCommit)
|
|
92
|
+
throw new Error(`The repository moved ${meanwhile} (now ${branch || "detached"} at ${short(head)}): another run is using it; start again once it is done`);
|
|
93
|
+
return work();
|
|
94
|
+
});
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* Every reason not to deliver the issue, found without changing anything; then
|
|
98
|
+
* the base must pass its checks, and the run takes the issue's branch.
|
|
99
|
+
*/
|
|
100
|
+
export function prepareIssue(checks) {
|
|
101
|
+
return step(async ({ dir, state, log, runDir }) => {
|
|
102
|
+
const slug = String(state.input.issue);
|
|
103
|
+
const base = await git.defaultBranch(dir);
|
|
104
|
+
const problems = [];
|
|
105
|
+
// The run commits and force-pushes on the branch the slug names.
|
|
106
|
+
if (slug === base)
|
|
107
|
+
problems.push(`Issue ${slug} names the main branch: its branch would replace ${base}; rename the issue`);
|
|
108
|
+
problems.push(...(await baseProblems(dir, base)));
|
|
109
|
+
const text = await readArtifact(dir, slug, "spec");
|
|
110
|
+
const spec = text === undefined ? undefined : parseSpec(text, slug);
|
|
111
|
+
if (!spec)
|
|
112
|
+
problems.push(`Issue ${slug} has no spec.md`);
|
|
113
|
+
else if (!spec.ok)
|
|
114
|
+
problems.push(...spec.problems.map((problem) => `spec.md: ${problem}`), `Fix spec.md with the issue skill (/issue ${slug}), then commit and push it`);
|
|
115
|
+
else
|
|
116
|
+
problems.push(...(await dependencyProblems(dir, slug, base, spec.value.dependencies)));
|
|
117
|
+
if (await hasArtifact(dir, slug, "plan"))
|
|
118
|
+
problems.push(`Issue ${slug} is already delivered (its plan.md is on ${base}): start a new issue`);
|
|
119
|
+
const { repository, problems: gitHub } = await gitHubProblems(dir);
|
|
120
|
+
problems.push(...gitHub);
|
|
121
|
+
if (problems.length > 0 || !spec?.ok || text === undefined || !repository)
|
|
122
|
+
throw cannotStart(problems);
|
|
123
|
+
log(`Starting point valid: ${base} matches origin, spec with ${spec.value.criteria.length} criteria, gh logged in`);
|
|
124
|
+
const baseCommit = await git.commitOf(dir, "HEAD");
|
|
125
|
+
await checkBase(dir, checks, base, log);
|
|
126
|
+
const workspace = await agentWorkspace(dir, runDir);
|
|
127
|
+
await withFreeTree(dir, { base, baseCommit }, `while ${base}'s checks ran`, () => git.switchTo(dir, slug, baseCommit));
|
|
128
|
+
log(`Created branch ${slug} from ${base} at ${short(baseCommit)}`);
|
|
129
|
+
return {
|
|
130
|
+
slug,
|
|
131
|
+
title: spec.value.title,
|
|
132
|
+
spec: text,
|
|
133
|
+
objective: spec.value.objective,
|
|
134
|
+
criteria: spec.value.criteria,
|
|
135
|
+
repository,
|
|
136
|
+
...workspace,
|
|
137
|
+
base,
|
|
138
|
+
baseCommit,
|
|
139
|
+
specPath: artifactPath(slug, "spec"),
|
|
140
|
+
planPath: artifactPath(slug, "plan"),
|
|
141
|
+
};
|
|
142
|
+
});
|
|
143
|
+
}
|
|
144
|
+
/**
|
|
145
|
+
* The same starting conditions as an issue's; then the base must pass its
|
|
146
|
+
* checks, and the run takes its own branch, `tidy-<run>`, for the cleanup it
|
|
147
|
+
* will choose. Unless a cleanup waits for the operator: one at a time, and
|
|
148
|
+
* the run ends there.
|
|
149
|
+
*/
|
|
150
|
+
export function prepareTidy(checks) {
|
|
151
|
+
return code(["ready", "waiting"], async ({ dir, state, log, runDir }) => {
|
|
152
|
+
const slug = tidySlug(state.id);
|
|
153
|
+
const base = await git.defaultBranch(dir);
|
|
154
|
+
const problems = await baseProblems(dir, base);
|
|
155
|
+
const { repository, problems: gitHub } = await gitHubProblems(dir);
|
|
156
|
+
problems.push(...gitHub);
|
|
157
|
+
if (problems.length > 0 || !repository)
|
|
158
|
+
throw cannotStart(problems);
|
|
159
|
+
log(`Starting point valid: ${base} matches origin, gh logged in`);
|
|
160
|
+
const baseCommit = await git.commitOf(dir, "HEAD");
|
|
161
|
+
await checkBase(dir, checks, base, log);
|
|
162
|
+
const workspace = await agentWorkspace(dir, runDir);
|
|
163
|
+
const { openPullRequests, waiting } = await withFreeTree(dir, { base, baseCommit }, `while ${base}'s checks ran`, async () => {
|
|
164
|
+
// Read once the tree is found free: a run is back on the main branch
|
|
165
|
+
// only once its pull request is open.
|
|
166
|
+
const openPullRequests = await github.openPullRequests(dir);
|
|
167
|
+
const waiting = openPullRequests.find(({ branch }) => isTidyBranch(branch));
|
|
168
|
+
if (!waiting)
|
|
169
|
+
await git.switchTo(dir, slug, baseCommit);
|
|
170
|
+
return { openPullRequests, waiting };
|
|
171
|
+
});
|
|
172
|
+
if (waiting)
|
|
173
|
+
return {
|
|
174
|
+
outcome: "waiting",
|
|
175
|
+
reason: `A cleanup waits for the operator: ${waiting.url}`,
|
|
176
|
+
data: { waitingFor: waiting.url },
|
|
177
|
+
};
|
|
178
|
+
log(`Created branch ${slug} from ${base} at ${short(baseCommit)}`);
|
|
179
|
+
return {
|
|
180
|
+
outcome: "ready",
|
|
181
|
+
reason: `${base} passes its checks, no cleanup waiting`,
|
|
182
|
+
data: {
|
|
183
|
+
slug,
|
|
184
|
+
repository,
|
|
185
|
+
...workspace,
|
|
186
|
+
base,
|
|
187
|
+
baseCommit,
|
|
188
|
+
specPath: artifactPath(slug, "spec"),
|
|
189
|
+
planPath: artifactPath(slug, "plan"),
|
|
190
|
+
openPullRequests,
|
|
191
|
+
},
|
|
192
|
+
};
|
|
193
|
+
});
|
|
194
|
+
}
|
|
195
|
+
//# sourceMappingURL=prepare.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"prepare.js","sourceRoot":"","sources":["../../../../src/app/workflows/stages/prepare.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { StageContext } from "@clemrig/flow";
|
|
2
|
+
/**
|
|
3
|
+
* A failed delivery resumes on its branch, at the commit its stage started
|
|
4
|
+
* from, with its changes in progress and none in .factory/ but the plan it may
|
|
5
|
+
* have written; `prepare` checks everything again, and a delivered run is done.
|
|
6
|
+
*/
|
|
7
|
+
export declare function resumeProblem({ dir, state, }: Pick<StageContext, "dir" | "state">): Promise<string | undefined>;
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import * as git from "../../../system/git.js";
|
|
2
|
+
import { artifactPath } from "../../../system/issues.js";
|
|
3
|
+
import { facts, short, specCommitOf } from "./run.js";
|
|
4
|
+
/**
|
|
5
|
+
* A failed delivery resumes on its branch, at the commit its stage started
|
|
6
|
+
* from, with its changes in progress and none in .factory/ but the plan it may
|
|
7
|
+
* have written; `prepare` checks everything again, and a delivered run is done.
|
|
8
|
+
*/
|
|
9
|
+
export async function resumeProblem({ dir, state, }) {
|
|
10
|
+
if (state.stage === "prepare")
|
|
11
|
+
return undefined;
|
|
12
|
+
// Its pull request is open: only its screenshots remain to attach, from the
|
|
13
|
+
// run's folder, whatever the repository is on.
|
|
14
|
+
if (state.stage === "attach")
|
|
15
|
+
return undefined;
|
|
16
|
+
// Stopped once its last stage was done, such as by Ctrl-C.
|
|
17
|
+
const { pullRequest } = state.data;
|
|
18
|
+
if (typeof pullRequest === "string")
|
|
19
|
+
return `the run already delivered its pull request ${pullRequest}`;
|
|
20
|
+
// An earlier factory read neither the repository on GitHub nor the spec's
|
|
21
|
+
// objective: the pull request this run would open needs them.
|
|
22
|
+
if (typeof state.data.repository !== "object")
|
|
23
|
+
return "the run started with an earlier version of the factory, whose data this one does not read: start a new run";
|
|
24
|
+
// The review and the description start from the tree the review reads,
|
|
25
|
+
// which Git may have pruned meanwhile.
|
|
26
|
+
const { reviewedTree } = state.data;
|
|
27
|
+
if ((state.stage === "review" || state.stage === "describe") &&
|
|
28
|
+
typeof reviewedTree === "string" &&
|
|
29
|
+
!(await git.hasObject(dir, reviewedTree)))
|
|
30
|
+
return `the tree the ${state.stage === "review" ? "review" : "description"} reads is no longer in Git (removed by git gc): start a new run`;
|
|
31
|
+
const f = facts(state);
|
|
32
|
+
const { slug, specPath, planPath } = f;
|
|
33
|
+
// Until the plan is committed, the branch is at the spec's commit: the base
|
|
34
|
+
// commit, until a cleanup commits its own.
|
|
35
|
+
const { planCommit } = state.data;
|
|
36
|
+
const expected = typeof planCommit === "string" ? planCommit : specCommitOf(f);
|
|
37
|
+
const branch = await git.currentBranch(dir);
|
|
38
|
+
const head = await git.commitOf(dir, "HEAD");
|
|
39
|
+
const problems = [];
|
|
40
|
+
if (branch !== slug || head !== expected)
|
|
41
|
+
problems.push(`the repository must be on ${slug} at ${short(expected)}, where the run stopped, with its changes in progress; it is on ${branch || "a detached HEAD"} at ${short(head)}${state.stage === "deliver" && branch === slug
|
|
42
|
+
? `; if the run already committed its changes, git reset --soft ${short(expected)} puts them back in progress`
|
|
43
|
+
: ""}`);
|
|
44
|
+
// `commit-spec` and `commit-plan` write their files again before committing them.
|
|
45
|
+
const rewritten = state.stage === "commit-spec"
|
|
46
|
+
? [artifactPath(slug, "issue"), specPath]
|
|
47
|
+
: state.stage === "commit-plan"
|
|
48
|
+
? [planPath]
|
|
49
|
+
: [];
|
|
50
|
+
if (await git.hasChanges(dir, ".factory", rewritten))
|
|
51
|
+
problems.push(".factory/ has changes in progress, which a run never delivers: discard them");
|
|
52
|
+
return problems.length ? problems.join("; ") : undefined;
|
|
53
|
+
}
|
|
54
|
+
//# sourceMappingURL=resume.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"resume.js","sourceRoot":"","sources":["../../../../src/app/workflows/stages/resume.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { type AgentProfile, type Stage } from "@clemrig/flow";
|
|
2
|
+
/**
|
|
3
|
+
* Every change since the plan, new files included, and the tree the review
|
|
4
|
+
* reads; a large diff is read from a file, part by part.
|
|
5
|
+
*/
|
|
6
|
+
export declare const diff: Stage<"ok">;
|
|
7
|
+
/**
|
|
8
|
+
* The reviewing agent: it judges the change against the spec, from the code
|
|
9
|
+
* and the checks alone. From the second round on, it first reviews the change
|
|
10
|
+
* knowing nothing of the earlier reviews, then, in the same conversation,
|
|
11
|
+
* checks what the previous one found against the code as it is now, and gives
|
|
12
|
+
* its whole review again: nothing found earlier is lost on the way.
|
|
13
|
+
*/
|
|
14
|
+
export declare function reviewer(profile: AgentProfile): Stage<"ok" | "modified">;
|
|
15
|
+
/** An unmet criterion or a blocking finding sends the change back, unless the operator accepted the review. */
|
|
16
|
+
export declare const judgeReview: Stage<"approved" | "changes" | "invalid">;
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
import { agent, gate, step, z, } from "@clemrig/flow";
|
|
2
|
+
import { largeChangeLines, reviewChanges, reviewOutput, reviewProblem, } from "../../../domain/review.js";
|
|
3
|
+
import * as git from "../../../system/git.js";
|
|
4
|
+
import { keepInRun } from "../../../system/run-files.js";
|
|
5
|
+
import { browserContext, readers, reading } from "./agents.js";
|
|
6
|
+
import { facts, reviewRounds } from "./run.js";
|
|
7
|
+
/**
|
|
8
|
+
* A new conversation for each review, and for a review started again after it
|
|
9
|
+
* changed the repository; a correction of its format resumes it.
|
|
10
|
+
*/
|
|
11
|
+
function reviewSession(state) {
|
|
12
|
+
const restarts = state.retries?.["review.modified"] ?? 0;
|
|
13
|
+
return `review-${reviewRounds(state)}${restarts > 0 ? `-${restarts}` : ""}`;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Every change since the plan, new files included, and the tree the review
|
|
17
|
+
* reads; a large diff is read from a file, part by part.
|
|
18
|
+
*/
|
|
19
|
+
export const diff = step(async ({ dir, state, log, runDir }) => {
|
|
20
|
+
const { planCommit } = facts(state);
|
|
21
|
+
const reviewedTree = await git.snapshot(dir);
|
|
22
|
+
const patch = await git.diff(dir, planCommit, reviewedTree);
|
|
23
|
+
if (patch.truncated)
|
|
24
|
+
throw new Error("The changes are too large to review (over 2 MiB of diff): the run stops");
|
|
25
|
+
const files = await git.diffStat(dir, planCommit, reviewedTree);
|
|
26
|
+
const lines = patch.text.split("\n").length;
|
|
27
|
+
log(`Changes since the plan: ${files.length} files, ${lines} diff lines`);
|
|
28
|
+
const changes = lines <= largeChangeLines
|
|
29
|
+
? { files, lines, large: false, diff: patch.text }
|
|
30
|
+
: {
|
|
31
|
+
files,
|
|
32
|
+
lines,
|
|
33
|
+
large: true,
|
|
34
|
+
diffFile: await keepInRun(runDir, "diff.patch", patch.text),
|
|
35
|
+
};
|
|
36
|
+
if (changes.large)
|
|
37
|
+
log(`Large change: the review reads its diff from ${changes.diffFile}`);
|
|
38
|
+
return { changes, reviewedTree };
|
|
39
|
+
});
|
|
40
|
+
/**
|
|
41
|
+
* The reviewing agent: it judges the change against the spec, from the code
|
|
42
|
+
* and the checks alone. From the second round on, it first reviews the change
|
|
43
|
+
* knowing nothing of the earlier reviews, then, in the same conversation,
|
|
44
|
+
* checks what the previous one found against the code as it is now, and gives
|
|
45
|
+
* its whole review again: nothing found earlier is lost on the way.
|
|
46
|
+
*/
|
|
47
|
+
export function reviewer(profile) {
|
|
48
|
+
const call = (context) => agent({ profile, schema: reviewOutput, session: reviewSession, context });
|
|
49
|
+
const fresh = call(({ state }) => {
|
|
50
|
+
const { slug, spec, changes, checks } = facts(state);
|
|
51
|
+
return { issue: slug, spec, changes, checks, ...browserContext(state) };
|
|
52
|
+
});
|
|
53
|
+
const recheck = call(({ state }) => ({
|
|
54
|
+
previous: facts(state).reviews?.at(-1),
|
|
55
|
+
}));
|
|
56
|
+
return reading(readers.review, {
|
|
57
|
+
...fresh,
|
|
58
|
+
async run(context) {
|
|
59
|
+
const first = await fresh.run(context);
|
|
60
|
+
if ((facts(context.state).reviews ?? []).length === 0)
|
|
61
|
+
return first;
|
|
62
|
+
return recheck.run(context);
|
|
63
|
+
},
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
/** An unmet criterion or a blocking finding sends the change back, unless the operator accepted the review. */
|
|
67
|
+
export const judgeReview = gate(["approved", "changes", "invalid"], async ({ state }) => {
|
|
68
|
+
const parsed = reviewOutput.safeParse(state.output);
|
|
69
|
+
if (!parsed.success)
|
|
70
|
+
return {
|
|
71
|
+
outcome: "invalid",
|
|
72
|
+
reason: `Invalid structured output:\n${z.prettifyError(parsed.error)}`,
|
|
73
|
+
};
|
|
74
|
+
const problem = reviewProblem(parsed.data, facts(state).criteria);
|
|
75
|
+
if (problem)
|
|
76
|
+
return { outcome: "invalid", reason: problem };
|
|
77
|
+
const changes = reviewChanges(parsed.data);
|
|
78
|
+
// Each review judged is kept, the last one apart for the stages after.
|
|
79
|
+
const judged = {
|
|
80
|
+
review: parsed.data,
|
|
81
|
+
reviews: [...(facts(state).reviews ?? []), parsed.data],
|
|
82
|
+
};
|
|
83
|
+
// The operator resumed the run to deliver it as the review left it.
|
|
84
|
+
if (changes && state.data.acceptedReview === true)
|
|
85
|
+
return {
|
|
86
|
+
outcome: "approved",
|
|
87
|
+
reason: "The operator accepted the review's unmet criteria and blocking findings",
|
|
88
|
+
data: judged,
|
|
89
|
+
};
|
|
90
|
+
if (changes)
|
|
91
|
+
return {
|
|
92
|
+
outcome: "changes",
|
|
93
|
+
reason: "The review requires changes",
|
|
94
|
+
feedback: changes,
|
|
95
|
+
data: judged,
|
|
96
|
+
};
|
|
97
|
+
return {
|
|
98
|
+
outcome: "approved",
|
|
99
|
+
reason: "Every criterion is met and nothing blocks",
|
|
100
|
+
data: judged,
|
|
101
|
+
};
|
|
102
|
+
});
|
|
103
|
+
//# sourceMappingURL=review.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"review.js","sourceRoot":"","sources":["../../../../src/app/workflows/stages/review.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,KAAK,EACL,IAAI,EAIJ,IAAI,EACJ,CAAC,GACF,MAAM,eAAe,CAAC;AACvB,OAAO,EAEL,gBAAgB,EAChB,aAAa,EACb,YAAY,EACZ,aAAa,GACd,MAAM,2BAA2B,CAAC;AACnC,OAAO,KAAK,GAAG,MAAM,wBAAwB,CAAC;AAC9C,OAAO,EAAE,SAAS,EAAE,MAAM,8BAA8B,CAAC;AACzD,OAAO,EAAE,cAAc,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,aAAa,CAAC;AAC/D,OAAO,EAAE,KAAK,EAAE,YAAY,EAAE,MAAM,UAAU,CAAC;AAE/C;;;GAGG;AACH,SAAS,aAAa,CAAC,KAAyB;IAC9C,MAAM,QAAQ,GAAG,KAAK,CAAC,OAAO,EAAE,CAAC,iBAAiB,CAAC,IAAI,CAAC,CAAC;IACzD,OAAO,UAAU,YAAY,CAAC,KAAK,CAAC,GAAG,QAAQ,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,QAAQ,EAAE,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC;AAC9E,CAAC;AAED;;;GAGG;AACH,MAAM,CAAC,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,EAAE,EAAE,GAAG,EAAE,KAAK,EAAE,GAAG,EAAE,MAAM,EAAE,EAAE,EAAE;IAC7D,MAAM,EAAE,UAAU,EAAE,GAAG,KAAK,CAAC,KAAK,CAAC,CAAC;IACpC,MAAM,YAAY,GAAG,MAAM,GAAG,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC;IAC7C,MAAM,KAAK,GAAG,MAAM,GAAG,CAAC,IAAI,CAAC,GAAG,EAAE,UAAU,EAAE,YAAY,CAAC,CAAC;IAC5D,IAAI,KAAK,CAAC,SAAS;QACjB,MAAM,IAAI,KAAK,CACb,yEAAyE,CAC1E,CAAC;IACJ,MAAM,KAAK,GAAG,MAAM,GAAG,CAAC,QAAQ,CAAC,GAAG,EAAE,UAAU,EAAE,YAAY,CAAC,CAAC;IAChE,MAAM,KAAK,GAAG,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,MAAM,CAAC;IAC5C,GAAG,CAAC,2BAA2B,KAAK,CAAC,MAAM,WAAW,KAAK,aAAa,CAAC,CAAC;IAC1E,MAAM,OAAO,GACX,KAAK,IAAI,gBAAgB;QACvB,CAAC,CAAC,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,IAAI,EAAE,KAAK,CAAC,IAAI,EAAE;QAClD,CAAC,CAAC;YACE,KAAK;YACL,KAAK;YACL,KAAK,EAAE,IAAI;YACX,QAAQ,EAAE,MAAM,SAAS,CAAC,MAAM,EAAE,YAAY,EAAE,KAAK,CAAC,IAAI,CAAC;SAC5D,CAAC;IACR,IAAI,OAAO,CAAC,KAAK;QACf,GAAG,CAAC,gDAAgD,OAAO,CAAC,QAAQ,EAAE,CAAC,CAAC;IAC1E,OAAO,EAAE,OAAO,EAAE,YAAY,EAAE,CAAC;AACnC,CAAC,CAAC,CAAC;AAEH;;;;;;GAMG;AACH,MAAM,UAAU,QAAQ,CAAC,OAAqB;IAC5C,MAAM,IAAI,GAAG,CAAC,OAAyD,EAAE,EAAE,CACzE,KAAK,CAAC,EAAE,OAAO,EAAE,MAAM,EAAE,YAAY,EAAE,OAAO,EAAE,aAAa,EAAE,OAAO,EAAE,CAAC,CAAC;IAC5E,MAAM,KAAK,GAAG,IAAI,CAAC,CAAC,EAAE,KAAK,EAAE,EAAE,EAAE;QAC/B,MAAM,EAAE,IAAI,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,EAAE,GAAG,KAAK,CAAC,KAAK,CAAC,CAAC;QACrD,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,EAAE,GAAG,cAAc,CAAC,KAAK,CAAC,EAAE,CAAC;IAC1E,CAAC,CAAC,CAAC;IACH,MAAM,OAAO,GAAG,IAAI,CAAC,CAAC,EAAE,KAAK,EAAE,EAAE,EAAE,CAAC,CAAC;QACnC,QAAQ,EAAE,KAAK,CAAC,KAAK,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC;KACvC,CAAC,CAAC,CAAC;IACJ,OAAO,OAAO,CAAC,OAAO,CAAC,MAAM,EAAE;QAC7B,GAAG,KAAK;QACR,KAAK,CAAC,GAAG,CAAC,OAAO;YACf,MAAM,KAAK,GAAG,MAAM,KAAK,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;YACvC,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,OAAO,IAAI,EAAE,CAAC,CAAC,MAAM,KAAK,CAAC;gBAAE,OAAO,KAAK,CAAC;YACpE,OAAO,OAAO,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;QAC9B,CAAC;KACF,CAAC,CAAC;AACL,CAAC;AAED,+GAA+G;AAC/G,MAAM,CAAC,MAAM,WAAW,GAAG,IAAI,CAC7B,CAAC,UAAU,EAAE,SAAS,EAAE,SAAS,CAAC,EAClC,KAAK,EAAE,EAAE,KAAK,EAAE,EAAE,EAAE;IAClB,MAAM,MAAM,GAAG,YAAY,CAAC,SAAS,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;IACpD,IAAI,CAAC,MAAM,CAAC,OAAO;QACjB,OAAO;YACL,OAAO,EAAE,SAAS;YAClB,MAAM,EAAE,+BAA+B,CAAC,CAAC,aAAa,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE;SACvE,CAAC;IACJ,MAAM,OAAO,GAAG,aAAa,CAAC,MAAM,CAAC,IAAI,EAAE,KAAK,CAAC,KAAK,CAAC,CAAC,QAAQ,CAAC,CAAC;IAClE,IAAI,OAAO;QAAE,OAAO,EAAE,OAAO,EAAE,SAAS,EAAE,MAAM,EAAE,OAAO,EAAE,CAAC;IAC5D,MAAM,OAAO,GAAG,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;IAC3C,uEAAuE;IACvE,MAAM,MAAM,GAAG;QACb,MAAM,EAAE,MAAM,CAAC,IAAI;QACnB,OAAO,EAAE,CAAC,GAAG,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,OAAO,IAAI,EAAE,CAAC,EAAE,MAAM,CAAC,IAAI,CAAC;KACxD,CAAC;IACF,oEAAoE;IACpE,IAAI,OAAO,IAAI,KAAK,CAAC,IAAI,CAAC,cAAc,KAAK,IAAI;QAC/C,OAAO;YACL,OAAO,EAAE,UAAU;YACnB,MAAM,EACJ,yEAAyE;YAC3E,IAAI,EAAE,MAAM;SACb,CAAC;IACJ,IAAI,OAAO;QACT,OAAO;YACL,OAAO,EAAE,SAAS;YAClB,MAAM,EAAE,6BAA6B;YACrC,QAAQ,EAAE,OAAO;YACjB,IAAI,EAAE,MAAM;SACb,CAAC;IACJ,OAAO;QACL,OAAO,EAAE,UAAU;QACnB,MAAM,EAAE,2CAA2C;QACnD,IAAI,EAAE,MAAM;KACb,CAAC;AACJ,CAAC,CACF,CAAC"}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import type { RunState } from "@clemrig/flow";
|
|
2
|
+
import type { FactoryDefinition } from "../../../domain/definition.js";
|
|
3
|
+
import type { Description } from "../../../domain/description.js";
|
|
4
|
+
import type { Plan } from "../../../domain/plan.js";
|
|
5
|
+
import type { Changes, Implementation, Review } from "../../../domain/review.js";
|
|
6
|
+
import type { Criterion } from "../../../domain/spec.js";
|
|
7
|
+
import type { KeptCheck } from "../../../system/run-files.js";
|
|
8
|
+
/** What the workflows use: the factory requires the budget, a workflow also runs without one. */
|
|
9
|
+
export type Settings = Pick<FactoryDefinition, "verify" | "profiles"> & {
|
|
10
|
+
maxCostUsd?: number;
|
|
11
|
+
};
|
|
12
|
+
/** What the stages record in the run's data, each once its stage has passed. */
|
|
13
|
+
export interface Facts {
|
|
14
|
+
slug: string;
|
|
15
|
+
title: string;
|
|
16
|
+
spec: string;
|
|
17
|
+
/** The spec's Objective, which the pull request recalls. */
|
|
18
|
+
objective: string;
|
|
19
|
+
criteria: Criterion[];
|
|
20
|
+
/** The repository on GitHub: its page, and whether anyone can see it. */
|
|
21
|
+
repository: {
|
|
22
|
+
url: string;
|
|
23
|
+
public: boolean;
|
|
24
|
+
};
|
|
25
|
+
/** The agents' browser, when `.factory/` installs one. */
|
|
26
|
+
browser?: string;
|
|
27
|
+
/** Where the describing agent puts its screenshots. */
|
|
28
|
+
screenshotsDir: string;
|
|
29
|
+
base: string;
|
|
30
|
+
/** The main branch's commit the run's branch starts from. */
|
|
31
|
+
baseCommit: string;
|
|
32
|
+
/** The commit of the spec a cleanup writes, once committed; an issue's spec was approved on the base. */
|
|
33
|
+
specCommit?: string;
|
|
34
|
+
specPath: string;
|
|
35
|
+
planPath: string;
|
|
36
|
+
plan: Plan;
|
|
37
|
+
planCommit: string;
|
|
38
|
+
implementation: Implementation;
|
|
39
|
+
checks: KeptCheck[];
|
|
40
|
+
/** The checks whose `when` no changed file matched. */
|
|
41
|
+
skippedChecks: string[];
|
|
42
|
+
changes: Changes;
|
|
43
|
+
/** The working tree the review reads, as a tree object: what it must leave as it found it. */
|
|
44
|
+
reviewedTree: string;
|
|
45
|
+
/** The review the run judged last: approved, or accepted by the operator. */
|
|
46
|
+
review: Review;
|
|
47
|
+
/** Every review the run judged, in order; absent from runs of an earlier factory. */
|
|
48
|
+
reviews?: Review[];
|
|
49
|
+
/** Set when the operator resumed the run to deliver it despite its review. */
|
|
50
|
+
acceptedReview?: boolean;
|
|
51
|
+
description: Description;
|
|
52
|
+
pullRequest: string;
|
|
53
|
+
}
|
|
54
|
+
export declare function facts(state: Readonly<RunState>): Facts;
|
|
55
|
+
/** The commit that holds the spec the run delivers: a cleanup's own, once committed, else the base commit. */
|
|
56
|
+
export declare function specCommitOf({ specCommit, baseCommit }: Facts): string;
|
|
57
|
+
/** Corrections the review asked for so far. */
|
|
58
|
+
export declare function reviewRounds(state: Readonly<RunState>): number;
|
|
59
|
+
export declare function short(commit: string): string;
|
|
60
|
+
export declare const message: (error: unknown) => string;
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
export function facts(state) {
|
|
2
|
+
return state.data;
|
|
3
|
+
}
|
|
4
|
+
/** The commit that holds the spec the run delivers: a cleanup's own, once committed, else the base commit. */
|
|
5
|
+
export function specCommitOf({ specCommit, baseCommit }) {
|
|
6
|
+
return specCommit ?? baseCommit;
|
|
7
|
+
}
|
|
8
|
+
/** Corrections the review asked for so far. */
|
|
9
|
+
export function reviewRounds(state) {
|
|
10
|
+
return state.retries?.["reviewed.changes"] ?? 0;
|
|
11
|
+
}
|
|
12
|
+
export function short(commit) {
|
|
13
|
+
return commit.slice(0, 7);
|
|
14
|
+
}
|
|
15
|
+
export const message = (error) => error instanceof Error ? error.message : String(error);
|
|
16
|
+
//# sourceMappingURL=run.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"run.js","sourceRoot":"","sources":["../../../../src/app/workflows/stages/run.ts"],"names":[],"mappings":"AAyDA,MAAM,UAAU,KAAK,CAAC,KAAyB;IAC7C,OAAO,KAAK,CAAC,IAAwB,CAAC;AACxC,CAAC;AAED,8GAA8G;AAC9G,MAAM,UAAU,YAAY,CAAC,EAAE,UAAU,EAAE,UAAU,EAAS;IAC5D,OAAO,UAAU,IAAI,UAAU,CAAC;AAClC,CAAC;AAED,+CAA+C;AAC/C,MAAM,UAAU,YAAY,CAAC,KAAyB;IACpD,OAAO,KAAK,CAAC,OAAO,EAAE,CAAC,kBAAkB,CAAC,IAAI,CAAC,CAAC;AAClD,CAAC;AAED,MAAM,UAAU,KAAK,CAAC,MAAc;IAClC,OAAO,MAAM,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;AAC5B,CAAC;AAED,MAAM,CAAC,MAAM,OAAO,GAAG,CAAC,KAAc,EAAE,EAAE,CACxC,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { type Log, type Stage } from "@clemrig/flow";
|
|
2
|
+
import { type VerifyCommand } from "../../../domain/verification.js";
|
|
3
|
+
import { type CheckProgress } from "../../../system/checks.js";
|
|
4
|
+
/** Journals each check as it starts, passes or is skipped: they can take minutes. */
|
|
5
|
+
export declare function journalChecks(log: Log, where?: string): (event: CheckProgress) => void;
|
|
6
|
+
/**
|
|
7
|
+
* The repository's checks on the change, a check with `when` only if a file
|
|
8
|
+
* changed since the plan matches it; a failure goes back to the implementing
|
|
9
|
+
* agent with the end of its output, the whole of it kept in the run.
|
|
10
|
+
*/
|
|
11
|
+
export declare function verify(checks: VerifyCommand[]): Stage<"passed" | "failed">;
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { code } from "@clemrig/flow";
|
|
2
|
+
import { failureFeedback, seconds, } from "../../../domain/verification.js";
|
|
3
|
+
import { runChecks } from "../../../system/checks.js";
|
|
4
|
+
import * as git from "../../../system/git.js";
|
|
5
|
+
import { keepCheckOutputs, keepFailedCheck, } from "../../../system/run-files.js";
|
|
6
|
+
import { facts } from "./run.js";
|
|
7
|
+
/** Journals each check as it starts, passes or is skipped: they can take minutes. */
|
|
8
|
+
export function journalChecks(log, where = "") {
|
|
9
|
+
return (event) => {
|
|
10
|
+
const { check } = event;
|
|
11
|
+
if (event.status === "started")
|
|
12
|
+
log(`Running ${check.name}${where}: ${check.command.join(" ")}`);
|
|
13
|
+
else if (event.status === "skipped")
|
|
14
|
+
log(`Skipped ${check.name}${where}: no change matches ${check.when?.join(", ")}`);
|
|
15
|
+
else
|
|
16
|
+
log(`${check.name} passed in ${seconds(event.durationMs)}`);
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* The repository's checks on the change, a check with `when` only if a file
|
|
21
|
+
* changed since the plan matches it; a failure goes back to the implementing
|
|
22
|
+
* agent with the end of its output, the whole of it kept in the run.
|
|
23
|
+
*/
|
|
24
|
+
export function verify(checks) {
|
|
25
|
+
return code(["passed", "failed"], async ({ dir, state, log, runDir }) => {
|
|
26
|
+
const result = await runChecks(dir, checks, await git.changedFiles(dir, facts(state).planCommit), journalChecks(log));
|
|
27
|
+
if (result.ok)
|
|
28
|
+
return {
|
|
29
|
+
outcome: "passed",
|
|
30
|
+
reason: "Every check passed",
|
|
31
|
+
data: {
|
|
32
|
+
// What each check wrote stays with the run, for the review.
|
|
33
|
+
checks: await keepCheckOutputs(runDir, result.passed),
|
|
34
|
+
skippedChecks: result.skipped.map(({ name }) => name),
|
|
35
|
+
},
|
|
36
|
+
};
|
|
37
|
+
// All it wrote, the agent receives its end only.
|
|
38
|
+
const kept = await keepFailedCheck(runDir, result.check.name, result.output);
|
|
39
|
+
log(`${result.check.name} failed: ${result.cause}; its output is in ${kept}`);
|
|
40
|
+
return {
|
|
41
|
+
outcome: "failed",
|
|
42
|
+
reason: `${result.check.name} failed: ${result.cause}`,
|
|
43
|
+
feedback: failureFeedback(result.check, result.cause, result.output),
|
|
44
|
+
};
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
//# sourceMappingURL=verify.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"verify.js","sourceRoot":"","sources":["../../../../src/app/workflows/stages/verify.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAwB,MAAM,eAAe,CAAC;AAC3D,OAAO,EACL,eAAe,EACf,OAAO,GAER,MAAM,iCAAiC,CAAC;AACzC,OAAO,EAAsB,SAAS,EAAE,MAAM,2BAA2B,CAAC;AAC1E,OAAO,KAAK,GAAG,MAAM,wBAAwB,CAAC;AAC9C,OAAO,EACL,gBAAgB,EAChB,eAAe,GAChB,MAAM,8BAA8B,CAAC;AACtC,OAAO,EAAE,KAAK,EAAE,MAAM,UAAU,CAAC;AAEjC,qFAAqF;AACrF,MAAM,UAAU,aAAa,CAC3B,GAAQ,EACR,KAAK,GAAG,EAAE;IAEV,OAAO,CAAC,KAAK,EAAE,EAAE;QACf,MAAM,EAAE,KAAK,EAAE,GAAG,KAAK,CAAC;QACxB,IAAI,KAAK,CAAC,MAAM,KAAK,SAAS;YAC5B,GAAG,CAAC,WAAW,KAAK,CAAC,IAAI,GAAG,KAAK,KAAK,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;aAC9D,IAAI,KAAK,CAAC,MAAM,KAAK,SAAS;YACjC,GAAG,CACD,WAAW,KAAK,CAAC,IAAI,GAAG,KAAK,uBAAuB,KAAK,CAAC,IAAI,EAAE,IAAI,CAAC,IAAI,CAAC,EAAE,CAC7E,CAAC;;YACC,GAAG,CAAC,GAAG,KAAK,CAAC,IAAI,cAAc,OAAO,CAAC,KAAK,CAAC,UAAU,CAAC,EAAE,CAAC,CAAC;IACnE,CAAC,CAAC;AACJ,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,MAAM,CAAC,MAAuB;IAC5C,OAAO,IAAI,CAAC,CAAC,QAAQ,EAAE,QAAQ,CAAC,EAAE,KAAK,EAAE,EAAE,GAAG,EAAE,KAAK,EAAE,GAAG,EAAE,MAAM,EAAE,EAAE,EAAE;QACtE,MAAM,MAAM,GAAG,MAAM,SAAS,CAC5B,GAAG,EACH,MAAM,EACN,MAAM,GAAG,CAAC,YAAY,CAAC,GAAG,EAAE,KAAK,CAAC,KAAK,CAAC,CAAC,UAAU,CAAC,EACpD,aAAa,CAAC,GAAG,CAAC,CACnB,CAAC;QACF,IAAI,MAAM,CAAC,EAAE;YACX,OAAO;gBACL,OAAO,EAAE,QAAQ;gBACjB,MAAM,EAAE,oBAAoB;gBAC5B,IAAI,EAAE;oBACJ,4DAA4D;oBAC5D,MAAM,EAAE,MAAM,gBAAgB,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,CAAC;oBACrD,aAAa,EAAE,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,EAAE,EAAE,EAAE,CAAC,IAAI,CAAC;iBACtD;aACF,CAAC;QACJ,iDAAiD;QACjD,MAAM,IAAI,GAAG,MAAM,eAAe,CAChC,MAAM,EACN,MAAM,CAAC,KAAK,CAAC,IAAI,EACjB,MAAM,CAAC,MAAM,CACd,CAAC;QACF,GAAG,CACD,GAAG,MAAM,CAAC,KAAK,CAAC,IAAI,YAAY,MAAM,CAAC,KAAK,sBAAsB,IAAI,EAAE,CACzE,CAAC;QACF,OAAO;YACL,OAAO,EAAE,QAAQ;YACjB,MAAM,EAAE,GAAG,MAAM,CAAC,KAAK,CAAC,IAAI,YAAY,MAAM,CAAC,KAAK,EAAE;YACtD,QAAQ,EAAE,eAAe,CAAC,MAAM,CAAC,KAAK,EAAE,MAAM,CAAC,KAAK,EAAE,MAAM,CAAC,MAAM,CAAC;SACrE,CAAC;IACJ,CAAC,CAAC,CAAC;AACL,CAAC"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { Settings } from "./stages/run.js";
|
|
2
|
+
/**
|
|
3
|
+
* Chooses the cleanup of the code that comes first, recent code first, and
|
|
4
|
+
* writes it as an issue with its spec; then plans, implements, checks and
|
|
5
|
+
* reviews it like any issue, and describes it in the pull request it opens.
|
|
6
|
+
*/
|
|
7
|
+
export declare function tidy(definition: Settings): import("@clemrig/flow").Workflow;
|