@turri-dev/cli 0.1.27-next.763d091 → 0.1.27
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.js +2 -2
- package/dist/regenerate/cases.js +1 -1
- package/dist/regenerate/conformance.d.ts +0 -7
- package/dist/regenerate/conformance.js +1 -41
- package/dist/regenerate/loop.d.ts +0 -7
- package/dist/regenerate/loop.js +21 -19
- package/dist/regenerate/test-kinds.d.ts +0 -5
- package/dist/regenerate/test-kinds.js +0 -18
- package/docs/config.md +2 -2
- package/docs/regenerating.md +5 -5
- package/docs/using.md +2 -2
- package/genome/rules/turri-regeneration.md +2 -1
- package/package.json +1 -1
package/dist/extension.js
CHANGED
|
@@ -1364,14 +1364,14 @@ export function projectConfigBlock(root) {
|
|
|
1364
1364
|
...[
|
|
1365
1365
|
config.module !== undefined && of !== "stack" ? `- module: ${config.module}${where} - the module to build when the user does not name one.` : undefined,
|
|
1366
1366
|
config.conformanceTests === false
|
|
1367
|
-
? "- conformance-tests: false - conformance tests are OFF:
|
|
1367
|
+
? "- conformance-tests: false - conformance tests are OFF: no new ones are written, unless the user asks for them in this conversation. The ones there are are kept up to date: a functional spec that has conformance tests and is regenerated - changed, regenerated again or taken out - gets a conformance pulse for it alone once its code lands. A functional spec with none gets none."
|
|
1368
1368
|
: config.conformanceTests === "chosen"
|
|
1369
1369
|
? "- conformance-tests: chosen - conformance tests are on for the functional specs chosen: those with acceptance tests always, those the chooser picks as each code pulse ends from what it regenerated, and those the user asks for with conform. A conformance pulse starts once the module's code is built, before anything is built on it. Once chosen, a functional spec keeps them."
|
|
1370
1370
|
: config.conformanceTests === "module"
|
|
1371
1371
|
? "- conformance-tests: module - conformance tests are on for the functional specs chosen: those with acceptance tests always, those the chooser picks - and plans into pulses - once the whole module is regenerated, and those the user asks for with conform. They run before anything is built on the module. Once chosen, a functional spec keeps them."
|
|
1372
1372
|
: config.conformanceTests === true
|
|
1373
1373
|
? "- conformance-tests: all - conformance tests are on, for every functional spec of every module that has test reqs."
|
|
1374
|
-
: "- conformance-tests: not set - conformance tests are OFF, as with false:
|
|
1374
|
+
: "- conformance-tests: not set - conformance tests are OFF, as with false: no new ones are written, unless the user asks for them in this conversation; the ones there are are kept up to date - a functional spec that has them and is regenerated gets a conformance pulse for it alone once its code lands.",
|
|
1375
1375
|
config.review === true
|
|
1376
1376
|
? "- review: true - the code and its tests are reviewed once they pass: two reviews at most, the arbiter weighing the second."
|
|
1377
1377
|
: `- review: ${config.review === false ? "false" : "not set, so off"} - the code and its tests are not reviewed: a pulse lands once its tests pass.`,
|
package/dist/regenerate/cases.js
CHANGED
|
@@ -65,7 +65,7 @@ export function conformanceCasesBlock(cases) {
|
|
|
65
65
|
lines.push(`- Changed - its text, its acceptance tests, or a concept it uses: ${named(cases.patch)}.`, " Its conformance tests exist: rewrite them to what it says now. The tests of everything else stay as they are.");
|
|
66
66
|
}
|
|
67
67
|
if (cases.again.length > 0) {
|
|
68
|
-
lines.push(`- Regenerated again - its code was built anew, and its conformance tests were written against the code it replaced: ${named(cases.again)}.`, " Its text did not change, but the code did
|
|
68
|
+
lines.push(`- Regenerated again - its code was built anew, and its conformance tests were written against the code it replaced: ${named(cases.again)}.`, " Its text did not change, but the code did. Run its tests against the new code. Those that pass are tests of the", " new code too - conformance tests are black box - and are not rewritten. Fix what fails against the", " specification: what a failing test checks that the specification says stays checked, the test changed only as", " the new code needs it reached; what it checks that the specification leaves open - a path, a field, a message,", " a format the old code chose and the specification never names - is the new code's choice now: rewrite that", " check to what the new code does, or take it out. No test may hold the new code to what only the old code did.", " The tests of everything else stay as they are.");
|
|
69
69
|
}
|
|
70
70
|
if (cases.delete.length > 0)
|
|
71
71
|
lines.push(`- Taken out - remove its conformance tests: ${named(cases.delete)}.`);
|
|
@@ -109,11 +109,6 @@ export interface RenderConformanceTestsResult {
|
|
|
109
109
|
};
|
|
110
110
|
/** The tests were sent back to be written and labelled, and never were: what was missing the last time. */
|
|
111
111
|
unlabelled?: string[];
|
|
112
|
-
/**
|
|
113
|
-
* Of the functional specs the job regenerates again, those whose tests this run wrote - a test file labelled with it
|
|
114
|
-
* added or changed. Only these have tests for the new code; the rest are still marked to regenerate again.
|
|
115
|
-
*/
|
|
116
|
-
rewritten?: string[];
|
|
117
112
|
/** The tests were optimised as far as REVIEW_ROUNDS reviews could take them: what the last one still suggested. */
|
|
118
113
|
reviewWaived?: ReviewProblem[];
|
|
119
114
|
/** The suite passed, but the tests never passed review: what the review last found. */
|
|
@@ -131,8 +126,6 @@ export interface RenderConformanceTestsResult {
|
|
|
131
126
|
* and this refuses to start without one.
|
|
132
127
|
*/
|
|
133
128
|
export declare function renderConformanceTests(options: RenderConformanceTestsOptions): Promise<RenderConformanceTestsResult>;
|
|
134
|
-
/** Sent back when the tests of a functional spec regenerated again are as they were: they are to be written for the new code. */
|
|
135
|
-
export declare function againPrompt(frids: string[]): string;
|
|
136
129
|
/**
|
|
137
130
|
* Why conformance tests did not land, in a line. Tests sent back to be labelled and never labelled may well pass: the
|
|
138
131
|
* line says so, rather than that they failed.
|
|
@@ -10,7 +10,7 @@ import { firstLine, record } from "./ledger.js";
|
|
|
10
10
|
import { createWorkLedger, describeAccounts, SUBMIT_WORK_NOTE, unanswered, unansweredPrompt } from "./submit-work.js";
|
|
11
11
|
import { CONFORMANCE_COMPACTION } from "./compaction.js";
|
|
12
12
|
import { NOTES_TURN, notesForSession, passedOutcome, readNotes, reflectOnReview, withNotes } from "./notes.js";
|
|
13
|
-
import { acceptanceLabel, acceptanceText,
|
|
13
|
+
import { acceptanceLabel, acceptanceText, labelsPrompt, labelsPromptFor, misplacedConformanceTests, misplacedTestsPrompt, missingTestLabels } from "./test-kinds.js";
|
|
14
14
|
import { createScriptTool, findTestScripts, projectRootFor, requireTestScripts, runConformanceScript, runUnitScript, failureSignature, STUCK_ROUNDS, scriptFailure, staticChecksOnly, withTestingScripts, } from "./test-scripts.js";
|
|
15
15
|
import { createWebTools, WEB_TOOLS } from "./web.js";
|
|
16
16
|
import { createMyDiffTool } from "./self-check.js";
|
|
@@ -345,19 +345,6 @@ export async function renderConformanceTests(options) {
|
|
|
345
345
|
? "turri is bringing the conformance tests in line with the spec change"
|
|
346
346
|
: "turri is writing conformance tests from the spec");
|
|
347
347
|
let response = await askConformance(BEGIN, options.onDelta);
|
|
348
|
-
// The functional specs of this module the job regenerates again, and of those, the ones whose tests this run has
|
|
349
|
-
// not written yet: no test file labelled with it added or changed since the run began.
|
|
350
|
-
const againFrids = (options.cases?.again ?? []).filter((spec) => spec.module === undefined || spec.module === moduleName).map((spec) => spec.frid);
|
|
351
|
-
const rewrittenAgain = () => {
|
|
352
|
-
const changes = diffFiles(testsAtStart, digestFiles(conformanceDir));
|
|
353
|
-
const touchedFiles = new Set([...changes.added, ...changes.changed]);
|
|
354
|
-
return againFrids.filter((id) => labelledFiles(conformanceDir, id).some((file) => touchedFiles.has(file)));
|
|
355
|
-
};
|
|
356
|
-
const staleAgain = () => {
|
|
357
|
-
const done = new Set(rewrittenAgain());
|
|
358
|
-
return againFrids.filter((id) => !done.has(id));
|
|
359
|
-
};
|
|
360
|
-
let againAsks = 0;
|
|
361
348
|
const maxRounds = options.maxRounds ?? DEFAULT_MAX_ROUNDS;
|
|
362
349
|
let verified = false;
|
|
363
350
|
let rounds = 0;
|
|
@@ -418,20 +405,6 @@ export async function renderConformanceTests(options) {
|
|
|
418
405
|
continue;
|
|
419
406
|
}
|
|
420
407
|
}
|
|
421
|
-
// Regenerated again: its tests were written against the code it replaced, and their passing says nothing of the new
|
|
422
|
-
// code - they pass on what only the old code did as readily. Its tests have to be written again in this run.
|
|
423
|
-
const stale = staleAgain();
|
|
424
|
-
if (stale.length > 0) {
|
|
425
|
-
if (againAsks >= LABEL_ASKS) {
|
|
426
|
-
stage(`the tests of functionalit${stale.length === 1 ? "y" : "ies"} ${stale.join(", ")}, regenerated again, were sent back ${LABEL_ASKS} times to be written again, and still are not - stopping`);
|
|
427
|
-
unlabelled = stale.map((id) => `The tests of functionality ${id}, regenerated again, were not written again for the new code: no test file labelled with it changed.`);
|
|
428
|
-
break;
|
|
429
|
-
}
|
|
430
|
-
againAsks += 1;
|
|
431
|
-
stage(`the tests of functionalit${stale.length === 1 ? "y" : "ies"} ${stale.join(", ")}, regenerated again, are as they were - the conformance agent writes them again`);
|
|
432
|
-
response = await askConformance(againPrompt(stale), options.onDelta);
|
|
433
|
-
continue;
|
|
434
|
-
}
|
|
435
408
|
// The suite is the tests folder: a conformance test in the code folder is in the wrong place,
|
|
436
409
|
// and not the coding agent's to write.
|
|
437
410
|
const misplaced = misplacedConformanceTests(buildDir);
|
|
@@ -857,7 +830,6 @@ export async function renderConformanceTests(options) {
|
|
|
857
830
|
...(stuck !== undefined ? { stuck } : {}),
|
|
858
831
|
...(escalation !== undefined ? { escalation } : {}),
|
|
859
832
|
...(unlabelled !== undefined ? { unlabelled } : {}),
|
|
860
|
-
...(againFrids.length > 0 ? { rewritten: rewrittenAgain() } : {}),
|
|
861
833
|
...(reviewWaived !== undefined ? { reviewWaived } : {}),
|
|
862
834
|
...(verified && green !== undefined ? { green } : {}),
|
|
863
835
|
...(!verified && lastReview !== undefined ? { review: lastReview } : {}),
|
|
@@ -870,18 +842,6 @@ export async function renderConformanceTests(options) {
|
|
|
870
842
|
await fixerServices.stopAll();
|
|
871
843
|
}
|
|
872
844
|
}
|
|
873
|
-
/** Sent back when the tests of a functional spec regenerated again are as they were: they are to be written for the new code. */
|
|
874
|
-
export function againPrompt(frids) {
|
|
875
|
-
const which = frids.length === 1 ? `functionality ${frids[0]}` : `functionalities ${frids.join(", ")}`;
|
|
876
|
-
return [
|
|
877
|
-
`${which[0].toUpperCase()}${which.slice(1)} ${frids.length === 1 ? "is" : "are"} regenerated again: ${frids.length === 1 ? "its" : "their"} code was built anew, and no test file labelled with ${frids.length === 1 ? "it" : "them"} has changed.`,
|
|
878
|
-
"Tests that pass prove nothing here: they were written against the code it replaced, and pass on what only the old",
|
|
879
|
-
"code did as readily as on what the specification says. Read the new code, and write the tests again for it: what",
|
|
880
|
-
"the specification says, checked as the new code is reached; what it leaves open - a path, a field, a message, a",
|
|
881
|
-
"format the old code chose - checked as the new code does it, or taken out. Keep each label. The tests of",
|
|
882
|
-
"everything else stay as they are.",
|
|
883
|
-
].join("\n");
|
|
884
|
-
}
|
|
885
845
|
/**
|
|
886
846
|
* Why conformance tests did not land, in a line. Tests sent back to be labelled and never labelled may well pass: the
|
|
887
847
|
* line says so, rather than that they failed.
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { type MarshalledPlainSource } from "../plain/spec.ts";
|
|
2
1
|
import { type ChunkAgents } from "./chunk-plan.ts";
|
|
3
2
|
import { type SpecGap } from "./notes.ts";
|
|
4
3
|
import { type Job, type JobRegistry, type PulseCases } from "./jobs.ts";
|
|
@@ -388,12 +387,6 @@ export declare function describeModules(statuses: ModuleStatus[], options?: {
|
|
|
388
387
|
}): string;
|
|
389
388
|
/** The functional specs a pulse covers, by case, each with the module whose spec it is: a host's to light and mark. */
|
|
390
389
|
export declare function pulseCases(cases: JobCases, moduleName: string): PulseCases;
|
|
391
|
-
/**
|
|
392
|
-
* What a conformance job records its tests are written for: its step's tree, but a functional spec of the module it
|
|
393
|
-
* regenerates again keeps its mark unless the run wrote its tests - tests that passed as they were are still the old
|
|
394
|
-
* code's, and still conformance work.
|
|
395
|
-
*/
|
|
396
|
-
export declare function keepAgainMarks(tree: MarshalledPlainSource, cases: JobCases, moduleName: string, rewritten: string[]): MarshalledPlainSource;
|
|
397
390
|
/**
|
|
398
391
|
* A module's first pulse when it builds one functional spec - the project set up and the smallest thing that runs: it lands on its
|
|
399
392
|
* unit tests alone, with no review. A reviewer, and an arbiter after it, on a scaffold is time spent before anything can
|
package/dist/regenerate/loop.js
CHANGED
|
@@ -236,16 +236,19 @@ export function createWorkLoop(options) {
|
|
|
236
236
|
* Which functional specs the conformance tests are for: every one - unless the config has them for the chosen ones,
|
|
237
237
|
* or the conversation named some over a config that has them off. Then: those named, those with acceptance tests
|
|
238
238
|
* when the config chooses, and those tested already - a choice is kept, and tests are never taken out because
|
|
239
|
-
* other functional specs were named.
|
|
239
|
+
* other functional specs were named. Off in the config and not asked for: those tested already alone - their tests
|
|
240
|
+
* kept up to date, and none written for any other.
|
|
240
241
|
*/
|
|
241
242
|
const chosenOf = (state, spec, tests) => {
|
|
242
243
|
const named = state.conformance?.chosen;
|
|
243
244
|
const choosing = configChosen(tipEntry(state).plainFile);
|
|
245
|
+
const tested = new Set((tests?.[FUNCTIONAL_REQUIREMENTS] ?? []).map((item) => item.frid));
|
|
246
|
+
if (state.conformance === undefined && !configConformance(tipEntry(state).plainFile))
|
|
247
|
+
return (frid) => tested.has(frid);
|
|
244
248
|
if (!choosing && (state.conformance === undefined || named === undefined || configConformance(tipEntry(state).plainFile)))
|
|
245
249
|
return undefined;
|
|
246
250
|
if (choosing && state.conformance !== undefined && named === undefined)
|
|
247
251
|
return undefined;
|
|
248
|
-
const tested = new Set((tests?.[FUNCTIONAL_REQUIREMENTS] ?? []).map((item) => item.frid));
|
|
249
252
|
const specs = spec.tree[FUNCTIONAL_REQUIREMENTS] ?? [];
|
|
250
253
|
const accepted = (frid) => (specs[Number(frid) - 1]?.[ACCEPTANCE_TESTS] ?? []).length > 0;
|
|
251
254
|
return (frid) => tested.has(frid) || named?.has(frid) === true || (choosing && accepted(frid));
|
|
@@ -326,10 +329,21 @@ export function createWorkLoop(options) {
|
|
|
326
329
|
return [];
|
|
327
330
|
});
|
|
328
331
|
};
|
|
329
|
-
/**
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
332
|
+
/**
|
|
333
|
+
* Whether a codebase's conformance tests are written: asked for, or on in the config - with test reqs to write them
|
|
334
|
+
* from, and code to test. Off and not asked for, the tests there are are still kept up to date: wanted when a
|
|
335
|
+
* functional spec that has conformance tests was added, changed, regenerated again or taken out in the code - the
|
|
336
|
+
* work then is for those alone (chosenOf). Work for the test reqs or definitions alone does not start it.
|
|
337
|
+
*/
|
|
338
|
+
const conformanceWanted = (state, spec) => {
|
|
339
|
+
if ((spec.tree[TEST_REQUIREMENTS] ?? []).length === 0 || readRecord(tipEntry(state).buildDir) === null)
|
|
340
|
+
return false;
|
|
341
|
+
if (state.conformance !== undefined || configConformance(tipEntry(state).plainFile))
|
|
342
|
+
return true;
|
|
343
|
+
const tested = testedOf(state);
|
|
344
|
+
const cases = casesOf(tested, workOf("conformance", tested, state, spec), { module: tipEntry(state).moduleName });
|
|
345
|
+
return cases.add.length + cases.patch.length + cases.again.length + cases.delete.length > 0;
|
|
346
|
+
};
|
|
333
347
|
/**
|
|
334
348
|
* The functional specs a conformance job is scheduled for, as the spec has them now - refused when one is not in
|
|
335
349
|
* the spec, or the module has no test reqs to write tests from: no code job would build it, or no test be written.
|
|
@@ -1679,7 +1693,7 @@ const defaultConformanceRunner = async (job, onDelta) => {
|
|
|
1679
1693
|
onDelta,
|
|
1680
1694
|
});
|
|
1681
1695
|
if (result.passed) {
|
|
1682
|
-
writeTestsRecord(result.conformanceDir,
|
|
1696
|
+
writeTestsRecord(result.conformanceDir, job.planned.step.tree);
|
|
1683
1697
|
landWork(job.buildDir, `conformance tests: ${job.planned.title}`);
|
|
1684
1698
|
}
|
|
1685
1699
|
return { landed: result.passed, report: describeSpecDiffConformance(result), ...(result.specGaps !== undefined ? { specGaps: result.specGaps } : {}) };
|
|
@@ -1689,18 +1703,6 @@ export function pulseCases(cases, moduleName) {
|
|
|
1689
1703
|
const of = (specs) => specs.map((spec) => ({ module: spec.module ?? moduleName, frid: spec.frid }));
|
|
1690
1704
|
return { add: of(cases.add), patch: of(cases.patch), again: of(cases.again), delete: of(cases.delete) };
|
|
1691
1705
|
}
|
|
1692
|
-
/**
|
|
1693
|
-
* What a conformance job records its tests are written for: its step's tree, but a functional spec of the module it
|
|
1694
|
-
* regenerates again keeps its mark unless the run wrote its tests - tests that passed as they were are still the old
|
|
1695
|
-
* code's, and still conformance work.
|
|
1696
|
-
*/
|
|
1697
|
-
export function keepAgainMarks(tree, cases, moduleName, rewritten) {
|
|
1698
|
-
const stale = new Set(cases.again.filter((spec) => (spec.module ?? moduleName) === moduleName && !rewritten.includes(spec.frid)).map((spec) => spec.frid));
|
|
1699
|
-
if (stale.size === 0)
|
|
1700
|
-
return tree;
|
|
1701
|
-
const items = tree[FUNCTIONAL_REQUIREMENTS] ?? [];
|
|
1702
|
-
return { ...tree, [FUNCTIONAL_REQUIREMENTS]: items.map((item) => (item.frid !== undefined && stale.has(item.frid) ? { ...item, [REGENERATE_AGAIN]: true } : item)) };
|
|
1703
|
-
}
|
|
1704
1706
|
/**
|
|
1705
1707
|
* A module's first pulse when it builds one functional spec - the project set up and the smallest thing that runs: it lands on its
|
|
1706
1708
|
* unit tests alone, with no review. A reviewer, and an arbiter after it, on a scaffold is time spent before anything can
|
|
@@ -21,11 +21,6 @@ export declare function acceptanceText(item: string): string;
|
|
|
21
21
|
* test file carries it, and each acceptance test no test is labelled as implementing.
|
|
22
22
|
*/
|
|
23
23
|
export declare function missingTestLabels(testsDir: string, frid: string, acceptanceTests: string[]): string[];
|
|
24
|
-
/**
|
|
25
|
-
* The test files that carry a functionality's labels - its conformance tests' or any of its acceptance tests'. The
|
|
26
|
-
* harness's own files beside them - the notes, the record - are not tests, whatever they mention.
|
|
27
|
-
*/
|
|
28
|
-
export declare function labelledFiles(testsDir: string, frid: string): string[];
|
|
29
24
|
/** Sent back when the labels are missing: which, and how to label them. */
|
|
30
25
|
export declare function labelsPrompt(frid: string, missing: string[], empty?: boolean, hasAcceptance?: boolean): string;
|
|
31
26
|
/**
|
|
@@ -54,24 +54,6 @@ export function missingTestLabels(testsDir, frid, acceptanceTests) {
|
|
|
54
54
|
});
|
|
55
55
|
return missing;
|
|
56
56
|
}
|
|
57
|
-
/**
|
|
58
|
-
* The test files that carry a functionality's labels - its conformance tests' or any of its acceptance tests'. The
|
|
59
|
-
* harness's own files beside them - the notes, the record - are not tests, whatever they mention.
|
|
60
|
-
*/
|
|
61
|
-
export function labelledFiles(testsDir, frid) {
|
|
62
|
-
const id = frid.replace(/\./g, "\\.");
|
|
63
|
-
const label = new RegExp(`(conformance tests|acceptance test \\d+) of functionality ${id}(?![0-9.]*[0-9])`);
|
|
64
|
-
return listFiles(testsDir).filter((file) => {
|
|
65
|
-
if (file.split(/[\\/]/).at(-1).startsWith(".turri"))
|
|
66
|
-
return false;
|
|
67
|
-
try {
|
|
68
|
-
return label.test(normalise(readFileSync(join(testsDir, file), "utf8")));
|
|
69
|
-
}
|
|
70
|
-
catch {
|
|
71
|
-
return false;
|
|
72
|
-
}
|
|
73
|
-
});
|
|
74
|
-
}
|
|
75
57
|
/** Sent back when the labels are missing: which, and how to label them. */
|
|
76
58
|
export function labelsPrompt(frid, missing, empty = false, hasAcceptance = true) {
|
|
77
59
|
return labelsPromptFor([{ frid, missing }], empty, hasAcceptance);
|
package/docs/config.md
CHANGED
|
@@ -83,7 +83,7 @@ models: # each agent role's model, thinking and tur
|
|
|
83
83
|
| `harness-model` | the model `turri` starts on - the one you talk to - and, in the conversation, the planner that cuts the work into jobs (on whatever model the chat is on when it plans); under `turri regenerate`, the planner too | [pi](https://pi.dev)'s configured model |
|
|
84
84
|
| `coding-model` | every regeneration: functionalities, \*\*\*plain diffs, conformance tests, repairs, planning \*\*\*plain diffs, the explorer | `harness-model` |
|
|
85
85
|
| `judge-model` | the judge and the test review of a conformance run | `coding-model` |
|
|
86
|
-
| `conformance-tests` | `all` (or `true`): conformance tests for every functional spec of every module with test reqs, written once each module's code is built, before anything is built on it. `chosen`: for the functional specs chosen - every one with acceptance tests, and those the chooser picks as each code pulse ends, from what that pulse regenerated. `module`: the same, but the chooser is asked once, when the whole module is regenerated, and plans the conformance pulses too. Under both, the choice is made inside the pulse, before anything is built on the module, and the pulse's report says what was chosen and why; those the person asks for with `conform` are written too; once chosen, a functional spec keeps them, and they are rewritten when it changes - see [who chooses, and when](regenerating.md#conformance-tests-in-the-work-loop). `false`:
|
|
86
|
+
| `conformance-tests` | `all` (or `true`): conformance tests for every functional spec of every module with test reqs, written once each module's code is built, before anything is built on it. `chosen`: for the functional specs chosen - every one with acceptance tests, and those the chooser picks as each code pulse ends, from what that pulse regenerated. `module`: the same, but the chooser is asked once, when the whole module is regenerated, and plans the conformance pulses too. Under both, the choice is made inside the pulse, before anything is built on the module, and the pulse's report says what was chosen and why; those the person asks for with `conform` are written too; once chosen, a functional spec keeps them, and they are rewritten when it changes - see [who chooses, and when](regenerating.md#conformance-tests-in-the-work-loop). `false`: no new ones written - until the conversation asks with `conform` - and the existing ones kept up to date: a functional spec that has conformance tests and is regenerated gets a conformance pulse for it alone once its code lands | `false` |
|
|
87
87
|
| `conformance-budget` | under `conformance-tests: chosen` or `module`: how many of a module's functional specs may be chosen for conformance tests, beside those with acceptance tests - a count (`2`) or a share of its functional specs (`30%`, rounded up, at least one). Every choice for the module counts, whenever it was made; the chooser is told what is left, and can choose no more. Your own `conform` is not limited. A stack's `config.turri.<stack>.yaml` can set its own | no limit |
|
|
88
88
|
| `review` | `true` or `false`: whether the code and its unit tests - and the conformance tests - are reviewed once they pass: two reviews at most, the second weighed by the arbiter; off, a job lands once its tests pass. A module's first pulse of one functional spec - the project set up and the smallest thing that runs - lands on its unit tests with no review - see [regenerating the code](regenerating.md#the-code-and-its-unit-tests-are-reviewed) | `false` |
|
|
89
89
|
| `mutation-testing` | `true` or `false`: whether the coding agent and the reviewer have `can_it_fail` - one change put in the code for one run of the unit testing script, to see that a test catches it; on a copy of the code, or in place and put back after the run (see [regenerating the code](regenerating.md#checking-its-own-work)). `turri regenerate --mutation-testing false` for one run | `true` |
|
|
@@ -170,7 +170,7 @@ A job keeps the model it was queued with: choosing another holds for what is que
|
|
|
170
170
|
|
|
171
171
|
## Conformance tests
|
|
172
172
|
|
|
173
|
-
`conformance-tests:` is the one switch, and the chat and every regeneration follow it. `false`
|
|
173
|
+
`conformance-tests:` is the one switch, and the chat and every regeneration follow it. `false` writes no new conformance tests, and keeps the ones there are up to date: a functional spec that has conformance tests - it is in `tests/.turri.json` - and is added, changed, regenerated again or taken out in the code gets a conformance pulse once its code lands, for it alone, the whole suite run with it. A functional spec with none gets none, though others of its module have them, and a module with none gets no conformance pulse at all. No key is the same as `false`: conformance tests are off by default. `all` (or `true`) means on wherever a module has `***test reqs***` - a module without them has nothing to write conformance tests from, so it writes none, but still keeps the codebase's existing ones passing. Asked in the conversation (`conform`), or with `--conformance-tests all|chosen|module|false` on `turri regenerate` and `change-plain` - for that run, the file left as it is - a regeneration does otherwise.
|
|
174
174
|
|
|
175
175
|
The chat writes `conformance-tests: false` into a new project unless you ask for tests.
|
|
176
176
|
|
package/docs/regenerating.md
CHANGED
|
@@ -224,7 +224,7 @@ The same `config.turri.yaml` holds the project's other defaults - `module`, `mod
|
|
|
224
224
|
|
|
225
225
|
They are required: a build with no unit testing script, or with conformance on and no conformance testing script, stops before it starts and says which skill adds it.
|
|
226
226
|
|
|
227
|
-
**The prepare script** is optional: turri never writes one, and runs one the project has - `prepare-environment-script:` in the config, else the one `prepare_environment*.sh` at the root, else in `test_scripts/` - on the build, before every run of the conformance testing script: the harness's own, and the agents' `run_conformance_tests`. Both get the same build folder - the code in place, or the same fresh copy - so what it sets up for that build, the conformance testing script finds. With conformance off it
|
|
227
|
+
**The prepare script** is optional: turri never writes one, and runs one the project has - `prepare-environment-script:` in the config, else the one `prepare_environment*.sh` at the root, else in `test_scripts/` - on the build, before every run of the conformance testing script: the harness's own, and the agents' `run_conformance_tests`. Both get the same build folder - the code in place, or the same fresh copy - so what it sets up for that build, the conformance testing script finds. With conformance off it runs only in a conformance pulse that keeps existing tests up to date. Every agent shown the conformance testing script - the explorer, the conformance agent, the judge, the code fixer and the repair agent - is shown it too, before the conformance testing script.
|
|
228
228
|
|
|
229
229
|
**Where they run.** On this machine, as you would run them: unfenced - no sandbox, no command check, no audit - in your own environment with this machine's toolchains, turri's model providers' API keys alone left out. They are yours, and whatever they do is your doing. That is the only way the two scripts are ever run: the harness's own runs, and the agents' `run_unit_tests` and `run_conformance_tests`, which run them exactly as the harness does - through the host runner, in a process group of their own, so the time limit and a stopped job end the script and all it started. A toolchain the script needs and this machine lacks - exit 69, exit 127, or a run that ended on a `command not found` / `failed to run command`, the last line it printed (`missingToolchain`) - is yours to install. Only on that evidence: a `command not found` earlier in the output, the run going on after it and failing for its own reasons, is a warning - a shell profile's, an optional tool probed for - and the failure is the agent's to fix. A missing toolchain is yours to install, never the agent's: the agent is told to report it with `report_environment_error` rather than work around it, and the pulse stops at the harness's first run of either script - unit or conformance - that cannot find it, rolled back, saying what to install (see [when the environment lacks something](#when-the-environment-lacks-something)). The code and the tests an agent wrote run unfenced inside the script too: what an agent runs with its own shell is fenced, what your script runs is not - an accepted gap.
|
|
230
230
|
|
|
@@ -284,11 +284,11 @@ It is told, too, what to do with each functional spec the job touches, by its ca
|
|
|
284
284
|
| fresh | the module requires none and nothing is built | set up the project and its unit tests where the unit testing script runs them, then build | set up `tests/` so the conformance testing script, which brings the tests together with `code/`, runs them, then write |
|
|
285
285
|
| add | a functional spec is new | build it and write its unit tests | write its tests |
|
|
286
286
|
| patch | its text changed, or a concept it uses did | change the existing code and tests to match - never build it again from scratch | rewrite its existing tests to match |
|
|
287
|
-
| again | `regenerate` named it: it is built, and marked in the code record to be regenerated again | take its implementation out and build it anew, doing what \*\*\*plain says now - the old code already doing it is no reason to keep it; no second implementation beside the old, nothing of it left unused; its unit tests written anew. A coding agent that writes nothing is sent back twice to build it anew, and then the pulse does not land | its tests were written against the code it replaced,
|
|
287
|
+
| again | `regenerate` named it: it is built, and marked in the code record to be regenerated again | take its implementation out and build it anew, doing what \*\*\*plain says now - the old code already doing it is no reason to keep it; no second implementation beside the old, nothing of it left unused; its unit tests written anew. A coding agent that writes nothing is sent back twice to build it anew, and then the pulse does not land | its tests were written against the code it replaced: run against the new code - black box, those that pass are its tests too, and are not rewritten; one that fails is judged against \*\*\*plain like any failure: what it checks that \*\*\*plain says stays checked, and what it checks that \*\*\*plain leaves open - a path, a field, a message the old code chose - is rewritten to what the new code does, or taken out |
|
|
288
288
|
| delete | a functional spec is gone | take out its code and the tests only it had | take out its tests |
|
|
289
289
|
| mechanical | no functional spec is touched - only reqs, or definitions none uses | adapt the code; write no new test; an existing one may change only mechanically - an import, a fixture, its setup - never what it asserts | the same, for test reqs, definitions or implementation reqs alone |
|
|
290
290
|
|
|
291
|
-
**Regenerating again.** `regenerate` with functionalities named regenerates them again though their \*\*\*plain did not change - made better, on another model, after many patches. Each is marked in the code record of every codebase it is built in (`regenerate_again`, committed), and nothing else changes: the record now differs from \*\*\*plain at that item, so the work loop plans it as it plans any change. A functional spec of a module below is regenerated again where its code lives - the codebase at the tip of each chain built on it, never the module's own codebase left behind - marked among that codebase's required functional specs. The planner groups the ones regenerated again as it groups new ones - up to three a job, only those next to each other in one module together - and the job is given the definitions and implementation reqs as every job is; `<spec change>` lists each under "To regenerate again", with its text as it is now. The job that regenerates it records it as \*\*\*plain has it, without the mark. Its conformance tests are marked with it, in the tests record of the codebase whose own functional spec it is: they were written against the code being replaced, and may hold the new code to what only the old code chose - `/healthcheck` where \*\*\*plain says only that there is a health check, and the new code answers `/health`. While the code regenerates they stay as they are; once it lands, the mark is conformance work like any change, and a conformance job
|
|
291
|
+
**Regenerating again.** `regenerate` with functionalities named regenerates them again though their \*\*\*plain did not change - made better, on another model, after many patches. Each is marked in the code record of every codebase it is built in (`regenerate_again`, committed), and nothing else changes: the record now differs from \*\*\*plain at that item, so the work loop plans it as it plans any change. A functional spec of a module below is regenerated again where its code lives - the codebase at the tip of each chain built on it, never the module's own codebase left behind - marked among that codebase's required functional specs. The planner groups the ones regenerated again as it groups new ones - up to three a job, only those next to each other in one module together - and the job is given the definitions and implementation reqs as every job is; `<spec change>` lists each under "To regenerate again", with its text as it is now. The job that regenerates it records it as \*\*\*plain has it, without the mark. Its conformance tests are marked with it, in the tests record of the codebase whose own functional spec it is: they were written against the code being replaced, and may hold the new code to what only the old code chose - `/healthcheck` where \*\*\*plain says only that there is a health check, and the new code answers `/health`. While the code regenerates they stay as they are; once it lands, the mark is conformance work like any change - with `conformance-tests: false` too, its tests being there - and a conformance job runs them on the new code, in the again case above, with the whole suite. The mark means its tests have not yet run on its new code: the job drops it once the whole suite passes, and one that does not land keeps it. Until it lands, regen shows its tests as written for an earlier build, and they are not counted as conformance tested.
|
|
292
292
|
|
|
293
293
|
A functional spec is patched when a concept it uses changed as well as when its own text did: the harness follows each functional spec's concepts through the definitions. Fresh is only where nothing exists: a module that requires another starts from what it inherits, and its first job adds or patches.
|
|
294
294
|
|
|
@@ -321,7 +321,7 @@ A conformance job is a \*\*\*plain diff's conformance run: the tests of its chan
|
|
|
321
321
|
|
|
322
322
|
**Up the chain.** A module built on another starts from its code and its conformance tests, copied up, and its conformance testing script runs those with its own. So a module waits for the one below it until that one's code is built and its conformance tests - those wanted, chosen or scheduled - are written and pass; only then is its codebase started from there.
|
|
323
323
|
|
|
324
|
-
**Who chooses, and when.** With `conformance-tests: all` every functional spec is tested, and with `false` none is unless the person asks with `conform`, for the session. With `chosen` or `module`, every one with acceptance tests - the author said what to check - and those the chooser picks: an agent on `harness-model` (or `models: chooser`), in the chat and on the command line alike. It reads the module's \*\*\*plain and names those worth black-box tests - what a user or a client relies on, not a detail the unit tests cover - and why.
|
|
324
|
+
**Who chooses, and when.** With `conformance-tests: all` every functional spec is tested, and with `false` none is unless the person asks with `conform`, for the session - but the tests there are are kept up to date: a functional spec in the tests record that a code pulse added, changed, regenerated again or took out gets a conformance pulse once its code lands, for it alone. Work for the test reqs or definitions alone starts none, and a functional spec with no conformance tests never gets them so. A module waits for that pulse below it, and drift shows it, as any conformance work. With `chosen` or `module`, every one with acceptance tests - the author said what to check - and those the chooser picks: an agent on `harness-model` (or `models: chooser`), in the chat and on the command line alike. It reads the module's \*\*\*plain and names those worth black-box tests - what a user or a client relies on, not a detail the unit tests cover - and why.
|
|
325
325
|
|
|
326
326
|
- `chosen`: it is asked as each code pulse ends, as the pulse's last stage, of what that pulse regenerated that no conformance test is for.
|
|
327
327
|
- `module`: it is asked once, when the module's last code pulse lands, of every functional spec of the module that is built and has none - and it plans them too, into groups, each a conformance pulse of its own.
|
|
@@ -473,7 +473,7 @@ What goes into them:
|
|
|
473
473
|
- **A code job landed:** the code record becomes the \*\*\*plain description the job ends on.
|
|
474
474
|
- **A conformance job landed:** the tests record becomes what its tests are written for.
|
|
475
475
|
- **A module started from another's code:** that code's record, as a lower module's - see [where things go](#where-things-go) - and an empty tests record: the tests it inherits are the module below's.
|
|
476
|
-
- **A functional spec named to `regenerate`:** it is marked to be regenerated again in the code record, so the next job regenerates it again; that job's record drops the mark. Its conformance tests, when it has them, are marked in the tests record too, and the conformance job that
|
|
476
|
+
- **A functional spec named to `regenerate`:** it is marked to be regenerated again in the code record, so the next job regenerates it again; that job's record drops the mark. Its conformance tests, when it has them, are marked in the tests record too, and the conformance job that runs them on the new code drops that mark once the whole suite passes.
|
|
477
477
|
|
|
478
478
|
A unit of work - a code job, a conformance job - lands whole or is undone whole. It starts from the module's last commit, putting back whatever an interrupted run left; a module with no commit yet is given one first. When it does not pass, the module goes back to that commit: its changes, the files it added and any commit it made on the way are gone, with the record they came with. Installed dependencies and caches stay. So the code, its tests and the records of what they were built from never disagree.
|
|
479
479
|
|
package/docs/using.md
CHANGED
|
@@ -88,7 +88,7 @@ A regeneration takes minutes, so in the TUI every tool that regenerates starts t
|
|
|
88
88
|
- **What these commands show you, the chat knows.** What `/regenerate` and `/halt activated` tell you - \*\*\*plain that does not parse, the pulses planned, a server halted - stays in the chat under the command, and since a slash command runs outside the conversation, it is put in the conversation as well: say "fix it" next and turri knows what you are looking at. Once `/regenerate` has started pulses, turri says so in the chat too - what runs and what is planned, and that you need not wait for it - without starting anything more for it; a module already up to date gets the plan's answer alone. A halted pulse reaches it as every pulse's end does.
|
|
89
89
|
- **Nothing is queued.** What is left to build is worked out from what each codebase is built from and \*\*\*plain as it is now - again whenever a pulse ends or \*\*\*plain changes - and the first pulse of it runs; see [a whole module](regenerating.md#a-whole-module). Changes that overlap never pile up: the next pulse builds \*\*\*plain as it is. The pulses planned after the one running are listed behind it, as queued, and so is each module waiting for the one it is built on - "waits for base" - until it can be planned. A change inside what the running pulse regenerates - a functional spec it regenerates reworded or given a sub-bullet, a definition or req it is given, a file they link - steers it: its agent is told, and it lands \*\*\*plain as it is now there, twice a pulse at most. Otherwise a pulse building something \*\*\*plain no longer wants is stopped and rolled back - unless `allow-to-stop-jobs: false` in `config.turri.yaml`, which has it land first and the change regenerated on top. To drop something, take it out of \*\*\*plain - one \*\*\*plain diff. Stopping a pulse leaves its change in \*\*\*plain: the codebase waits until its \*\*\*plain changes or it is asked for again.
|
|
90
90
|
- **A change to a module below** is made in the codebase at the tip of the chain, where its code lives; the codebase below is left as it was, and is brought up to its own \*\*\*plain only when a new module is to start from it. \*\*\*plain moved into a module the code was not built on stops the codebase: no change to its code brings that module in, so it has to be started over - its code and conformance tests taken out, kept in its history, and built again from the bottom of the chain. That is never done unasked: the chat asks you, saying why, and regenerates it from scratch once you agree; on the command line `turri regenerate` asks at the terminal, and `--start-over` agrees in advance.
|
|
91
|
-
- **Conformance tests are a track of their own.** A code pulse lands once its unit tests pass and are reviewed. Then, before the next one builds on it, a conformance pulse writes and runs the tests of what it built - from the test reqs and acceptance tests - with the whole suite the module inherits, and, where one fails, fixes the test or the code, whichever is wrong; what they are written for is recorded in `tests/.turri.json`. With `conformance-tests: false` none is written until you ask - `conform`, for a module or some of its functional specs. A conformance pulse that does not pass is undone whole and halts the conformance tests alone: the code goes on being built.
|
|
91
|
+
- **Conformance tests are a track of their own.** A code pulse lands once its unit tests pass and are reviewed. Then, before the next one builds on it, a conformance pulse writes and runs the tests of what it built - from the test reqs and acceptance tests - with the whole suite the module inherits, and, where one fails, fixes the test or the code, whichever is wrong; what they are written for is recorded in `tests/.turri.json`. With `conformance-tests: false` none is written until you ask - `conform`, for a module or some of its functional specs - but those there are are kept up to date: a functional spec that has conformance tests and is regenerated gets a conformance pulse for it alone. A conformance pulse that does not pass is undone whole and halts the conformance tests alone: the code goes on being built.
|
|
92
92
|
- **A pulse that fails** - its work errors, or it does not pass - is run once more; one stopped on what only you can fix - the machine lacks something, a testing script no code could pass - is not. Failing again, it stops, and so does every pulse queued behind it on the same module, without running - the other modules' pulses go on. The harness then works out why: an agent that can read the project's \*\*\*plain, code, tests, testing scripts and logs, and turri's own source, skills and rules, says what happened, why, whether the fix belongs in \*\*\*plain, the environment, the code or turri itself, and exactly what to do.
|
|
93
93
|
- **When a pulse finishes** the transcript shows its line - `✓`, `✗` or `■`, its module, kind and number, how long it took and what it cost, never what it regenerated - `✓ shorty · conformance pulse 4 finished after 3m 47s · 488.6k tok · $0.02` - and the chat, which gets its whole report, reacts then and there, whatever else is running or queued, without waiting for you to speak: at once when it is idle, else as soon as the reply it is in ends. It does what the project's guidance says to do when a pulse finishes - release what was built, say - or tells you what finished and what it means. Ask to try it ("let me try it") and the chat releases what is built to `dist/<module>` and starts it on your machine, even while other pulses run. See [deploy](deploy.md). The chat reacts to it then and there, whatever else is running or queued, without waiting for you to speak - at once when it is idle, else as soon as the reply it is in ends: it does what the project's guidance says to do when a pulse finishes (release what was built, say), or tells you in a line or two what finished.
|
|
94
94
|
- **When a pulse stops** - a gate still failing after 20 attempts - it says where it stopped, why in plain words, and what to change in \*\*\*plain - which section, and how. Running the same build again picks up from there.
|
|
@@ -97,7 +97,7 @@ In `print`, `json` and `rpc` modes the process ends when the answer is written,
|
|
|
97
97
|
|
|
98
98
|
## The project's config.turri.yaml
|
|
99
99
|
|
|
100
|
-
The chat reads the project's `config.turri.yaml` afresh every turn and follows it: its agent is told what it sets, and the tools that regenerate default to it - `module` when you ask to build without naming one, `conformance-tests: false` to regenerate without conformance tests
|
|
100
|
+
The chat reads the project's `config.turri.yaml` afresh every turn and follows it: its agent is told what it sets, and the tools that regenerate default to it - `module` when you ask to build without naming one, `conformance-tests: false` to regenerate without writing new conformance tests unless you ask for them (the ones there are kept up to date), `coding-model` for what regenerations run on, and the testing scripts. `harness-model` is what `turri` starts on. What you ask for in the conversation wins. Every key, what happens without it, and how the models inherit: [the project config](config.md).
|
|
101
101
|
|
|
102
102
|
A project whose parts are on different stacks gives each stack its own `config.turri.<stack>.yaml` beside `config.turri.yaml` - `config.turri.backend.yaml`, `config.turri.frontend.yaml` - with its testing scripts, models and conformance over the project's. A stack named after its module builds it - `config.turri.backend.yaml` builds `backend.plain` - with nothing to choose, so a backend and a frontend are each built with their own, side by side; for a stack named otherwise the chat's model passes it as `stack`, or choose one for the session with `/stack backend`. The chat is told each stack's and which is chosen, and the status bar shows it. See [stacks](config.md#stacks).
|
|
103
103
|
|
|
@@ -26,7 +26,8 @@ The other rules say what a spec means. This one says what turri does with a chan
|
|
|
26
26
|
- A definition changed patches every functional spec that uses its concept, directly or through other definitions. So does a change to a linked resource: the spec or definition linking it is changed though its text is not. A widely used definition is the costly edit - not a spec's position
|
|
27
27
|
- Implementation reqs alone, or definitions no functional spec uses, are a mechanical change: the code adapts, and no test is written or changes what it asserts
|
|
28
28
|
- Test reqs and acceptance tests reach the conformance tests only, never the code
|
|
29
|
-
- A functional spec regenerated again by name has its code taken out and built anew - never left as it was, even where it already does what the spec says; a pulse that changes nothing does not land. Its conformance tests are
|
|
29
|
+
- A functional spec regenerated again by name has its code taken out and built anew - never left as it was, even where it already does what the spec says; a pulse that changes nothing does not land. Its conformance tests, when it has them, are run on its new code once it lands: those that pass are kept as they are; one that fails is judged against the spec - what it checks that the spec leaves open, a path, a field, a message the old code chose, goes by the new code
|
|
30
|
+
- `conformance-tests: false` writes no new conformance tests, but keeps those there are up to date: a functional spec that has conformance tests and is changed, regenerated again or taken out gets a conformance pulse for it alone once its code lands. One with none gets none
|
|
30
31
|
- Definitions moved to a common module and reqs reordered are recorded, not rendered
|
|
31
32
|
- A functional spec written on its happy path and hardened later - its validation, edge cases and errors added to it - is patched in place each time: growing a spec a step at a time costs no more than writing it whole up front
|
|
32
33
|
- A module's first pulse, when it builds one functional spec - the project set up and the smallest thing that runs - lands on its unit tests with no review
|