@turri-dev/cli 0.1.27 → 0.1.28-next.d5f2680
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +6 -6
- package/dist/advisor-chat.d.ts +63 -0
- package/dist/advisor-chat.js +290 -0
- package/dist/background-skills.d.ts +53 -0
- package/dist/background-skills.js +121 -0
- package/dist/bedrock-region.d.ts +66 -0
- package/dist/bedrock-region.js +144 -0
- package/dist/btw.d.ts +1 -1
- package/dist/btw.js +1 -1
- package/dist/change-plain-cli.js +20 -13
- package/dist/chat-idle.d.ts +7 -0
- package/dist/chat-idle.js +20 -0
- package/dist/chats.d.ts +10 -0
- package/dist/chats.js +23 -0
- package/dist/cli-options.d.ts +1 -2
- package/dist/cli-options.js +2 -7
- package/dist/cli.js +11 -3
- package/dist/config-command.d.ts +14 -0
- package/dist/config-command.js +35 -15
- package/dist/config-edit.d.ts +3 -3
- package/dist/config-edit.js +16 -12
- package/dist/config.d.ts +54 -34
- package/dist/config.js +40 -44
- package/dist/conform-cli.js +12 -10
- package/dist/conformance-switch.d.ts +34 -0
- package/dist/conformance-switch.js +108 -0
- package/dist/cultivate-cli.js +15 -5
- package/dist/drift-cli.js +2 -0
- package/dist/extension.d.ts +15 -0
- package/dist/extension.js +260 -83
- package/dist/host-session.d.ts +26 -0
- package/dist/host-session.js +30 -2
- package/dist/host.d.ts +5 -2
- package/dist/host.js +7 -1
- package/dist/init-config.d.ts +1 -1
- package/dist/init-config.js +20 -11
- package/dist/loop-cli.d.ts +3 -3
- package/dist/loop-cli.js +13 -16
- package/dist/loop-tools.js +18 -10
- package/dist/model-check.d.ts +8 -3
- package/dist/model-check.js +13 -8
- package/dist/new-project.js +12 -8
- package/dist/{validate-cli.js → parse-cli.js} +11 -5
- package/dist/parse-tool.d.ts +9 -0
- package/dist/parse-tool.js +33 -0
- package/dist/pi-launch.js +1 -1
- package/dist/plain/check.d.ts +53 -6
- package/dist/plain/check.js +217 -24
- package/dist/plain/concepts.d.ts +8 -0
- package/dist/plain/concepts.js +66 -23
- package/dist/plain/cycles.d.ts +21 -0
- package/dist/plain/cycles.js +190 -0
- package/dist/plain/errors.d.ts +4 -0
- package/dist/plain/errors.js +7 -0
- package/dist/plain/files.d.ts +18 -1
- package/dist/plain/files.js +39 -2
- package/dist/plain/graph.d.ts +33 -4
- package/dist/plain/graph.js +60 -12
- package/dist/plain/highlight.d.ts +3 -2
- package/dist/plain/highlight.js +4 -3
- package/dist/plain/index.d.ts +8 -5
- package/dist/plain/index.js +8 -5
- package/dist/plain/liquid.js +1 -6
- package/dist/plain/markdown.d.ts +32 -3
- package/dist/plain/markdown.js +146 -24
- package/dist/plain/module-concepts.js +2 -2
- package/dist/plain/parser.d.ts +83 -28
- package/dist/plain/parser.js +381 -260
- package/dist/plain/plain.tmLanguage.json +29 -3
- package/dist/plain/project.d.ts +48 -0
- package/dist/plain/project.js +155 -0
- package/dist/plain/spec.d.ts +90 -28
- package/dist/plain/spec.js +107 -133
- package/dist/plain/test-plain-file.d.ts +59 -0
- package/dist/plain/test-plain-file.js +232 -0
- package/dist/regenerate/activity.d.ts +20 -9
- package/dist/regenerate/activity.js +11 -4
- package/dist/regenerate/advisor-records.d.ts +93 -0
- package/dist/regenerate/advisor-records.js +110 -0
- package/dist/regenerate/advisor-runs.d.ts +87 -0
- package/dist/regenerate/advisor-runs.js +327 -0
- package/dist/regenerate/base-folder.d.ts +68 -0
- package/dist/regenerate/base-folder.js +168 -0
- package/dist/regenerate/cases.d.ts +9 -6
- package/dist/regenerate/cases.js +70 -14
- package/dist/regenerate/change-view.d.ts +8 -3
- package/dist/regenerate/change-view.js +71 -22
- package/dist/regenerate/chunk-plan.d.ts +36 -15
- package/dist/regenerate/chunk-plan.js +165 -59
- package/dist/regenerate/conform.d.ts +3 -0
- package/dist/regenerate/conform.js +6 -5
- package/dist/regenerate/conformance-agents.d.ts +18 -39
- package/dist/regenerate/conformance-agents.js +60 -88
- package/dist/regenerate/conformance.d.ts +23 -13
- package/dist/regenerate/conformance.js +119 -82
- package/dist/regenerate/diff-view.d.ts +1 -1
- package/dist/regenerate/diff-view.js +20 -4
- package/dist/regenerate/drift.d.ts +47 -15
- package/dist/regenerate/drift.js +101 -30
- package/dist/regenerate/jobs.d.ts +2 -2
- package/dist/regenerate/jobs.js +1 -1
- package/dist/regenerate/ledger.d.ts +19 -0
- package/dist/regenerate/loop.d.ts +62 -43
- package/dist/regenerate/loop.js +472 -341
- package/dist/regenerate/paths.d.ts +4 -0
- package/dist/regenerate/paths.js +7 -1
- package/dist/regenerate/plain-palette.js +2 -0
- package/dist/regenerate/prompt.d.ts +57 -19
- package/dist/regenerate/prompt.js +157 -122
- package/dist/regenerate/records.d.ts +43 -12
- package/dist/regenerate/records.js +102 -88
- package/dist/regenerate/regenerate.js +29 -11
- package/dist/regenerate/regression.d.ts +4 -4
- package/dist/regenerate/regression.js +14 -10
- package/dist/regenerate/renames.d.ts +42 -0
- package/dist/regenerate/renames.js +185 -0
- package/dist/regenerate/requires.d.ts +1 -1
- package/dist/regenerate/requires.js +10 -3
- package/dist/regenerate/resources.d.ts +5 -3
- package/dist/regenerate/resources.js +4 -4
- package/dist/regenerate/scripts-being-written.d.ts +19 -0
- package/dist/regenerate/scripts-being-written.js +30 -0
- package/dist/regenerate/spec-map-view.d.ts +2 -2
- package/dist/regenerate/spec-map-view.js +46 -15
- package/dist/regenerate/spec-map.d.ts +27 -5
- package/dist/regenerate/spec-map.js +92 -60
- package/dist/regenerate/spec-steps.d.ts +48 -5
- package/dist/regenerate/spec-steps.js +213 -28
- package/dist/regenerate/state.d.ts +4 -3
- package/dist/regenerate/stop-report.d.ts +1 -1
- package/dist/regenerate/stop-report.js +10 -10
- package/dist/regenerate/test-advisor.d.ts +101 -0
- package/dist/regenerate/test-advisor.js +379 -0
- package/dist/regenerate/test-kinds.d.ts +71 -28
- package/dist/regenerate/test-kinds.js +110 -63
- package/dist/regenerate/test-scripts.d.ts +2 -0
- package/dist/regenerate/test-scripts.js +4 -0
- package/dist/regenerate/testing-steps.d.ts +41 -0
- package/dist/regenerate/testing-steps.js +110 -0
- package/dist/regenerate/testing.d.ts +1 -1
- package/dist/regenerate/testing.js +5 -4
- package/dist/regenerate/unit-review.d.ts +1 -1
- package/dist/regenerate/unit-review.js +3 -3
- package/dist/regenerate-cli.js +11 -12
- package/dist/release-agent.js +4 -0
- package/dist/release-cli.js +2 -0
- package/dist/release-tools.js +2 -0
- package/dist/run-report.d.ts +2 -2
- package/dist/run-report.js +4 -3
- package/dist/self-invocation.d.ts +1 -1
- package/dist/self-invocation.js +1 -1
- package/dist/sentinel.js +1 -1
- package/dist/setup.d.ts +10 -0
- package/dist/setup.js +52 -4
- package/dist/skill.d.ts +10 -0
- package/dist/skill.js +6 -0
- package/dist/skills.js +3 -1
- package/dist/spec-tools.js +21 -31
- package/dist/test-plain-files.d.ts +41 -0
- package/dist/test-plain-files.js +177 -0
- package/docs/README.md +3 -1
- package/docs/authoring-plain.md +46 -13
- package/docs/chats.md +3 -1
- package/docs/config.md +20 -15
- package/docs/deploy.md +1 -1
- package/docs/development.md +2 -1
- package/docs/headless.md +15 -18
- package/docs/hosting.md +28 -12
- package/docs/installation.md +1 -0
- package/docs/parser.md +88 -27
- package/docs/regenerating.md +87 -52
- package/docs/sentinel.md +1 -1
- package/docs/skills-and-gates.md +3 -1
- package/docs/test-advisor.md +110 -0
- package/docs/theming.md +1 -1
- package/docs/using.md +17 -15
- package/genome/rules/bullet-continuation.md +8 -8
- package/genome/rules/concise-specs-examples.md +111 -80
- package/genome/rules/concise-specs-integration-examples.md +56 -41
- package/genome/rules/concise-specs.md +17 -16
- package/genome/rules/definitions.md +5 -5
- package/genome/rules/examples/integration-embedded-testing/run_conformance_tests_java.sh +1 -1
- package/genome/rules/func-specs.md +69 -74
- package/genome/rules/impl-reqs.md +4 -4
- package/genome/rules/integration-embedded-testing.md +20 -20
- package/genome/rules/integration-embedded.md +31 -31
- package/genome/rules/integration-standalone.md +9 -9
- package/genome/rules/integrations.md +18 -18
- package/genome/rules/linked-resources.md +19 -16
- package/genome/rules/module-structure.md +21 -14
- package/genome/rules/project-model.md +18 -17
- package/genome/rules/required-concepts.md +1 -1
- package/genome/rules/requires-modules.md +6 -5
- package/genome/rules/semantic-ids.md +49 -0
- package/genome/rules/stdlib.md +4 -5
- package/genome/rules/test-plain-files.md +130 -0
- package/genome/rules/test-reqs.md +8 -8
- package/genome/rules/turri-regeneration.md +23 -8
- package/genome/skills/analyze-2-func-specs/SKILL.md +36 -21
- package/genome/skills/analyze-func-specs/SKILL.md +46 -32
- package/genome/skills/break-down-func-spec/SKILL.md +91 -67
- package/genome/skills/consolidate-concepts/SKILL.md +10 -10
- package/genome/skills/convert-old-syntax/SKILL.md +68 -0
- package/genome/skills/convert-old-syntax/references/rubric.md +11 -0
- package/genome/skills/debug-specs/SKILL.md +62 -56
- package/genome/skills/implement-conformance-testing-script/SKILL.md +19 -15
- package/genome/skills/implement-conformance-testing-script/assets/run_conformance_tests_java.sh +2 -2
- package/genome/skills/implement-conformance-testing-script/assets/run_conformance_tests_python.sh +2 -2
- package/genome/skills/implement-unit-testing-script/SKILL.md +14 -10
- package/genome/skills/refactor-module/SKILL.md +35 -35
- package/genome/skills/resolve-section-ownership/SKILL.md +9 -9
- package/genome/skills/resolve-spec-conflict/SKILL.md +35 -29
- package/package.json +7 -6
- package/dist/regenerate/choose-conformance.d.ts +0 -32
- package/dist/regenerate/choose-conformance.js +0 -117
- /package/dist/{validate-cli.d.ts → parse-cli.d.ts} +0 -0
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { readFileSync, writeFileSync } from "node:fs";
|
|
2
2
|
import { basename, dirname, join, resolve } from "node:path";
|
|
3
|
+
import { scenariosOf, scenarioText } from "./test-kinds.js";
|
|
3
4
|
import { defineTool } from "@earendil-works/pi-coding-agent";
|
|
4
5
|
import { Type } from "typebox";
|
|
5
6
|
import { ask, BEGIN, createExecutor } from "../pi.js";
|
|
@@ -10,7 +11,8 @@ import { reportActivity } from "./activity.js";
|
|
|
10
11
|
import { runTiming, withTestingScripts } from "./test-scripts.js";
|
|
11
12
|
import { createWebTools, WEB_TOOLS } from "./web.js";
|
|
12
13
|
import { diffFiles, digestFiles } from "./codebase.js";
|
|
13
|
-
import { ERROR_CAUSE_RULES, fixMethod, INTEGRITY_RULES, specPrimer } from "./prompt.js";
|
|
14
|
+
import { BASE_FOLDER_RULES, baseFolderBlock, ERROR_CAUSE_RULES, fixMethod, INTEGRITY_RULES, specPrimer } from "./prompt.js";
|
|
15
|
+
import { baseOfCodebase } from "./base-folder.js";
|
|
14
16
|
import { conformanceReviewCasesBlock } from "./cases.js";
|
|
15
17
|
import { REVIEW_LESSON } from "./notes.js";
|
|
16
18
|
import { CODER_COMPACTION, REVIEWER_COMPACTION } from "./compaction.js";
|
|
@@ -20,9 +22,8 @@ export function explorationText(exploration, replies = []) {
|
|
|
20
22
|
const sections = [
|
|
21
23
|
`## How to build and run it\n\n${exploration.buildAndRun.trim()}`,
|
|
22
24
|
`## How the conformance tests reach it, run by the testing script\n\n${exploration.testsReachIt.trim()}`,
|
|
23
|
-
`## How each functionality is reached\n\n${exploration.interfaces.map((item) => `### ${item.module} ${item.
|
|
25
|
+
`## How each functionality is reached\n\n${exploration.interfaces.map((item) => `### ${item.module} ${item.funcId}\n\n${item.how.trim()}`).join("\n\n")}`,
|
|
24
26
|
`## What it needs around it\n\n${exploration.environment.trim()}`,
|
|
25
|
-
`## Plan for the conformance tests\n\n${exploration.plan.map((item) => `### ${item.module} ${item.frid}\n\n${item.tests.trim()}`).join("\n\n")}`,
|
|
26
27
|
...(exploration.other?.trim() ? [`## Anything else\n\n${exploration.other.trim()}`] : []),
|
|
27
28
|
// An account it could not complete is passed on with what it wrote, so nothing it found is lost.
|
|
28
29
|
...(replies.length > 0 ? [`## What the explorer wrote as it worked\n\n${replies.map((reply) => reply.trim()).join("\n\n")}`] : []),
|
|
@@ -32,7 +33,7 @@ export function explorationText(exploration, replies = []) {
|
|
|
32
33
|
/**
|
|
33
34
|
* What an exploration lacks for the conformance tests to be written from it alone, or nothing when
|
|
34
35
|
* it is whole: how to build and run it, and - for every functionality the tests are for - how it
|
|
35
|
-
* is reached
|
|
36
|
+
* is reached.
|
|
36
37
|
*/
|
|
37
38
|
/**
|
|
38
39
|
* How many tool calls the agents with one thing to decide may make before they are told to decide:
|
|
@@ -47,9 +48,7 @@ export const JUDGE_CALLS = 30;
|
|
|
47
48
|
export const MAX_EXPLORATION_CHARS = 12_000;
|
|
48
49
|
/** How many times the explorer may hand an exploration over: each ends its turn, and is checked. */
|
|
49
50
|
export const EXPLORATION_SUBMISSIONS = 3;
|
|
50
|
-
export function explorationProblems(exploration, scope, moduleName
|
|
51
|
-
/** The functionalities whose acceptance tests the plan has to carry out, and how many each has. */
|
|
52
|
-
acceptance = []) {
|
|
51
|
+
export function explorationProblems(exploration, scope, moduleName) {
|
|
53
52
|
if (exploration === undefined)
|
|
54
53
|
return ["You have not called submit_exploration."];
|
|
55
54
|
const problems = [];
|
|
@@ -58,11 +57,11 @@ acceptance = []) {
|
|
|
58
57
|
problems.push(`It is ${size} characters - keep it under ${MAX_EXPLORATION_CHARS}. Hand over the interface, not the code: the routes, ` +
|
|
59
58
|
"commands, fields and their types, and the setup - no code listings, no files pasted whole.");
|
|
60
59
|
}
|
|
61
|
-
const unknown =
|
|
60
|
+
const unknown = exploration.interfaces.filter((item) => item.funcId !== "" && !scope.functionalities.some((ref) => ref.module === item.module && ref.funcId === item.funcId));
|
|
62
61
|
if (unknown.length > 0 && scope.tested === undefined) {
|
|
63
|
-
const names = [...new Set(unknown.map((item) => `${item.module} ${item.
|
|
64
|
-
problems.push(`${names.join(", ")} ${names.length === 1 ? "is not a functionality" : "are not functionalities"}: give module and id as ` +
|
|
65
|
-
`the specification
|
|
62
|
+
const names = [...new Set(unknown.map((item) => `${item.module} ${item.funcId}`))];
|
|
63
|
+
problems.push(`${names.join(", ")} ${names.length === 1 ? "is not a functionality" : "are not functionalities"}: give module and func-id as ` +
|
|
64
|
+
`the specification has them - ${scope.functionalities.filter((ref) => ref.module === moduleName).map(refLabel).join(", ") || "none yet"}.`);
|
|
66
65
|
}
|
|
67
66
|
const short = (text) => (text ?? "").trim().length < 40;
|
|
68
67
|
if (short(exploration.buildAndRun))
|
|
@@ -75,46 +74,26 @@ acceptance = []) {
|
|
|
75
74
|
if (short(exploration.environment))
|
|
76
75
|
problems.push("environment says too little: every variable, configuration file, service, database and fixture it needs, and how each test gets a fresh one - or say plainly it needs none.");
|
|
77
76
|
const reached = exploration.interfaces.filter((item) => !short(item.how));
|
|
78
|
-
const planned = exploration.plan.filter((item) => !short(item.tests));
|
|
79
77
|
if (scope.tested !== undefined) {
|
|
80
|
-
// A functionality's run: the one under test, reached
|
|
78
|
+
// A functionality's run: the one under test, reached.
|
|
81
79
|
const tested = scope.tested;
|
|
82
|
-
const has = (list) => list.some((item) => item.module === tested.module && item.
|
|
80
|
+
const has = (list) => list.some((item) => item.module === tested.module && item.funcId === tested.funcId);
|
|
83
81
|
if (!has(reached))
|
|
84
82
|
problems.push(`interfaces does not say how ${refLabel(tested)} is reached: the command and its arguments, or the route, method, headers and the request and response fields.`);
|
|
85
|
-
if (!has(planned))
|
|
86
|
-
problems.push(`plan has no tests for ${refLabel(tested)}: the cases to write, how each is set up and invoked, and what it checks as the specification says.`);
|
|
87
83
|
}
|
|
88
84
|
else {
|
|
89
|
-
// A spec change's: what it touches is for the explorer to say, but some of
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
problems.push(`plan has no tests for \`${moduleName}\`: the cases the change adds, changes or removes, for each functionality it touches.`);
|
|
94
|
-
}
|
|
95
|
-
// Every acceptance test is carried out, step by step, from outside: a plan that leaves one out
|
|
96
|
-
// leaves its author to write a placeholder.
|
|
97
|
-
for (const item of acceptance) {
|
|
98
|
-
const text = exploration.plan
|
|
99
|
-
.filter((entry) => entry.module === item.module && entry.frid === item.frid)
|
|
100
|
-
.map((entry) => entry.tests)
|
|
101
|
-
.join("\n");
|
|
102
|
-
const named = new Set([...text.matchAll(/acceptance test\s*(\d+)/gi)].map((match) => Number(match[1])));
|
|
103
|
-
const missing = Array.from({ length: item.count }, (_, index) => index + 1).filter((n) => !named.has(n));
|
|
104
|
-
if (missing.length > 0) {
|
|
105
|
-
problems.push(`plan does not carry out ${missing.map((n) => `acceptance test ${n}`).join(", ")} of ${item.module} ${item.frid}: name each ` +
|
|
106
|
-
'"acceptance test N" as the specification numbers them, and say how each of its steps is done from outside - through ' +
|
|
107
|
-
"which endpoint, or which class and method where the test reqs let the tests call a component directly - with what, " +
|
|
108
|
-
"and what it asserts. A long acceptance test is planned whole, never cut down.");
|
|
85
|
+
// A spec change's: what it touches is for the explorer to say, but some of a module it touches has to be reached.
|
|
86
|
+
const modules = scope.touchedModules !== undefined && scope.touchedModules.length > 0 ? scope.touchedModules : [moduleName];
|
|
87
|
+
if (!reached.some((item) => modules.includes(item.module))) {
|
|
88
|
+
problems.push(`interfaces says nothing of how the functionalities of ${modules.map((module) => `\`${module}\``).join(" or ")} the change touches are reached - one entry each.`);
|
|
109
89
|
}
|
|
110
90
|
}
|
|
111
91
|
return problems;
|
|
112
92
|
}
|
|
113
93
|
/**
|
|
114
|
-
* How the built software is invoked from outside,
|
|
115
|
-
*
|
|
116
|
-
*
|
|
117
|
-
* back until it does. What the explorer wrote in its replies goes with an account it never completed.
|
|
94
|
+
* How the built software is invoked from outside, as an agent that explored the code writes it - everything of the code
|
|
95
|
+
* the tests' author works from; what to test is the scenarios'. Checked before it is used: it has to cover every
|
|
96
|
+
* functionality the tests are for, and is sent back until it does. What the explorer wrote in its replies goes with an account it never completed.
|
|
118
97
|
*/
|
|
119
98
|
export async function exploreInvocation(options) {
|
|
120
99
|
// The codebase's account from an earlier run, and what of the code changed since: the explorer starts from it.
|
|
@@ -126,14 +105,14 @@ export async function exploreInvocation(options) {
|
|
|
126
105
|
module: Type.Optional(Type.String({ description: "The module the functionality is in" })),
|
|
127
106
|
id: Type.Optional(Type.String({
|
|
128
107
|
description: target !== undefined
|
|
129
|
-
? `The functionality's id as the specification
|
|
130
|
-
: 'The functionality\'s id as the specification
|
|
108
|
+
? `The functionality's func-id as the specification has it, e.g. "${target.funcId}" - leave both out for ${refLabel(target)}, the one under test`
|
|
109
|
+
: 'The functionality\'s func-id as the specification has it, e.g. "add-task" - never a name of your own',
|
|
131
110
|
})),
|
|
132
111
|
};
|
|
133
112
|
/** An entry's functionality: as given when it is one, otherwise - in a functionality's run - the one under test. */
|
|
134
113
|
const owner = (item) => {
|
|
135
|
-
const given = { module: (item.module ?? "").trim() || options.spec.moduleName,
|
|
136
|
-
const known = options.scope.functionalities.some((known) => known.module === given.module && known.
|
|
114
|
+
const given = { module: (item.module ?? "").trim() || options.spec.moduleName, funcId: (item.id ?? "").trim() };
|
|
115
|
+
const known = options.scope.functionalities.some((known) => known.module === given.module && known.funcId === given.funcId);
|
|
137
116
|
return known || target === undefined ? given : { ...target };
|
|
138
117
|
};
|
|
139
118
|
const submit = defineTool({
|
|
@@ -158,12 +137,6 @@ export async function exploreInvocation(options) {
|
|
|
158
137
|
environment: Type.String({
|
|
159
138
|
description: "What it needs around it: environment variables, configuration, services, databases, files, fixtures - and how each test gets a fresh one",
|
|
160
139
|
}),
|
|
161
|
-
plan: Type.Array(Type.Object({
|
|
162
|
-
...ref,
|
|
163
|
-
tests: Type.String({
|
|
164
|
-
description: "The conformance tests to write for it, from the specification and its acceptance tests: each case, how it is set up and invoked, what it checks as the specification says, and where its files go as the test reqs say - and every acceptance test, named \"acceptance test N\", with how each of its steps is done from outside and what it asserts",
|
|
165
|
-
}),
|
|
166
|
-
})),
|
|
167
140
|
other: Type.Optional(Type.String({ description: "Anything else the test author needs" })),
|
|
168
141
|
}),
|
|
169
142
|
async execute(_id, params) {
|
|
@@ -174,7 +147,6 @@ export async function exploreInvocation(options) {
|
|
|
174
147
|
testsReachIt: given.tests_reach_it ?? "",
|
|
175
148
|
interfaces: given.interfaces.map((item) => ({ ...owner(item), how: item.how })),
|
|
176
149
|
environment: given.environment,
|
|
177
|
-
plan: given.plan.map((item) => ({ ...owner(item), tests: item.tests })),
|
|
178
150
|
other: given.other,
|
|
179
151
|
};
|
|
180
152
|
// Every hand-over ends the turn and is checked: the harness says what is missing, a few times at most.
|
|
@@ -199,7 +171,7 @@ export async function exploreInvocation(options) {
|
|
|
199
171
|
try {
|
|
200
172
|
replies.push(await ask(session, BEGIN, options.onDelta));
|
|
201
173
|
for (let again = 0; again < EXPLORATION_SUBMISSIONS; again += 1) {
|
|
202
|
-
const problems = explorationProblems(exploration, options.scope, options.spec.moduleName
|
|
174
|
+
const problems = explorationProblems(exploration, options.scope, options.spec.moduleName);
|
|
203
175
|
if (problems.length === 0 || submissions >= EXPLORATION_SUBMISSIONS)
|
|
204
176
|
break;
|
|
205
177
|
reportActivity(` the exploration is not complete yet: ${problems[0].slice(0, 120)}`);
|
|
@@ -209,7 +181,7 @@ export async function exploreInvocation(options) {
|
|
|
209
181
|
finally {
|
|
210
182
|
session.dispose();
|
|
211
183
|
}
|
|
212
|
-
const complete = explorationProblems(exploration, options.scope, options.spec.moduleName
|
|
184
|
+
const complete = explorationProblems(exploration, options.scope, options.spec.moduleName).length === 0;
|
|
213
185
|
const found = replies.filter((reply) => reply.trim() !== "");
|
|
214
186
|
if (exploration === undefined) {
|
|
215
187
|
return found.length > 0
|
|
@@ -332,7 +304,7 @@ export function createAskExplorerTool(options) {
|
|
|
332
304
|
}
|
|
333
305
|
export function explorePrompt(buildDir, spec, scope, testingScripts, question, previous) {
|
|
334
306
|
const target = scope.tested !== undefined
|
|
335
|
-
? `
|
|
307
|
+
? `functional spec ${refLabel(scope.tested)}`
|
|
336
308
|
: `the spec change - every functionality of \`${spec.moduleName}\` it can reach`;
|
|
337
309
|
return [
|
|
338
310
|
specPrimer({ testReqs: true }),
|
|
@@ -358,17 +330,11 @@ export function explorePrompt(buildDir, spec, scope, testingScripts, question, p
|
|
|
358
330
|
" reached from outside: the command and its arguments, or the route, method and headers, and the fields",
|
|
359
331
|
" of its request and response and their types, as the code takes them;",
|
|
360
332
|
"- environment: environment variables, configuration, services and databases it talks to, files and",
|
|
361
|
-
" fixtures - and how each test gets a fresh one
|
|
362
|
-
`- plan: the conformance tests to write for ${target}, from the specification and its acceptance tests:`,
|
|
363
|
-
" each case, how it is set up and invoked, what it checks as the specification says, and where the files",
|
|
364
|
-
" go as the test reqs say. Every acceptance test, named \"acceptance test N\" as the specification numbers",
|
|
365
|
-
" them, with how each of its steps is done from outside - through which endpoint, or which class and",
|
|
366
|
-
" method where the test reqs let the tests call a component directly - and what it asserts. A long one is",
|
|
367
|
-
" planned whole, never cut down;",
|
|
333
|
+
" fixtures - and how each test gets a fresh one.",
|
|
368
334
|
"",
|
|
335
|
+
"What to test is not yours to say: the tests carry out the functional specs' scenarios, and nothing else.",
|
|
369
336
|
"Describe how to call it, never what it answers: what it returns in each case is for the tests to check",
|
|
370
|
-
"against the
|
|
371
|
-
"specification, and the code only tells you how to reach it. Read the code - its build files, its entry points, its",
|
|
337
|
+
"against the scenarios, not to copy from the code - the code only tells you how to reach it. Read the code - its build files, its entry points, its",
|
|
372
338
|
"configuration - with the file tools: you have no shell, and run nothing. Change nothing. Then call submit_exploration - once, with the whole of it: it ends your turn, and",
|
|
373
339
|
`you are told if anything is missing. You can hand it over ${EXPLORATION_SUBMISSIONS} times at most.`,
|
|
374
340
|
"",
|
|
@@ -397,7 +363,7 @@ export function explorePrompt(buildDir, spec, scope, testingScripts, question, p
|
|
|
397
363
|
"This codebase was explored before, for an earlier conformance run, and the account below was handed over then.",
|
|
398
364
|
"Start from it - do not explore again what it already says. Check what it says against the files that changed",
|
|
399
365
|
"since, listed below, and read further only for what it does not cover yet: how the functionality under test",
|
|
400
|
-
"now is reached
|
|
366
|
+
"now is reached. Then hand over the whole account, updated - what still holds kept,",
|
|
401
367
|
"what changed corrected, what is new added.",
|
|
402
368
|
"",
|
|
403
369
|
`<the account handed over before>
|
|
@@ -450,7 +416,7 @@ export async function createTestReviewer(options) {
|
|
|
450
416
|
}),
|
|
451
417
|
what: Type.String({ description: "Exactly what is missing or bad, and what the specification says instead" }),
|
|
452
418
|
blocking: Type.Boolean({
|
|
453
|
-
description: "True when the functionality is not yet tested black box because of it: a
|
|
419
|
+
description: "True when the functionality is not yet tested black box because of it: a scenario's test missing, cut down or " +
|
|
454
420
|
"skipping what it checks, the software mocked or not reached from outside, a test that cannot fail. False for a way to make " +
|
|
455
421
|
"tests that already do that better",
|
|
456
422
|
}),
|
|
@@ -502,10 +468,10 @@ function problemText(problem) {
|
|
|
502
468
|
}
|
|
503
469
|
/** The reviewer's task, for its instructions: word for word through every review of the run. */
|
|
504
470
|
export function testReviewTask(options) {
|
|
505
|
-
const
|
|
471
|
+
const scenarios = options.functionalities.flatMap((item) => scenariosOf(item).map((scenario) => ({ module: item.module ?? options.spec.moduleName, funcId: item.funcId, scenario })));
|
|
506
472
|
const whose = options.forChange
|
|
507
|
-
? `the functionalities
|
|
508
|
-
: `
|
|
473
|
+
? `the functionalities the change touches (${options.functionalities.map((item) => `${item.module ?? options.spec.moduleName}:${item.funcId}`).join(", ") || "none"})`
|
|
474
|
+
: `functional spec ${options.spec.moduleName}:${options.functionalities[0]?.funcId ?? ""}`;
|
|
509
475
|
return [
|
|
510
476
|
specPrimer({ testReqs: true }),
|
|
511
477
|
`You review the conformance tests of the \`${options.spec.moduleName}\` specification a conformance run adds or changes, each time`,
|
|
@@ -524,7 +490,9 @@ export function testReviewTask(options) {
|
|
|
524
490
|
"check it against the diff and the files. A problem it says is fixed that the tests do not fix is still a problem;",
|
|
525
491
|
"one it declined, weigh its reason - it may be right.",
|
|
526
492
|
"",
|
|
527
|
-
...(options.cases !== undefined && options.cases.mechanical
|
|
493
|
+
...(options.cases !== undefined && options.cases.mechanical
|
|
494
|
+
? [...conformanceReviewCasesBlock(options.cases), "", "And, of every test the run changed:"]
|
|
495
|
+
: [...(options.cases !== undefined ? conformanceReviewCasesBlock(options.cases, options.spec.moduleName) : []), "Check every test the run added or changed:"]),
|
|
528
496
|
"- Black box: it reaches the software only the way the test reqs have it - its API, its command, its public",
|
|
529
497
|
" classes where the test reqs say so - and never mocks, stubs or replaces any part of the software under test:",
|
|
530
498
|
" no mocked beans, services or clients of its own. A real external service the test reqs say not to mock is",
|
|
@@ -532,33 +500,33 @@ export function testReviewTask(options) {
|
|
|
532
500
|
"- It tests something: every test asserts what the specification says, and fails when that does not hold. No",
|
|
533
501
|
" placeholder, no `assertTrue(true)`, no empty or skipped test, no failure caught and swallowed, no assertion",
|
|
534
502
|
" so loose any answer passes.",
|
|
535
|
-
...(
|
|
503
|
+
...(scenarios.length > 0
|
|
536
504
|
? [
|
|
537
|
-
`- The
|
|
538
|
-
"
|
|
539
|
-
"
|
|
505
|
+
`- The scenarios of ${whose}, each carried out by at least one test, doing every step it lists, in order, with`,
|
|
506
|
+
" what it names, and asserting every expectation it states. A step left out, replaced by a stand-in, or said",
|
|
507
|
+
" to be impossible to run is missing, not done:",
|
|
540
508
|
"",
|
|
541
|
-
...
|
|
509
|
+
...scenarios.map(({ module, funcId, scenario }) => `<scenario>\n${scenarioText(module, funcId, scenario)}\n</scenario>`),
|
|
542
510
|
"",
|
|
543
511
|
]
|
|
544
512
|
: []),
|
|
545
|
-
|
|
546
|
-
" edge
|
|
547
|
-
"
|
|
548
|
-
"
|
|
549
|
-
"
|
|
513
|
+
"- The tests are the scenarios', and only theirs: a test that carries out no scenario is not one to ask for, and a",
|
|
514
|
+
" missing edge case the scenarios do not name is not a problem to raise. Each is checked against the",
|
|
515
|
+
" specification, not against what the code happens to do - only what can be seen from outside: where the",
|
|
516
|
+
" specification says one part calls or uses another, the test checks the outcome that follows - what comes back,",
|
|
517
|
+
" what is stored, what the other service received - never that the call was made.",
|
|
550
518
|
"",
|
|
551
519
|
"The first review finds every problem, in that one review - the tests go back once with all of them. A review after",
|
|
552
520
|
"it checks each problem you raised last first - fixed, or still there - and raises a new one only when it is one of",
|
|
553
|
-
"the above - a test that cannot fail, mocks the software, or leaves out or misdoes what the specification or
|
|
554
|
-
"
|
|
521
|
+
"the above - a test that cannot fail, mocks the software, or leaves out or misdoes what the specification or a",
|
|
522
|
+
"scenario asks for. Not a matter of style or thoroughness the last review let pass: the review is not done",
|
|
555
523
|
"again from the start.",
|
|
556
524
|
"",
|
|
557
525
|
"Each review ends with submit_review, once: pass when nothing is missing and nothing is bad; otherwise every problem",
|
|
558
526
|
'- the file, the test, its kind - "missing" for a test that is not there, "bad" for one that is weak, wrong or not',
|
|
559
527
|
"black box - and exactly what, with what the specification says instead. Be specific: the agent that fixes them gets",
|
|
560
528
|
"your list and nothing else. Mark a problem blocking when because of it the functionality is not yet tested black",
|
|
561
|
-
"box - a
|
|
529
|
+
"box - a scenario's test missing, cut down or skipping what it checks, the software mocked or not reached",
|
|
562
530
|
"from outside, a test that cannot fail. Those are worked on first; the rest - ways to make tests that already do",
|
|
563
531
|
"that better - are let go after a few reviews.",
|
|
564
532
|
"",
|
|
@@ -614,15 +582,15 @@ export function reviewFeedbackPrompt(problems) {
|
|
|
614
582
|
}
|
|
615
583
|
/** "billing 4": a functionality as the judge and the person read it. */
|
|
616
584
|
export function refLabel(ref) {
|
|
617
|
-
return `${ref.module} ${ref.
|
|
585
|
+
return `${ref.module} ${ref.funcId}`;
|
|
618
586
|
}
|
|
619
|
-
const same = (a, b) => a.module === b.module && a.
|
|
587
|
+
const same = (a, b) => a.module === b.module && a.funcId === b.funcId;
|
|
620
588
|
/** A conflict's functionalities as the judge gave them, tidied: trimmed, and each once. */
|
|
621
589
|
export function conflictRefs(given) {
|
|
622
590
|
const refs = [];
|
|
623
591
|
for (const item of given ?? []) {
|
|
624
|
-
const ref = { module: (item.module ?? "").trim(),
|
|
625
|
-
if (ref.module !== "" && ref.
|
|
592
|
+
const ref = { module: (item.module ?? "").trim(), funcId: (item.funcId ?? item.id ?? "").trim() };
|
|
593
|
+
if (ref.module !== "" && ref.funcId !== "" && !refs.some((known) => same(known, ref)))
|
|
626
594
|
refs.push(ref);
|
|
627
595
|
}
|
|
628
596
|
return refs;
|
|
@@ -648,7 +616,7 @@ export function conflictKey(functionalities) {
|
|
|
648
616
|
return sortedRefs(functionalities).map(refLabel).join(" & ");
|
|
649
617
|
}
|
|
650
618
|
export function sortedRefs(functionalities) {
|
|
651
|
-
return conflictRefs(functionalities).sort((a, b) => a.module.localeCompare(b.module) || a.
|
|
619
|
+
return conflictRefs(functionalities).sort((a, b) => a.module.localeCompare(b.module) || a.funcId.localeCompare(b.funcId, undefined, { numeric: true }));
|
|
652
620
|
}
|
|
653
621
|
/** How many times a spec conflict between the same two functionalities is raised before it counts. */
|
|
654
622
|
export const SPEC_CONFLICT_RAISES = 3;
|
|
@@ -749,7 +717,7 @@ export async function judgeFailure(options) {
|
|
|
749
717
|
functionalities: Type.Optional(Type.Array(Type.Object({
|
|
750
718
|
module: Type.String({ description: "The module the functionality is in" }),
|
|
751
719
|
id: Type.String({ description: "Its id in that module" }),
|
|
752
|
-
}), { description: 'For spec: the two functionalities that cannot both hold, e.g. [{"module": "billing", "id": "
|
|
720
|
+
}), { description: 'For spec: the two functionalities that cannot both hold, e.g. [{"module": "billing", "id": "refund"}, {"module": "accounts", "id": "register"}]' })),
|
|
753
721
|
}),
|
|
754
722
|
async execute(_id, params) {
|
|
755
723
|
const given = params;
|
|
@@ -1041,6 +1009,7 @@ export async function createCodeFixer(options) {
|
|
|
1041
1009
|
* word for word through every fix of the run; each failure comes as a message (`codeFixPrompt`).
|
|
1042
1010
|
*/
|
|
1043
1011
|
export function codeFixerTask(options) {
|
|
1012
|
+
const base = baseOfCodebase(options.buildDir);
|
|
1044
1013
|
return [
|
|
1045
1014
|
specPrimer({ testReqs: false }),
|
|
1046
1015
|
`You are the coding agent of the \`${options.spec.moduleName}\` specification. Its code is in ${options.buildDir}. When its`,
|
|
@@ -1050,6 +1019,8 @@ export function codeFixerTask(options) {
|
|
|
1050
1019
|
options.spec.text.trim(),
|
|
1051
1020
|
"</specification>",
|
|
1052
1021
|
"",
|
|
1022
|
+
// What came from the base folder its codebase started from: a fix keeps it.
|
|
1023
|
+
...(base !== undefined ? [baseFolderBlock(base), ""] : []),
|
|
1053
1024
|
...fixMethod({ what: "conformance tests", fixes: "code", runTool: "run_conformance_tests" }),
|
|
1054
1025
|
"",
|
|
1055
1026
|
"run_conformance_tests runs the project's conformance testing script on the code as it now is, with the whole",
|
|
@@ -1072,6 +1043,7 @@ export function codeFixerTask(options) {
|
|
|
1072
1043
|
"Keep the build's identity as it is - its name, coordinates, version, package and main class - unless the",
|
|
1073
1044
|
"specification asks for another: the tests and other builds point at them. Leave in your notes what holds about",
|
|
1074
1045
|
"the code for the next agent, not an account of this fix: the harness records the attempts itself.",
|
|
1046
|
+
...(base !== undefined ? BASE_FOLDER_RULES : []),
|
|
1075
1047
|
...INTEGRITY_RULES,
|
|
1076
1048
|
...ERROR_CAUSE_RULES,
|
|
1077
1049
|
].join("\n");
|
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
import { ACCEPTANCE_TESTS, type MarshalledPlainSource } from "../plain/spec.ts";
|
|
1
|
+
import { ACCEPTANCE_TESTS, CONFORMANCE_TESTS, type MarshalledPlainSource } from "../plain/spec.ts";
|
|
2
2
|
import { type BuildingSpec } from "./activity.ts";
|
|
3
3
|
import type { JobCases } from "./drift.ts";
|
|
4
|
+
import { type TestedSpec } from "./test-kinds.ts";
|
|
4
5
|
import { type ScriptRun, failureSignature, STUCK_ROUNDS } from "./test-scripts.ts";
|
|
5
6
|
import type { ToolDefinition } from "@earendil-works/pi-coding-agent";
|
|
6
7
|
import { type Evidence, type ReviewProblem } from "./conformance-agents.ts";
|
|
@@ -15,7 +16,7 @@ export interface RenderConformanceTestsOptions {
|
|
|
15
16
|
/** What the job does to each functional spec (work.ts): its agents are told case by case. */
|
|
16
17
|
cases?: JobCases;
|
|
17
18
|
plainFile: string;
|
|
18
|
-
|
|
19
|
+
funcId?: string;
|
|
19
20
|
templateDirs?: string[];
|
|
20
21
|
/** The rendered code under test. Must exist and be non-empty. */
|
|
21
22
|
buildDir?: string;
|
|
@@ -32,6 +33,11 @@ export interface RenderConformanceTestsOptions {
|
|
|
32
33
|
dryRun?: boolean;
|
|
33
34
|
/** Update the tests for a spec change, given as a diff, rather than for one functionality. */
|
|
34
35
|
diff?: string;
|
|
36
|
+
/**
|
|
37
|
+
* For a spec change: what the tests are written for once it lands - the tests record it writes. Every scenario in it
|
|
38
|
+
* has a labelled test before a run counts, and a label naming nothing in it sends the agent back.
|
|
39
|
+
*/
|
|
40
|
+
recorded?: MarshalledPlainSource;
|
|
35
41
|
/** That change section by section, as the agents are shown it in place of the diff. */
|
|
36
42
|
change?: string;
|
|
37
43
|
/**
|
|
@@ -50,7 +56,7 @@ export interface RenderConformanceTestsOptions {
|
|
|
50
56
|
}
|
|
51
57
|
export interface RenderConformanceTestsResult {
|
|
52
58
|
moduleName: string;
|
|
53
|
-
|
|
59
|
+
funcId: string;
|
|
54
60
|
buildDir: string;
|
|
55
61
|
conformanceDir: string;
|
|
56
62
|
prompt: string;
|
|
@@ -80,7 +86,7 @@ export interface RenderConformanceTestsResult {
|
|
|
80
86
|
specConflict?: {
|
|
81
87
|
functionalities: Array<{
|
|
82
88
|
module: string;
|
|
83
|
-
|
|
89
|
+
funcId: string;
|
|
84
90
|
}>;
|
|
85
91
|
reason: string;
|
|
86
92
|
};
|
|
@@ -132,14 +138,11 @@ export declare function renderConformanceTests(options: RenderConformanceTestsOp
|
|
|
132
138
|
*/
|
|
133
139
|
export declare function notLandedWhy(what: string, unlabelled?: string[]): string;
|
|
134
140
|
/**
|
|
135
|
-
* The functionalities a spec change touches -
|
|
136
|
-
* has a line the change adds or removes - with their
|
|
141
|
+
* The functionalities with scenarios a spec change touches - by the job's cases, else those whose functional spec, or one of
|
|
142
|
+
* whose scenarios, has a line the change adds or removes - with their scenarios: what a spec diff's
|
|
137
143
|
* conformance run labels, checks and reviews, as a functionality's run does for its one.
|
|
138
144
|
*/
|
|
139
|
-
export declare function touchedFunctionalities(tree: MarshalledPlainSource,
|
|
140
|
-
frid: string;
|
|
141
|
-
acceptanceTests: string[];
|
|
142
|
-
}>;
|
|
145
|
+
export declare function touchedFunctionalities(tree: MarshalledPlainSource, funcIds: string[], diff: string, moduleName?: string, cases?: JobCases): TestedSpec[];
|
|
143
146
|
/** From the how-manieth review that sends the tests back the arbiter weighs each one (see arbiter.ts). */
|
|
144
147
|
export declare const REVIEW_ROUNDS = 1;
|
|
145
148
|
/** How many times the tests are sent back to be written and labelled before the run stops. */
|
|
@@ -173,9 +176,16 @@ export declare function buildManifests(buildDir: string): Array<{
|
|
|
173
176
|
export declare function failureLine(run: ScriptRun): string;
|
|
174
177
|
/**
|
|
175
178
|
* The functional spec a conformance run writes tests for, for the person watching: which one of how
|
|
176
|
-
* many, and its text as the spec has it, with its
|
|
179
|
+
* many, and its text as the spec has it, with its scenarios after it.
|
|
177
180
|
*/
|
|
178
|
-
export declare function buildingSpec(moduleName: string,
|
|
181
|
+
export declare function buildingSpec(moduleName: string, funcId: string, total: number, specifications: {
|
|
179
182
|
"functional specs": string[];
|
|
180
|
-
[
|
|
183
|
+
[CONFORMANCE_TESTS]?: Array<{
|
|
184
|
+
id: string;
|
|
185
|
+
text: string;
|
|
186
|
+
}>;
|
|
187
|
+
[ACCEPTANCE_TESTS]?: Array<{
|
|
188
|
+
id: string;
|
|
189
|
+
text: string;
|
|
190
|
+
}>;
|
|
181
191
|
}): BuildingSpec;
|