@turri-dev/cli 0.1.3-next.a5d3797 → 0.1.3-next.b675516
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/regenerate/conformance-agents.js +61 -7
- package/dist/regenerate/conformance.js +29 -22
- package/dist/regenerate/debugger.js +27 -3
- package/dist/regenerate/environment-error.js +17 -0
- package/dist/regenerate/prompt.js +3 -1
- package/dist/regenerate/regenerate.js +24 -6
- package/dist/regenerate/test-scripts.js +22 -1
- package/dist/regenerate/unit-review.js +11 -0
- package/docs/regenerating.md +28 -9
- package/package.json +3 -3
|
@@ -7,6 +7,7 @@ import { confinedTools } from "./confine.js";
|
|
|
7
7
|
import { ensureInModulesScratch, modulesScratchDir } from "./paths.js";
|
|
8
8
|
import { workspaceBlock } from "./workspace.js";
|
|
9
9
|
import { reportActivity } from "./activity.js";
|
|
10
|
+
import { runTiming, withTestingScripts } from "./test-scripts.js";
|
|
10
11
|
import { createWebTools, WEB_TOOLS } from "./web.js";
|
|
11
12
|
import { diffFiles, digestFiles } from "./codebase.js";
|
|
12
13
|
import { ERROR_CAUSE_RULES, fixMethod, INTEGRITY_RULES, specPrimer } from "./prompt.js";
|
|
@@ -377,9 +378,17 @@ export function explorePrompt(buildDir, spec, scope, testingScripts, question, p
|
|
|
377
378
|
...(testingScripts !== undefined && testingScripts.length > 0
|
|
378
379
|
? [
|
|
379
380
|
"",
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
381
|
+
...(testingScripts.some((script) => script.prepare === true)
|
|
382
|
+
? [
|
|
383
|
+
"These are the project's prepare script, which the harness runs on the build first, and its conformance testing",
|
|
384
|
+
"script, which runs only once the prepare script passes and decides whether the tests pass - read both for what",
|
|
385
|
+
"they build or install before the tests run, and so what the tests can depend on:",
|
|
386
|
+
]
|
|
387
|
+
: [
|
|
388
|
+
"This is the project's conformance testing script, which the harness runs to decide whether the",
|
|
389
|
+
"tests pass - read it for what it builds or installs before the tests run, and so what the tests can depend on:",
|
|
390
|
+
]),
|
|
391
|
+
...testingScripts.flatMap((script) => ["", `<script${script.prepare === true ? ' kind="prepare environment"' : ""} path="${script.path}" run-as="${script.runs}">`, script.content.trimEnd(), "</script>"]),
|
|
383
392
|
]
|
|
384
393
|
: []),
|
|
385
394
|
...(previous !== undefined && question === undefined
|
|
@@ -703,7 +712,7 @@ export function unfoundedEvidence(evidence, opened) {
|
|
|
703
712
|
"opened, the testing script, or the failing run - each with where. What <how to invoke it> says is not evidence: it " +
|
|
704
713
|
"may be wrong, so check it against the testing script and the files.");
|
|
705
714
|
}
|
|
706
|
-
const inPrompt = (source) => /^(the )?(conformance )?testing script$|^(the )?failing run$/i.test(source.trim());
|
|
715
|
+
const inPrompt = (source) => /^(the )?(conformance )?testing script$|^(the )?prepare script$|^(the )?failing run$/i.test(source.trim());
|
|
707
716
|
const unread = given.filter((item) => !inPrompt(item.source) && !opened(item.source.trim()));
|
|
708
717
|
if (unread.length > 0) {
|
|
709
718
|
return (`Your evidence cites ${unread.map((item) => item.source.trim()).join(", ")}, which you did not open. Open each file you cite ` +
|
|
@@ -734,7 +743,7 @@ export async function judgeFailure(options) {
|
|
|
734
743
|
description: "For code: the conformance testing script's own words for what it needs the build to have - the file, entry point or module the run failed on - copied exactly from the script",
|
|
735
744
|
})),
|
|
736
745
|
evidence: Type.Optional(Type.Array(Type.Object({
|
|
737
|
-
source: Type.String({ description: "Where: the path of a file you opened, `testing script`, or `failing run`" }),
|
|
746
|
+
source: Type.String({ description: "Where: the path of a file you opened, `testing script`, `prepare script` where there is one, or `failing run`" }),
|
|
738
747
|
shows: Type.String({ description: "What it shows, e.g. `the script runs node dist/cli.js` or `tsconfig.json emits src/cli.ts as dist/src/cli.js`" }),
|
|
739
748
|
}), { description: "What you found yourself, each with where - never what <how to invoke it> claims unchecked" })),
|
|
740
749
|
functionalities: Type.Optional(Type.Array(Type.Object({
|
|
@@ -882,6 +891,8 @@ export function judgePrompt(options) {
|
|
|
882
891
|
" the tests set up themselves - a credential, a variable or a fixture a test sets, overrides or names wrong: that is",
|
|
883
892
|
" tests; nor for a toolchain or package missing where the tests run: the agents install those. The run stops on it",
|
|
884
893
|
` once it has been judged so ${ENVIRONMENT_RAISES} times in a row; until then the tests are checked for whether they cause it.`,
|
|
894
|
+
" A run the time limit stopped is environment only when nothing hangs and it needs more time than a test run may",
|
|
895
|
+
" take - and then it stops at once.",
|
|
885
896
|
"",
|
|
886
897
|
"When the output does not show why the software failed - it answered with an error that names no cause, and",
|
|
887
898
|
"logged nothing about it - the code is what is wrong first: it hides the cause, and nobody can fix what they",
|
|
@@ -897,6 +908,12 @@ export function judgePrompt(options) {
|
|
|
897
908
|
"what is wrong. What it does with the tests, the tests have to fit; what it needs of the code - a file it runs or",
|
|
898
909
|
"checks for, a command it builds with - the code has to provide. A verdict that the script is wrong is neither",
|
|
899
910
|
"code nor tests: say which of the two has to change to get past it.",
|
|
911
|
+
...(scripts.some((script) => script.prepare === true)
|
|
912
|
+
? [
|
|
913
|
+
"The prepare script below it runs on the build before every run, and the conformance testing script only once it",
|
|
914
|
+
"passes: it binds the same way - neither agent can change it, and what it needs of the code the code provides.",
|
|
915
|
+
]
|
|
916
|
+
: []),
|
|
900
917
|
"",
|
|
901
918
|
]
|
|
902
919
|
: []),
|
|
@@ -926,13 +943,41 @@ export function judgePrompt(options) {
|
|
|
926
943
|
"",
|
|
927
944
|
`<how to invoke it>\n${options.invocation.trim()}\n</how to invoke it>`,
|
|
928
945
|
"",
|
|
929
|
-
...scripts.flatMap((script) =>
|
|
946
|
+
...scripts.flatMap((script) => {
|
|
947
|
+
const tag = script.prepare === true ? "prepare script" : "conformance testing script";
|
|
948
|
+
return [`<${tag} path="${script.path}" run-as="${script.runs}">`, script.content.trimEnd(), `</${tag}>`, ""];
|
|
949
|
+
}),
|
|
950
|
+
...(options.run.timedOut === true ? [...timedOutBlock(options.run), ""] : []),
|
|
930
951
|
`<failing run exit="${options.run.exitCode}" command="${options.run.command}">`,
|
|
952
|
+
runTiming(options.run),
|
|
953
|
+
"",
|
|
931
954
|
options.run.output.trimEnd(),
|
|
932
955
|
"</failing run>",
|
|
933
956
|
...(options.escalation !== undefined ? ["", ...escalationBlock(options.escalation)] : []),
|
|
934
957
|
].join("\n");
|
|
935
958
|
}
|
|
959
|
+
/**
|
|
960
|
+
* For the judge, of a run the time limit stopped: what kept it from finishing - something that hangs, the code's or a
|
|
961
|
+
* test's, or a suite that is only slow, which no agent can give more time.
|
|
962
|
+
*/
|
|
963
|
+
export function timedOutBlock(run) {
|
|
964
|
+
const limit = Math.round(run.timeoutMs / 1000);
|
|
965
|
+
return [
|
|
966
|
+
"<timed out>",
|
|
967
|
+
`This run did not end on its own: the time limit stopped it at ${limit}s, and it counts as failed. Its output ends where it`,
|
|
968
|
+
"was stopped - the step running then, or the one after the last it printed, is what did not finish. Decide what kept it:",
|
|
969
|
+
"- code: the software hangs - it waits on input, keeps a server in the foreground, never exits, loops, or blocks on a",
|
|
970
|
+
" lock or a call that never answers - when the tests reach it as the specification says.",
|
|
971
|
+
"- tests: a test waits for something that never comes - a process it started and never stops, output it waits for that",
|
|
972
|
+
" the software never prints, a port or a file nobody makes, a sleep or a retry with no end.",
|
|
973
|
+
"- environment: nothing hangs - the output shows the run making steady progress right up to the limit (installing,",
|
|
974
|
+
" building, test after test passing) and it needs more time than a test run may take. No agent can give it that: the",
|
|
975
|
+
" run stops at once, and the person is told to raise the limit. Say from the output how far it got, and what took",
|
|
976
|
+
" the time.",
|
|
977
|
+
"Judge from the output and the files, never from the time alone.",
|
|
978
|
+
"</timed out>",
|
|
979
|
+
];
|
|
980
|
+
}
|
|
936
981
|
/** For the judge: the tests were fixed for this failure, and it did not go away - is it the code's? */
|
|
937
982
|
function escalationBlock(escalation) {
|
|
938
983
|
const changed = escalation.changed.length === 0 ? "changed no file" : `changed ${escalation.changed.slice(0, 8).join(", ")}${escalation.changed.length > 8 ? ` and ${escalation.changed.length - 8} more` : ""}`;
|
|
@@ -976,7 +1021,10 @@ export async function createCodeFixer(options) {
|
|
|
976
1021
|
model: options.model,
|
|
977
1022
|
label: `${options.label} coding agent`, role: "coding",
|
|
978
1023
|
system: `${fence.note}\n\n${workspaceBlock({ dir: options.buildDir, hasMyDiff: options.toolNames.includes("my_diff") })}`,
|
|
979
|
-
|
|
1024
|
+
// What the conformance testing script needs of the build, the code provides: the fixer reads it, not only a judge's quote of it.
|
|
1025
|
+
task: options.testingScripts !== undefined
|
|
1026
|
+
? withTestingScripts(codeFixerTask(options), options.testingScripts.scripts, ["conformance", "unit"], options.testingScripts.root)
|
|
1027
|
+
: codeFixerTask(options),
|
|
980
1028
|
compaction: { instructions: CODER_COMPACTION },
|
|
981
1029
|
...(options.checkEnvironment !== undefined ? { checkEnvironment: options.checkEnvironment } : {}),
|
|
982
1030
|
});
|
|
@@ -1029,6 +1077,8 @@ export function codeFixPrompt(options) {
|
|
|
1029
1077
|
`<why the code>\n${options.reason.trim()}\n</why the code>`,
|
|
1030
1078
|
"",
|
|
1031
1079
|
`<failing run command="${options.run.command}">`,
|
|
1080
|
+
runTiming(options.run),
|
|
1081
|
+
"",
|
|
1032
1082
|
options.run.output.trimEnd(),
|
|
1033
1083
|
"</failing run>",
|
|
1034
1084
|
"",
|
|
@@ -1062,6 +1112,8 @@ export function escalatedFixPrompt(options) {
|
|
|
1062
1112
|
"</evidence>",
|
|
1063
1113
|
"",
|
|
1064
1114
|
`<failing run command="${options.run.command}">`,
|
|
1115
|
+
runTiming(options.run),
|
|
1116
|
+
"",
|
|
1065
1117
|
options.run.output.trimEnd(),
|
|
1066
1118
|
"</failing run>",
|
|
1067
1119
|
"",
|
|
@@ -1099,6 +1151,8 @@ export function testsFixPrompt(options) {
|
|
|
1099
1151
|
`<why the tests>\n${options.reason.trim()}\n</why the tests>`,
|
|
1100
1152
|
"",
|
|
1101
1153
|
"<failing run>",
|
|
1154
|
+
runTiming(options.run),
|
|
1155
|
+
"",
|
|
1102
1156
|
options.run.output.trimEnd(),
|
|
1103
1157
|
"</failing run>",
|
|
1104
1158
|
"",
|
|
@@ -9,12 +9,12 @@ import { abandonWork, diffSinceSnapshot, moduleDirOf, recordHistory, startWork,
|
|
|
9
9
|
import { createWorkLedger, describeAccounts, SUBMIT_WORK_NOTE, unanswered, unansweredPrompt } from "./submit-work.js";
|
|
10
10
|
import { CONFORMANCE_COMPACTION } from "./compaction.js";
|
|
11
11
|
import { NOTES_TURN, notesForSession, passedOutcome, readNotes, reflectOnReview, withNotes } from "./notes.js";
|
|
12
|
-
import { labelsPrompt, labelsPromptFor, misplacedConformanceTests, misplacedTestsPrompt, missingTestLabels } from "./test-kinds.js";
|
|
12
|
+
import { acceptanceLabel, acceptanceText, labelsPrompt, labelsPromptFor, misplacedConformanceTests, misplacedTestsPrompt, missingTestLabels } from "./test-kinds.js";
|
|
13
13
|
import { createScriptTool, findTestScripts, projectRootFor, requireTestScripts, runConformanceScript, runUnitScript, failureSignature, STUCK_ROUNDS, scriptFailure, staticChecksOnly, withTestingScripts, } from "./test-scripts.js";
|
|
14
14
|
import { createWebTools, WEB_TOOLS } from "./web.js";
|
|
15
15
|
import { createMyDiffTool } from "./self-check.js";
|
|
16
16
|
import { createServiceTools } from "./services.js";
|
|
17
|
-
import { missingToolchainError, toolchainError } from "./environment-error.js";
|
|
17
|
+
import { missingToolchainError, timeLimitError, toolchainError } from "./environment-error.js";
|
|
18
18
|
import { workspaceBlock } from "./workspace.js";
|
|
19
19
|
import { codeFixPrompt, conflictKey, escalatedFixPrompt, createCodeFixer, answerAsExplorer, createAskExplorerTool, exploreInvocation, judgeFailure, SPEC_CONFLICT_RAISES, ENVIRONMENT_RAISES, environmentCheckPrompt, createTestReviewer, testReviewRequest, reviewFeedbackPrompt, testsFixPrompt, sortedRefs, } from "./conformance-agents.js";
|
|
20
20
|
import { resolveRequiresChain } from "./requires.js";
|
|
@@ -157,9 +157,11 @@ export async function renderConformanceTests(options) {
|
|
|
157
157
|
// may read the code but writes only tests. An explorer reads the code first and writes down how the
|
|
158
158
|
// software is invoked, which is what the tests are written against.
|
|
159
159
|
const label = `${moduleName} ${options.diff !== undefined ? "spec change" : frid}`;
|
|
160
|
-
const specContext = { moduleName, text: specText(specifications, moduleName, frids, required, options.change ?? options.diff, sectionModules(tree, moduleName)) };
|
|
161
160
|
// For a spec change: the functionalities it touches, whose acceptance tests are labelled, checked and reviewed.
|
|
162
161
|
const touched = forChange ? touchedFunctionalities(tree, frids, options.diff) : [];
|
|
162
|
+
// The acceptance tests the run's tests carry out: every functionality the change touches, or the one under test.
|
|
163
|
+
const carriedOut = forChange ? touched : [{ frid: frid, acceptanceTests: specifications[ACCEPTANCE_TESTS] ?? [] }];
|
|
164
|
+
const specContext = { moduleName, text: specText(specifications, moduleName, frids, required, carriedOut, options.change ?? options.diff, sectionModules(tree, moduleName)) };
|
|
163
165
|
// Any functionality of the codebase - this module's, and those of the modules it requires. A
|
|
164
166
|
// functionality's run may raise a spec conflict only with that functionality; a spec diff's, between any two.
|
|
165
167
|
const own = (forChange ? frids : frids.slice(0, frids.indexOf(frid) + 1)).map((id) => ({ module: moduleName, frid: id }));
|
|
@@ -223,11 +225,15 @@ export async function renderConformanceTests(options) {
|
|
|
223
225
|
};
|
|
224
226
|
stage(`exploring the code for how it is invoked${options.model ? ` (${options.model})` : ""}`);
|
|
225
227
|
// The explorer reads the conformance testing script too: it is how the tests are built and run, and so how they reach the software.
|
|
228
|
+
// The prepare script, where there is one, runs on the build before it - and only once it passes does the suite run.
|
|
226
229
|
const testingScripts = [
|
|
230
|
+
...(scripts.prepare !== undefined
|
|
231
|
+
? [{ path: relative(root, scripts.prepare), runs: "<script> <copy of the build>, before every run of the conformance testing script", content: readFileSync(scripts.prepare, "utf8"), prepare: true }]
|
|
232
|
+
: []),
|
|
227
233
|
{ path: relative(root, scripts.conformance), runs: "<script> <copy of the build> <copy of the whole conformance suite>", content: readFileSync(scripts.conformance, "utf8") },
|
|
228
234
|
];
|
|
229
235
|
// The acceptance tests the explorer's plan has to carry out, step by step.
|
|
230
|
-
const toCarryOut =
|
|
236
|
+
const toCarryOut = carriedOut
|
|
231
237
|
.map((item) => ({ module: moduleName, frid: item.frid, count: item.acceptanceTests.length }))
|
|
232
238
|
.filter((item) => item.count > 0);
|
|
233
239
|
let invocation = await exploreInvocation({ buildDir, spec: specContext, scope, testingScripts, acceptance: toCarryOut, label, model: options.model, onDelta: options.onDelta });
|
|
@@ -319,6 +325,7 @@ export async function renderConformanceTests(options) {
|
|
|
319
325
|
],
|
|
320
326
|
toolNames: ["run_unit_tests", "run_conformance_tests", ...fixerServices.names, "update_notes"],
|
|
321
327
|
checkEnvironment: { run: () => runUnitScript(buildDir, scripts.unit), script: scripts.unit },
|
|
328
|
+
testingScripts: { scripts, root },
|
|
322
329
|
});
|
|
323
330
|
fixerConformanceRuns.newTurn();
|
|
324
331
|
return ask(fixer, withNotes(text, buildDir), options.onDelta);
|
|
@@ -494,19 +501,6 @@ export async function renderConformanceTests(options) {
|
|
|
494
501
|
idleFixes = 0;
|
|
495
502
|
continue;
|
|
496
503
|
}
|
|
497
|
-
// A run out of time is not a question of test or code: the tests' environment is the conformance agent's.
|
|
498
|
-
if (recheck.timedOut === true) {
|
|
499
|
-
const moved = await unmoved();
|
|
500
|
-
if (moved === "stuck")
|
|
501
|
-
break;
|
|
502
|
-
if (moved === "asked")
|
|
503
|
-
continue;
|
|
504
|
-
stage("the conformance run did not get to the tests - back to the conformance agent");
|
|
505
|
-
const fixed = await changing(() => askConformance(scriptFailure(recheck, "conformance testing"), options.onDelta));
|
|
506
|
-
response = fixed.result;
|
|
507
|
-
recordAttempt(rounds, recheck, `not a question of code or tests (${recheck.timedOut === true ? "the time limit" : `exit ${recheck.exitCode}`})`, { who: "conformance agent", files: fixed.files });
|
|
508
|
-
continue;
|
|
509
|
-
}
|
|
510
504
|
stage(`conformance tests failing - working out whether the code, the tests or the spec are wrong${judgeModel ? ` (${judgeModel})` : ""}`);
|
|
511
505
|
// A fix that changed no file leaves the next run as this one: IDLE_FIXES of them in a row, and nothing will change.
|
|
512
506
|
const idle = (files) => {
|
|
@@ -541,6 +535,14 @@ export async function renderConformanceTests(options) {
|
|
|
541
535
|
onDelta: options.onDelta,
|
|
542
536
|
});
|
|
543
537
|
const reason = verdict.reason.split("\n")[0].slice(0, 160);
|
|
538
|
+
// Stopped by the time limit while it was still getting on: no agent can give it more time. The pulse stops now -
|
|
539
|
+
// rolled back, no retry - and the person is told how to raise the limit.
|
|
540
|
+
if (verdict.culprit === "environment" && recheck.timedOut === true) {
|
|
541
|
+
stage(`the conformance run needs more than the ${Math.round(recheck.timeoutMs / 1000)}s a test run may take - stopping`);
|
|
542
|
+
if (start !== undefined)
|
|
543
|
+
abandonWork(start, moduleName, "the conformance testing script needs more time than a test run may take");
|
|
544
|
+
throw timeLimitError(recheck, { moduleName, script: scripts.conformance, kind: "conformance", reason: verdict.reason });
|
|
545
|
+
}
|
|
544
546
|
// The environment - what the run is given - is the person's to fix: once judged so twice in a row, the run stops.
|
|
545
547
|
if (verdict.culprit === "environment") {
|
|
546
548
|
environmentRaises += 1;
|
|
@@ -692,7 +694,7 @@ export async function renderConformanceTests(options) {
|
|
|
692
694
|
buildDir,
|
|
693
695
|
conformanceDir,
|
|
694
696
|
spec: specContext,
|
|
695
|
-
functionalities:
|
|
697
|
+
functionalities: carriedOut,
|
|
696
698
|
forChange,
|
|
697
699
|
invocation,
|
|
698
700
|
label,
|
|
@@ -952,9 +954,10 @@ export function failureLine(run) {
|
|
|
952
954
|
}
|
|
953
955
|
/**
|
|
954
956
|
* The spec as the explorer, the judge and the coding agent are shown it - its functional specs
|
|
955
|
-
* numbered with their ids, so a spec conflict can name them
|
|
957
|
+
* numbered with their ids, so a spec conflict can name them, and the acceptance tests the run carries
|
|
958
|
+
* out labelled as their tests are, so a verdict can quote them.
|
|
956
959
|
*/
|
|
957
|
-
function specText(specifications, moduleName, frids, required, diff, modules) {
|
|
960
|
+
function specText(specifications, moduleName, frids, required, acceptance, diff, modules) {
|
|
958
961
|
// Every functionality in the codebase, each with its module: the required modules' first, then this one's.
|
|
959
962
|
const numbered = (key, items) => key === FUNCTIONAL_REQUIREMENTS
|
|
960
963
|
? [
|
|
@@ -962,10 +965,14 @@ function specText(specifications, moduleName, frids, required, diff, modules) {
|
|
|
962
965
|
]
|
|
963
966
|
: items;
|
|
964
967
|
// Each section in its tag, as every agent is shown it (see specPrimer).
|
|
965
|
-
const sections = [DEFINITIONS, NON_FUNCTIONAL_REQUIREMENTS, TEST_REQUIREMENTS, FUNCTIONAL_REQUIREMENTS
|
|
968
|
+
const sections = [DEFINITIONS, NON_FUNCTIONAL_REQUIREMENTS, TEST_REQUIREMENTS, FUNCTIONAL_REQUIREMENTS]
|
|
966
969
|
.map((key) => [key, specifications[key] ?? []])
|
|
967
970
|
.filter(([, items]) => items.length > 0)
|
|
968
|
-
.map(([key, items]) => (key === FUNCTIONAL_REQUIREMENTS
|
|
971
|
+
.map(([key, items]) => (key === FUNCTIONAL_REQUIREMENTS ? section(key, numbered(key, items)) : moduleSection(key, items, moduleName, modules)));
|
|
972
|
+
// Not only the last functionality's: a spec change's run carries out the acceptance tests of every one it touches.
|
|
973
|
+
const tests = acceptance.flatMap((item) => item.acceptanceTests.map((test, index) => `${acceptanceLabel(item.frid, index + 1)}: ${acceptanceText(test)}`));
|
|
974
|
+
if (tests.length > 0)
|
|
975
|
+
sections.push(section(ACCEPTANCE_TESTS, tests));
|
|
969
976
|
if (diff !== undefined)
|
|
970
977
|
sections.push(specChangeBlock(diff));
|
|
971
978
|
return sections.join("\n\n");
|
|
@@ -4,6 +4,7 @@ import { Type } from "typebox";
|
|
|
4
4
|
import { ask, BEGIN, createExecutor } from "../pi.js";
|
|
5
5
|
import { confinedTools } from "./confine.js";
|
|
6
6
|
import { createWebTools, WEB_TOOLS } from "./web.js";
|
|
7
|
+
import { runTiming } from "./test-scripts.js";
|
|
7
8
|
/**
|
|
8
9
|
* A fresh look at a failure the coding agent keeps hitting.
|
|
9
10
|
*
|
|
@@ -24,9 +25,12 @@ export function diagnosisText(diagnosis) {
|
|
|
24
25
|
}
|
|
25
26
|
function debugPrompt(input) {
|
|
26
27
|
const script = input.script !== undefined && existsSync(input.script) ? readFileSync(input.script, "utf8") : undefined;
|
|
28
|
+
const timedOut = input.run?.timedOut === true;
|
|
27
29
|
return [
|
|
28
|
-
|
|
29
|
-
|
|
30
|
+
timedOut
|
|
31
|
+
? `The unit testing script of the codebase in ${input.buildDir} did not finish: the time limit stopped it. You look at it with fresh eyes.`
|
|
32
|
+
: `The unit tests of the codebase in ${input.buildDir} have failed the same way ${input.times} times in a row. The coding agent`,
|
|
33
|
+
...(timedOut ? [] : ["keeps changing things and the failure does not move: it has not seen the cause. You look at it with fresh eyes."]),
|
|
30
34
|
"",
|
|
31
35
|
"Find the root cause - the one thing that makes this failure happen - and the change that fixes it. Read the failing",
|
|
32
36
|
"run below, the tests it names, the code they exercise, the build files (pyproject.toml, package.json, go.mod, pom.xml,",
|
|
@@ -37,6 +41,21 @@ function debugPrompt(input) {
|
|
|
37
41
|
"",
|
|
38
42
|
"You change nothing - you only read. When you know, call submit_diagnosis: the root cause, with the evidence for it,",
|
|
39
43
|
"and the fix - which files, and exactly what to change in them.",
|
|
44
|
+
...(timedOut
|
|
45
|
+
? [
|
|
46
|
+
"",
|
|
47
|
+
"<timed out>",
|
|
48
|
+
"The run's output ends where the time limit stopped it: the test running then, or the step after the last one it",
|
|
49
|
+
"printed, is what did not finish. Find what kept it:",
|
|
50
|
+
"- something hangs - a test or the code waits on input, keeps a server in the foreground, never exits, loops, blocks",
|
|
51
|
+
" on a lock or a network call that never answers: the root cause and the fix, as for any failure;",
|
|
52
|
+
"- nothing hangs - the output shows the run making steady progress right up to the limit (installing, building, test",
|
|
53
|
+
" after test passing) and it needs more time than a test run may take: set too_slow, and say in root_cause from the",
|
|
54
|
+
" output how far it got and what took the time. No agent can give it more time: the work stops, and the person is",
|
|
55
|
+
" told to raise the limit. Judge from the output and the files, never from the time alone.",
|
|
56
|
+
"</timed out>",
|
|
57
|
+
]
|
|
58
|
+
: []),
|
|
40
59
|
"",
|
|
41
60
|
"<what the change is meant to do>",
|
|
42
61
|
input.task.trim(),
|
|
@@ -44,6 +63,7 @@ function debugPrompt(input) {
|
|
|
44
63
|
...(script !== undefined ? ["", `<testing script path="${input.script}">`, script.trimEnd(), "</testing script>"] : []),
|
|
45
64
|
"",
|
|
46
65
|
"<failing run>",
|
|
66
|
+
...(input.run !== undefined ? [runTiming(input.run), ""] : []),
|
|
47
67
|
input.failure.trimEnd().slice(-8000),
|
|
48
68
|
"</failing run>",
|
|
49
69
|
].join("\n");
|
|
@@ -58,10 +78,14 @@ export async function debugFailure(input) {
|
|
|
58
78
|
parameters: Type.Object({
|
|
59
79
|
root_cause: Type.String({ description: "The one thing that makes the failure happen, and the evidence for it: the lines of the run, the code, the docs" }),
|
|
60
80
|
fix: Type.String({ description: "The change that fixes it: which files, and exactly what to change in them" }),
|
|
81
|
+
too_slow: Type.Optional(Type.Boolean({
|
|
82
|
+
description: "Only for a run the time limit stopped: true when nothing hangs - it was making progress up to the limit and needs more time than a test run may take",
|
|
83
|
+
})),
|
|
61
84
|
}),
|
|
62
85
|
async execute(_id, params) {
|
|
63
86
|
const given = params;
|
|
64
|
-
|
|
87
|
+
// Too slow is a verdict on a run the limit stopped, and on no other.
|
|
88
|
+
found = { rootCause: given.root_cause, fix: given.fix, ...(given.too_slow === true && input.run?.timedOut === true ? { tooSlow: true } : {}) };
|
|
65
89
|
return { content: [{ type: "text", text: "Recorded." }], details: {}, terminate: true };
|
|
66
90
|
},
|
|
67
91
|
});
|
|
@@ -224,6 +224,23 @@ export function toolchainError(missing, details) {
|
|
|
224
224
|
source: "harness",
|
|
225
225
|
});
|
|
226
226
|
}
|
|
227
|
+
/**
|
|
228
|
+
* A testing script the time limit stopped while it was still making progress - judged so by the judge or the debugger,
|
|
229
|
+
* from where its output stops: nothing hangs, the suite needs more time than a test run may take. No agent can give it
|
|
230
|
+
* that, so the pulse stops and the person is told how to.
|
|
231
|
+
*/
|
|
232
|
+
export function timeLimitError(run, details) {
|
|
233
|
+
const limit = Math.round(run.timeoutMs / 1000);
|
|
234
|
+
return new EnvironmentError({
|
|
235
|
+
moduleName: details.moduleName,
|
|
236
|
+
missing: `more time for a test run than its ${limit}s limit`,
|
|
237
|
+
neededBy: `the ${details.kind} testing script, \`${basename(details.script)}\``,
|
|
238
|
+
shownBy: `it ran ${Math.round((run.elapsedMs ?? run.timeoutMs) / 1000)}s and was stopped at the limit, still making progress - ${details.reason.replace(/\s+/g, " ").trim().slice(0, 400)}`,
|
|
239
|
+
fix: `give a test run more time - set TURRI_COMMAND_TIMEOUT, in seconds, above ${limit} (e.g. TURRI_COMMAND_TIMEOUT=${limit * 3}) in the ` +
|
|
240
|
+
"environment turri runs in - or make the suite faster: what it installs or builds on every run, and the tests that take longest.",
|
|
241
|
+
source: "agent",
|
|
242
|
+
});
|
|
243
|
+
}
|
|
227
244
|
/** The harness's run of a testing script, when it could not find its toolchain: the error that stops the pulse. */
|
|
228
245
|
export function missingToolchainError(run, details) {
|
|
229
246
|
const missing = missingToolchain(run);
|
|
@@ -278,8 +278,10 @@ function itemText(serialized) {
|
|
|
278
278
|
return item;
|
|
279
279
|
const { markdown, [ACCEPTANCE_TESTS]: tests } = (item ?? {});
|
|
280
280
|
const lines = [markdown ?? JSON.stringify(item)];
|
|
281
|
+
// Each test is an item of the tree, its text in its markdown - as spec-steps.ts `itemText` reads it.
|
|
282
|
+
const testText = (test) => (typeof test === "string" ? test : (test?.markdown ?? JSON.stringify(test)));
|
|
281
283
|
if (Array.isArray(tests) && tests.length > 0)
|
|
282
|
-
lines.push(" <acceptance tests>", ...tests.map((test) => ` ${
|
|
284
|
+
lines.push(" <acceptance tests>", ...tests.map((test) => ` ${testText(test).replace(/\n/g, "\n ")}`), " </acceptance tests>");
|
|
283
285
|
return lines.join("\n");
|
|
284
286
|
}
|
|
285
287
|
const indent = (text, by) => text.split("\n").map((line) => `${by}${line}`).join("\n");
|
|
@@ -20,7 +20,7 @@ import { createWebTools, WEB_TOOLS } from "./web.js";
|
|
|
20
20
|
import { createCanItFailTool, createMyDiffTool, selfCheckNote } from "./self-check.js";
|
|
21
21
|
import { createServiceTools } from "./services.js";
|
|
22
22
|
import { createUnitGate } from "./unit-gate.js";
|
|
23
|
-
import { toolchainError } from "./environment-error.js";
|
|
23
|
+
import { timeLimitError, toolchainError } from "./environment-error.js";
|
|
24
24
|
import { workspaceBlock } from "./workspace.js";
|
|
25
25
|
import { ATTEMPTS, describeStop } from "./stop-report.js";
|
|
26
26
|
import { summarizeDiff } from "./job-panel.js";
|
|
@@ -333,6 +333,8 @@ export async function renderSpecDiff(options) {
|
|
|
333
333
|
let draftAsks = 0;
|
|
334
334
|
/** What the unit testing script needs and does not find, last run. */
|
|
335
335
|
let missing;
|
|
336
|
+
/** The unit testing script needs more time than a test run may take - the debugger's finding: the pulse stops on it. */
|
|
337
|
+
let slow;
|
|
336
338
|
/** The last round failed on the script's static checks alone: the unit tests passed. */
|
|
337
339
|
let staticOnly = false;
|
|
338
340
|
const testRemovals = [];
|
|
@@ -423,24 +425,35 @@ export async function renderSpecDiff(options) {
|
|
|
423
425
|
seenFailures.set(key, seen);
|
|
424
426
|
let prompt = verdict.prompt;
|
|
425
427
|
const circling = loop.stuck;
|
|
426
|
-
|
|
428
|
+
// A run the time limit stopped is looked at at once: whether something hangs, or the suite only needs more time.
|
|
429
|
+
const timedOut = verdict.run.timedOut === true;
|
|
430
|
+
if (circling || timedOut || (key !== undefined && seen >= STUCK_ROUNDS)) {
|
|
427
431
|
if (key !== undefined)
|
|
428
432
|
seenFailures.delete(key);
|
|
429
433
|
const times = circling ? Math.max(seen, loop.failing) : seen;
|
|
430
434
|
loop.reset();
|
|
431
435
|
stage(circling
|
|
432
436
|
? `the coding agent is going round in circles - ${times} failing runs - a debugger looks at it with fresh eyes`
|
|
433
|
-
:
|
|
437
|
+
: timedOut && seen < STUCK_ROUNDS
|
|
438
|
+
? `the unit testing script did not finish in ${Math.round(verdict.run.timeoutMs / 1000)}s - a debugger works out whether something hangs`
|
|
439
|
+
: `the same failure ${seen} times - a debugger looks at it with fresh eyes`);
|
|
434
440
|
const found = await debugFailure({
|
|
435
441
|
buildDir,
|
|
436
442
|
failure: verdict.run.output,
|
|
437
443
|
times,
|
|
438
444
|
task: steeredChange() ?? codingText ?? changeText ?? options.diff ?? "",
|
|
439
445
|
script: verdict.script,
|
|
446
|
+
run: verdict.run,
|
|
440
447
|
label: `${moduleName} spec change`,
|
|
441
448
|
...(debuggerModel !== undefined ? { model: debuggerModel } : {}),
|
|
442
449
|
onDelta: options.onDelta,
|
|
443
450
|
}).catch(() => undefined);
|
|
451
|
+
// Nothing hangs: the suite needs more time than a test run may take, and no agent can give it that.
|
|
452
|
+
if (found?.tooSlow === true) {
|
|
453
|
+
stage(`the unit testing script needs more than the ${Math.round(verdict.run.timeoutMs / 1000)}s a test run may take - stopping`);
|
|
454
|
+
slow = timeLimitError(verdict.run, { moduleName, script: verdict.script, kind: "unit", reason: found.rootCause });
|
|
455
|
+
return false;
|
|
456
|
+
}
|
|
444
457
|
if (found !== undefined) {
|
|
445
458
|
const cause = found.rootCause.split("\n")[0].slice(0, 240);
|
|
446
459
|
reportActivity(` the debugger: ${cause.slice(0, 160)}`);
|
|
@@ -486,6 +499,7 @@ export async function renderSpecDiff(options) {
|
|
|
486
499
|
...(options.cases !== undefined ? { cases: options.cases } : {}),
|
|
487
500
|
label: `${moduleName} spec change`,
|
|
488
501
|
...(scripts.runOn === "copy" ? { unitScript: scripts.unit } : {}),
|
|
502
|
+
testingScript: { path: relative(root, scripts.unit), content: readFileSync(scripts.unit, "utf8") },
|
|
489
503
|
model: options.judgeModel ?? modelSpec(inheritModel(configForSpec(tip.plainFile).judgeModel, modelOnly(options.model))),
|
|
490
504
|
send: async (next) => {
|
|
491
505
|
response = await ask(executor, next, options.onDelta);
|
|
@@ -543,13 +557,17 @@ export async function renderSpecDiff(options) {
|
|
|
543
557
|
stage(unitReviewStopped ? "the unit tests pass but still leave the change untested - undoing the change" : "unit tests NOT passing - undoing the change");
|
|
544
558
|
abandonWork(unit, moduleName, missing !== undefined
|
|
545
559
|
? "the unit testing script could not run"
|
|
546
|
-
:
|
|
547
|
-
? "the
|
|
548
|
-
:
|
|
560
|
+
: slow !== undefined
|
|
561
|
+
? "the unit testing script needs more time than a test run may take"
|
|
562
|
+
: staticOnly
|
|
563
|
+
? "the spec change did not pass its static checks"
|
|
564
|
+
: "the spec change did not pass its unit tests");
|
|
549
565
|
// The environment lacks what the testing script needs: the pulse stops, rolled back, and the person is told.
|
|
550
566
|
if (missing !== undefined) {
|
|
551
567
|
throw toolchainError(missing, { moduleName, script: scripts.unit, kind: "unit" });
|
|
552
568
|
}
|
|
569
|
+
if (slow !== undefined)
|
|
570
|
+
throw slow;
|
|
553
571
|
return { ...base, passed: false, stoppedAt: "unit tests", ...(staticOnly && !unitReviewStopped ? { staticChecks: true } : {}) };
|
|
554
572
|
}
|
|
555
573
|
stage("unit tests pass - landing the change on them");
|
|
@@ -335,7 +335,15 @@ async function runScript(cwd, script, args, logDir, options = {}) {
|
|
|
335
335
|
const noted = withScriptLineNote(text, script);
|
|
336
336
|
// Its failures, counts and end are what the agent is shown; the whole is kept, a read_output away.
|
|
337
337
|
const log = keepTestOutput(logDir, basename(script), noted);
|
|
338
|
-
return {
|
|
338
|
+
return {
|
|
339
|
+
command: shown(script),
|
|
340
|
+
exitCode: timedOut === true ? 124 : exitCode,
|
|
341
|
+
output: shownTestOutput(noted, log),
|
|
342
|
+
timedOut: timedOut === true,
|
|
343
|
+
timeoutMs,
|
|
344
|
+
elapsedMs: Date.now() - started,
|
|
345
|
+
...(log !== undefined ? { log } : {}),
|
|
346
|
+
};
|
|
339
347
|
}
|
|
340
348
|
/** What bash says when a line of the script itself cannot run: a command or file that is not there, or not runnable. */
|
|
341
349
|
const OWN_LINE_FAILURE = /: (No such file or directory|command not found|Permission denied)$/;
|
|
@@ -589,6 +597,19 @@ function loopNotice(loop) {
|
|
|
589
597
|
"the failure with fresh eyes, and you will be given what it finds.",
|
|
590
598
|
].join("\n");
|
|
591
599
|
}
|
|
600
|
+
/**
|
|
601
|
+
* How long a run took against its time limit, in a line: for whoever judges it or works out its cause - so a run that
|
|
602
|
+
* ended on its own is never taken for one the limit stopped, nor the other way.
|
|
603
|
+
*/
|
|
604
|
+
export function runTiming(run) {
|
|
605
|
+
const seconds = (ms) => `${(ms / 1000).toFixed(1)}s`;
|
|
606
|
+
const limit = seconds(run.timeoutMs);
|
|
607
|
+
if (run.timedOut === true) {
|
|
608
|
+
return `The time limit stopped this run: it had run ${seconds(run.elapsedMs ?? run.timeoutMs)} of the ${limit} a test run may take, and had not finished.`;
|
|
609
|
+
}
|
|
610
|
+
const took = run.elapsedMs !== undefined ? `after ${seconds(run.elapsedMs)} of the ${limit} a test run may take` : `well within the ${limit} a test run may take`;
|
|
611
|
+
return `This run ended on its own - exit ${run.exitCode}, ${took}. The time limit did not stop it and played no part in how it ended.`;
|
|
612
|
+
}
|
|
592
613
|
/** A run the time limit stopped: failed, and what most often keeps a test run from ending. */
|
|
593
614
|
function timedOutFailure(run, what) {
|
|
594
615
|
const limit = Math.round(run.timeoutMs / 1000);
|
|
@@ -63,6 +63,17 @@ export function unitReviewTask(options) {
|
|
|
63
63
|
options.spec.trim(),
|
|
64
64
|
"</specification>",
|
|
65
65
|
"",
|
|
66
|
+
...(options.testingScript !== undefined
|
|
67
|
+
? [
|
|
68
|
+
"The harness runs the unit tests with this script, and it decides whether they pass. Read it for which tests it",
|
|
69
|
+
"runs and how: a test it never reaches - a file it does not pick up, a folder it does not look in - is not run, and",
|
|
70
|
+
"checks nothing.",
|
|
71
|
+
`<unit testing script path="${options.testingScript.path}">`,
|
|
72
|
+
options.testingScript.content.trimEnd(),
|
|
73
|
+
"</unit testing script>",
|
|
74
|
+
"",
|
|
75
|
+
]
|
|
76
|
+
: []),
|
|
66
77
|
...(options.cases !== undefined ? [...codeReviewCasesBlock(options.cases), ""] : []),
|
|
67
78
|
// A job that touches no functional spec is held only to that: the checks of behaviour are not its.
|
|
68
79
|
...(mechanical ? [] : ["Check that the code does what the specification states for this work:"]),
|
package/docs/regenerating.md
CHANGED
|
@@ -160,6 +160,10 @@ runs the unit testing script itself**, and only that run counts (see
|
|
|
160
160
|
tried. An agent circles a cause it has not thought of - an in-memory database every connection opens
|
|
161
161
|
empty, a test runner that leaves the code off its path - and this is the way out. The rounds go on as
|
|
162
162
|
before, up to 20; should the same failure come round three times again, the debugger looks again.
|
|
163
|
+
**A run the time limit stopped** is looked at by the debugger at once, told how long the run had and that the limit
|
|
164
|
+
stopped it: it finds what hangs - the root cause and the fix, as above - or that nothing does, the run getting on up
|
|
165
|
+
to the limit and needing more time than a test run may take. Then the pulse stops, rolled back and not retried, and
|
|
166
|
+
the person is told to raise `TURRI_COMMAND_TIMEOUT` or make the suite faster.
|
|
163
167
|
- **It goes round in circles in its own runs.** An agent stuck on a failure can try the same few changes in
|
|
164
168
|
turn - three signatures of a stand-in function, say - and run the tests after each, dozens of times in one
|
|
165
169
|
turn. Its own `run_unit_tests` runs are watched (`createLoopWatch` in `test-scripts.ts`): the code folder back
|
|
@@ -262,7 +266,8 @@ Passing unit tests prove only what they check, and a coding agent is quick to wr
|
|
|
262
266
|
next to nothing - that a function exists, that a result is not empty, that a status is 200 - or code
|
|
263
267
|
that no test reaches. So, with `review: true` in the [config](config.md), once the unit testing script
|
|
264
268
|
passes a reviewer on the judge model reads what the work wrote, beside the spec, and changes nothing. It
|
|
265
|
-
reviews both halves
|
|
269
|
+
reviews both halves, with the unit testing script word for word in its prompt: a test the script never reaches is
|
|
270
|
+
not run, and checks nothing. The review is off by default - `review: false`, or the key left out - and so is the
|
|
266
271
|
conformance tests' review: the work goes on once its tests pass.
|
|
267
272
|
|
|
268
273
|
- **The code.** Every behaviour, edge case, validation rule and error the spec states for this work
|
|
@@ -499,8 +504,9 @@ testing script, stops before it starts and says which skill adds it.
|
|
|
499
504
|
`prepare-environment-script:` in the config, else the one `prepare_environment*.sh` at the root, else in
|
|
500
505
|
`test_scripts/` - on the build, before every run of the conformance testing script: the harness's own, and the agents'
|
|
501
506
|
`run_conformance_tests`. Both get the same build folder - the code in place, or the same fresh copy - so what it sets up
|
|
502
|
-
for that build, the conformance testing script finds. With conformance off it is not run.
|
|
503
|
-
|
|
507
|
+
for that build, the conformance testing script finds. With conformance off it is not run. Every agent shown the
|
|
508
|
+
conformance testing script - the explorer, the conformance agent, the judge, the code fixer and the repair agent - is
|
|
509
|
+
shown it too, before the conformance testing script.
|
|
504
510
|
|
|
505
511
|
**Where they run.** On this machine, as you would run them: unfenced - no sandbox, no command check, no audit - in
|
|
506
512
|
your own environment with this machine's toolchains, turri's model providers' API keys alone left out. They are yours,
|
|
@@ -832,7 +838,8 @@ coding agent that fixes the code is told the same of the tests: it can read them
|
|
|
832
838
|
Before the test author starts, an explorer - an agent in
|
|
833
839
|
the code's folder with the file tools alone: it reads the code, runs nothing and changes nothing - hands over everything
|
|
834
840
|
the author needs, in parts: how to build and run it; how the conformance tests reach it as the project's
|
|
835
|
-
conformance testing script runs them - the explorer is given the script, and
|
|
841
|
+
conformance testing script runs them - the explorer is given the script, and the prepare script before it where there
|
|
842
|
+
is one, and says what they build or
|
|
836
843
|
installs and what the tests depend on or start (a package, a module, an entry point, by its exact name);
|
|
837
844
|
how each functionality is reached from outside
|
|
838
845
|
(the command, or the route, method and the request and response fields); what it needs around it; and
|
|
@@ -915,7 +922,8 @@ culprit: what it does with the tests, the tests fit; what it needs of the build,
|
|
|
915
922
|
- **the code:** the coding agent - in the code's folder, reading the tests but never changing them - fixes it,
|
|
916
923
|
and keeps its unit tests passing. The judge quotes the spec's own words the code does not meet, or the testing
|
|
917
924
|
script's for what it needs of the build (`script_needs`: a build output, an entry point, a module) - each checked
|
|
918
|
-
by the harness against the text it quotes
|
|
925
|
+
by the harness against the text it quotes. The acceptance tests are in that text, labelled as their tests are:
|
|
926
|
+
for a spec change, those of every functional spec it touches, not only the last one's;
|
|
919
927
|
- **the tests:** the conformance agent corrects them to what the spec says;
|
|
920
928
|
- **the spec:** two functional specs cannot both hold, whatever the code does;
|
|
921
929
|
- **the environment:** neither the code nor the tests can fix it - only the person can, in what the run is given:
|
|
@@ -957,7 +965,9 @@ spending the rest of its 20 attempts on it. Two failures are the same once what
|
|
|
957
965
|
taken out of their lines - numbers, times, paths, colour codes, and the ids a service makes up for each
|
|
958
966
|
response, a UUID or a correlation id. Nothing changes either when the agent sent to fix the code or the tests
|
|
959
967
|
changes no file: two such fixes in a row, and the run stops. The conformance agent has the testing script in its prompt,
|
|
960
|
-
and is told to fit the tests' own build to it.
|
|
968
|
+
and is told to fit the tests' own build to it. The code fixer has the testing scripts its fixes are run by - the
|
|
969
|
+
conformance testing script, the prepare script before it, and the unit testing script - word for word in its task:
|
|
970
|
+
what the conformance testing script needs of the build, it reads there, not only in the judge's quote of it.
|
|
961
971
|
|
|
962
972
|
**Escalation.** A failure judged the tests' goes to the conformance agent first: it owns the tests. When the run then
|
|
963
973
|
fails the same way - the fix changed nothing, or nothing that mattered - the judge is asked again, now whether the
|
|
@@ -978,9 +988,18 @@ the run failed with, how it was judged, and which files the agent sent to fix it
|
|
|
978
988
|
attempt's line says what came of it - and each judge reads them first, so it does not send a
|
|
979
989
|
failure the way that already failed. A run that does not pass is undone whole, its notes with it.
|
|
980
990
|
|
|
981
|
-
Then the suite runs again. A run
|
|
982
|
-
|
|
983
|
-
|
|
991
|
+
Then the suite runs again. A run that exits 80 goes back to the conformance agent without a judge - the tests pass and
|
|
992
|
+
only their static checks do not (see [the static checks](#the-static-checks)): the findings are the tests'.
|
|
993
|
+
|
|
994
|
+
**Timing.** Every run is shown to the judge, the debugger and whoever fixes it with how long it ran against its time
|
|
995
|
+
limit (`runTiming` in `test-scripts.ts`): `This run ended on its own - exit 1, after 37.2s of the 120.0s a test run
|
|
996
|
+
may take`, or `The time limit stopped this run: it had run 120.0s of the 120.0s ...` - so none takes a run that
|
|
997
|
+
ended on its own for one the limit stopped, or the other way. A run the time limit stopped is judged like any other,
|
|
998
|
+
and the judge is told what keeps a run from finishing: the code hangs (it waits on input, keeps a server in the
|
|
999
|
+
foreground, never exits) - the code's; a test waits for what never comes - the tests'; or nothing hangs, the output
|
|
1000
|
+
showing the run getting on up to the limit, and it needs more time than a test run may take - the environment. That
|
|
1001
|
+
verdict stops the pulse at once, rolled back and not retried, and the person is told to raise `TURRI_COMMAND_TIMEOUT`
|
|
1002
|
+
or make the suite faster - no agent can give a run more time. A run whose script cannot find its
|
|
984
1003
|
toolchain stops the pulse instead - see [when the environment lacks something](#when-the-environment-lacks-something).
|
|
985
1004
|
Unit tests failing after the suite passes go to the coding agent. It goes round up to 20 times; still failing, the work is undone.
|
|
986
1005
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@turri-dev/cli",
|
|
3
|
-
"version": "0.1.3-next.
|
|
3
|
+
"version": "0.1.3-next.b675516",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"repository": {
|
|
@@ -33,8 +33,8 @@
|
|
|
33
33
|
"grammar:sync": "node scripts/sync-plyn-grammar.mjs"
|
|
34
34
|
},
|
|
35
35
|
"dependencies": {
|
|
36
|
-
"@earendil-works/pi-coding-agent": "^1.
|
|
37
|
-
"@earendil-works/pi-tui": "^1.
|
|
36
|
+
"@earendil-works/pi-coding-agent": "^1.2.0",
|
|
37
|
+
"@earendil-works/pi-tui": "^1.2.0",
|
|
38
38
|
"@langchain/langgraph": "1.4.15",
|
|
39
39
|
"gray-matter": "^4.0.3",
|
|
40
40
|
"js-yaml": "^5.4.2",
|