@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.
Files changed (216) hide show
  1. package/README.md +6 -6
  2. package/dist/advisor-chat.d.ts +63 -0
  3. package/dist/advisor-chat.js +290 -0
  4. package/dist/background-skills.d.ts +53 -0
  5. package/dist/background-skills.js +121 -0
  6. package/dist/bedrock-region.d.ts +66 -0
  7. package/dist/bedrock-region.js +144 -0
  8. package/dist/btw.d.ts +1 -1
  9. package/dist/btw.js +1 -1
  10. package/dist/change-plain-cli.js +20 -13
  11. package/dist/chat-idle.d.ts +7 -0
  12. package/dist/chat-idle.js +20 -0
  13. package/dist/chats.d.ts +10 -0
  14. package/dist/chats.js +23 -0
  15. package/dist/cli-options.d.ts +1 -2
  16. package/dist/cli-options.js +2 -7
  17. package/dist/cli.js +11 -3
  18. package/dist/config-command.d.ts +14 -0
  19. package/dist/config-command.js +35 -15
  20. package/dist/config-edit.d.ts +3 -3
  21. package/dist/config-edit.js +16 -12
  22. package/dist/config.d.ts +54 -34
  23. package/dist/config.js +40 -44
  24. package/dist/conform-cli.js +12 -10
  25. package/dist/conformance-switch.d.ts +34 -0
  26. package/dist/conformance-switch.js +108 -0
  27. package/dist/cultivate-cli.js +15 -5
  28. package/dist/drift-cli.js +2 -0
  29. package/dist/extension.d.ts +15 -0
  30. package/dist/extension.js +260 -83
  31. package/dist/host-session.d.ts +26 -0
  32. package/dist/host-session.js +30 -2
  33. package/dist/host.d.ts +5 -2
  34. package/dist/host.js +7 -1
  35. package/dist/init-config.d.ts +1 -1
  36. package/dist/init-config.js +20 -11
  37. package/dist/loop-cli.d.ts +3 -3
  38. package/dist/loop-cli.js +13 -16
  39. package/dist/loop-tools.js +18 -10
  40. package/dist/model-check.d.ts +8 -3
  41. package/dist/model-check.js +13 -8
  42. package/dist/new-project.js +12 -8
  43. package/dist/{validate-cli.js → parse-cli.js} +11 -5
  44. package/dist/parse-tool.d.ts +9 -0
  45. package/dist/parse-tool.js +33 -0
  46. package/dist/pi-launch.js +1 -1
  47. package/dist/plain/check.d.ts +53 -6
  48. package/dist/plain/check.js +217 -24
  49. package/dist/plain/concepts.d.ts +8 -0
  50. package/dist/plain/concepts.js +66 -23
  51. package/dist/plain/cycles.d.ts +21 -0
  52. package/dist/plain/cycles.js +190 -0
  53. package/dist/plain/errors.d.ts +4 -0
  54. package/dist/plain/errors.js +7 -0
  55. package/dist/plain/files.d.ts +18 -1
  56. package/dist/plain/files.js +39 -2
  57. package/dist/plain/graph.d.ts +33 -4
  58. package/dist/plain/graph.js +60 -12
  59. package/dist/plain/highlight.d.ts +3 -2
  60. package/dist/plain/highlight.js +4 -3
  61. package/dist/plain/index.d.ts +8 -5
  62. package/dist/plain/index.js +8 -5
  63. package/dist/plain/liquid.js +1 -6
  64. package/dist/plain/markdown.d.ts +32 -3
  65. package/dist/plain/markdown.js +146 -24
  66. package/dist/plain/module-concepts.js +2 -2
  67. package/dist/plain/parser.d.ts +83 -28
  68. package/dist/plain/parser.js +381 -260
  69. package/dist/plain/plain.tmLanguage.json +29 -3
  70. package/dist/plain/project.d.ts +48 -0
  71. package/dist/plain/project.js +155 -0
  72. package/dist/plain/spec.d.ts +90 -28
  73. package/dist/plain/spec.js +107 -133
  74. package/dist/plain/test-plain-file.d.ts +59 -0
  75. package/dist/plain/test-plain-file.js +232 -0
  76. package/dist/regenerate/activity.d.ts +20 -9
  77. package/dist/regenerate/activity.js +11 -4
  78. package/dist/regenerate/advisor-records.d.ts +93 -0
  79. package/dist/regenerate/advisor-records.js +110 -0
  80. package/dist/regenerate/advisor-runs.d.ts +87 -0
  81. package/dist/regenerate/advisor-runs.js +327 -0
  82. package/dist/regenerate/base-folder.d.ts +68 -0
  83. package/dist/regenerate/base-folder.js +168 -0
  84. package/dist/regenerate/cases.d.ts +9 -6
  85. package/dist/regenerate/cases.js +70 -14
  86. package/dist/regenerate/change-view.d.ts +8 -3
  87. package/dist/regenerate/change-view.js +71 -22
  88. package/dist/regenerate/chunk-plan.d.ts +36 -15
  89. package/dist/regenerate/chunk-plan.js +165 -59
  90. package/dist/regenerate/conform.d.ts +3 -0
  91. package/dist/regenerate/conform.js +6 -5
  92. package/dist/regenerate/conformance-agents.d.ts +18 -39
  93. package/dist/regenerate/conformance-agents.js +60 -88
  94. package/dist/regenerate/conformance.d.ts +23 -13
  95. package/dist/regenerate/conformance.js +119 -82
  96. package/dist/regenerate/diff-view.d.ts +1 -1
  97. package/dist/regenerate/diff-view.js +20 -4
  98. package/dist/regenerate/drift.d.ts +47 -15
  99. package/dist/regenerate/drift.js +101 -30
  100. package/dist/regenerate/jobs.d.ts +2 -2
  101. package/dist/regenerate/jobs.js +1 -1
  102. package/dist/regenerate/ledger.d.ts +19 -0
  103. package/dist/regenerate/loop.d.ts +62 -43
  104. package/dist/regenerate/loop.js +472 -341
  105. package/dist/regenerate/paths.d.ts +4 -0
  106. package/dist/regenerate/paths.js +7 -1
  107. package/dist/regenerate/plain-palette.js +2 -0
  108. package/dist/regenerate/prompt.d.ts +57 -19
  109. package/dist/regenerate/prompt.js +157 -122
  110. package/dist/regenerate/records.d.ts +43 -12
  111. package/dist/regenerate/records.js +102 -88
  112. package/dist/regenerate/regenerate.js +29 -11
  113. package/dist/regenerate/regression.d.ts +4 -4
  114. package/dist/regenerate/regression.js +14 -10
  115. package/dist/regenerate/renames.d.ts +42 -0
  116. package/dist/regenerate/renames.js +185 -0
  117. package/dist/regenerate/requires.d.ts +1 -1
  118. package/dist/regenerate/requires.js +10 -3
  119. package/dist/regenerate/resources.d.ts +5 -3
  120. package/dist/regenerate/resources.js +4 -4
  121. package/dist/regenerate/scripts-being-written.d.ts +19 -0
  122. package/dist/regenerate/scripts-being-written.js +30 -0
  123. package/dist/regenerate/spec-map-view.d.ts +2 -2
  124. package/dist/regenerate/spec-map-view.js +46 -15
  125. package/dist/regenerate/spec-map.d.ts +27 -5
  126. package/dist/regenerate/spec-map.js +92 -60
  127. package/dist/regenerate/spec-steps.d.ts +48 -5
  128. package/dist/regenerate/spec-steps.js +213 -28
  129. package/dist/regenerate/state.d.ts +4 -3
  130. package/dist/regenerate/stop-report.d.ts +1 -1
  131. package/dist/regenerate/stop-report.js +10 -10
  132. package/dist/regenerate/test-advisor.d.ts +101 -0
  133. package/dist/regenerate/test-advisor.js +379 -0
  134. package/dist/regenerate/test-kinds.d.ts +71 -28
  135. package/dist/regenerate/test-kinds.js +110 -63
  136. package/dist/regenerate/test-scripts.d.ts +2 -0
  137. package/dist/regenerate/test-scripts.js +4 -0
  138. package/dist/regenerate/testing-steps.d.ts +41 -0
  139. package/dist/regenerate/testing-steps.js +110 -0
  140. package/dist/regenerate/testing.d.ts +1 -1
  141. package/dist/regenerate/testing.js +5 -4
  142. package/dist/regenerate/unit-review.d.ts +1 -1
  143. package/dist/regenerate/unit-review.js +3 -3
  144. package/dist/regenerate-cli.js +11 -12
  145. package/dist/release-agent.js +4 -0
  146. package/dist/release-cli.js +2 -0
  147. package/dist/release-tools.js +2 -0
  148. package/dist/run-report.d.ts +2 -2
  149. package/dist/run-report.js +4 -3
  150. package/dist/self-invocation.d.ts +1 -1
  151. package/dist/self-invocation.js +1 -1
  152. package/dist/sentinel.js +1 -1
  153. package/dist/setup.d.ts +10 -0
  154. package/dist/setup.js +52 -4
  155. package/dist/skill.d.ts +10 -0
  156. package/dist/skill.js +6 -0
  157. package/dist/skills.js +3 -1
  158. package/dist/spec-tools.js +21 -31
  159. package/dist/test-plain-files.d.ts +41 -0
  160. package/dist/test-plain-files.js +177 -0
  161. package/docs/README.md +3 -1
  162. package/docs/authoring-plain.md +46 -13
  163. package/docs/chats.md +3 -1
  164. package/docs/config.md +20 -15
  165. package/docs/deploy.md +1 -1
  166. package/docs/development.md +2 -1
  167. package/docs/headless.md +15 -18
  168. package/docs/hosting.md +28 -12
  169. package/docs/installation.md +1 -0
  170. package/docs/parser.md +88 -27
  171. package/docs/regenerating.md +87 -52
  172. package/docs/sentinel.md +1 -1
  173. package/docs/skills-and-gates.md +3 -1
  174. package/docs/test-advisor.md +110 -0
  175. package/docs/theming.md +1 -1
  176. package/docs/using.md +17 -15
  177. package/genome/rules/bullet-continuation.md +8 -8
  178. package/genome/rules/concise-specs-examples.md +111 -80
  179. package/genome/rules/concise-specs-integration-examples.md +56 -41
  180. package/genome/rules/concise-specs.md +17 -16
  181. package/genome/rules/definitions.md +5 -5
  182. package/genome/rules/examples/integration-embedded-testing/run_conformance_tests_java.sh +1 -1
  183. package/genome/rules/func-specs.md +69 -74
  184. package/genome/rules/impl-reqs.md +4 -4
  185. package/genome/rules/integration-embedded-testing.md +20 -20
  186. package/genome/rules/integration-embedded.md +31 -31
  187. package/genome/rules/integration-standalone.md +9 -9
  188. package/genome/rules/integrations.md +18 -18
  189. package/genome/rules/linked-resources.md +19 -16
  190. package/genome/rules/module-structure.md +21 -14
  191. package/genome/rules/project-model.md +18 -17
  192. package/genome/rules/required-concepts.md +1 -1
  193. package/genome/rules/requires-modules.md +6 -5
  194. package/genome/rules/semantic-ids.md +49 -0
  195. package/genome/rules/stdlib.md +4 -5
  196. package/genome/rules/test-plain-files.md +130 -0
  197. package/genome/rules/test-reqs.md +8 -8
  198. package/genome/rules/turri-regeneration.md +23 -8
  199. package/genome/skills/analyze-2-func-specs/SKILL.md +36 -21
  200. package/genome/skills/analyze-func-specs/SKILL.md +46 -32
  201. package/genome/skills/break-down-func-spec/SKILL.md +91 -67
  202. package/genome/skills/consolidate-concepts/SKILL.md +10 -10
  203. package/genome/skills/convert-old-syntax/SKILL.md +68 -0
  204. package/genome/skills/convert-old-syntax/references/rubric.md +11 -0
  205. package/genome/skills/debug-specs/SKILL.md +62 -56
  206. package/genome/skills/implement-conformance-testing-script/SKILL.md +19 -15
  207. package/genome/skills/implement-conformance-testing-script/assets/run_conformance_tests_java.sh +2 -2
  208. package/genome/skills/implement-conformance-testing-script/assets/run_conformance_tests_python.sh +2 -2
  209. package/genome/skills/implement-unit-testing-script/SKILL.md +14 -10
  210. package/genome/skills/refactor-module/SKILL.md +35 -35
  211. package/genome/skills/resolve-section-ownership/SKILL.md +9 -9
  212. package/genome/skills/resolve-spec-conflict/SKILL.md +35 -29
  213. package/package.json +7 -6
  214. package/dist/regenerate/choose-conformance.d.ts +0 -32
  215. package/dist/regenerate/choose-conformance.js +0 -117
  216. /package/dist/{validate-cli.d.ts → parse-cli.d.ts} +0 -0
@@ -1,10 +1,10 @@
1
1
  import { modulesScratchDir } from "./paths.js";
2
- import { ACCEPTANCE_TESTS, DEFINITIONS, FUNCTIONAL_REQUIREMENTS, NON_FUNCTIONAL_REQUIREMENTS, REQUIRED_FUNCTIONAL_SPECS, TEST_REQUIREMENTS, } from "../plain/spec.js";
2
+ import { ACCEPTANCE_TESTS, CONFORMANCE_TESTS, DEFINITIONS, FUNCTIONAL_REQUIREMENTS, NON_FUNCTIONAL_REQUIREMENTS, REQUIRED_FUNCTIONAL_SPECS, TEST_REQUIREMENTS, } from "../plain/spec.js";
3
3
  import { codeCopyOf, isCodeResource } from "./resources.js";
4
- import { acceptanceLabel, acceptanceText, conformanceLabel } from "./test-kinds.js";
4
+ import { conformanceLabel, scenarioLabel, scenariosOf, scenarioText } from "./test-kinds.js";
5
5
  import { codeCasesBlock, conformanceCasesBlock } from "./cases.js";
6
6
  import { marksRegenerateAgain } from "./records.js";
7
- import { resourcesChanged } from "./spec-steps.js";
7
+ import { resourcesChanged, scenarioLines } from "./spec-steps.js";
8
8
  /**
9
9
  * Everything already in the code: the required modules' functional specs, bottom of the
10
10
  * chain first, then this module's own earlier ones. Null when there is nothing.
@@ -13,17 +13,17 @@ import { resourcesChanged } from "./spec-steps.js";
13
13
  * that does not know what exists re-implements it; a test writer that does not know it
14
14
  * cannot set up what a functionality depends on; a fixer that does not know it breaks it.
15
15
  */
16
- function alreadyImplemented(moduleName, own, required) {
16
+ function alreadyImplemented(moduleName, own, funcIds, required) {
17
17
  if (own.length === 0 && required.length === 0)
18
18
  return null;
19
- const entries = [...required, ...own.map((spec, index) => ({ module: moduleName, frid: String(index + 1), spec }))];
19
+ const entries = [...required, ...own.map((spec, index) => ({ module: moduleName, funcId: funcIds[index] ?? "", spec }))];
20
20
  return `<already implemented>\n${functionalitiesByModule(entries, moduleName, "implemented")}\n</already implemented>`;
21
21
  }
22
22
  /**
23
23
  * Functional specs grouped by the module they belong to - in the order the modules are built on
24
- * one another, the current module marked - and each tagged with its module and its id there. A codebase holds the
25
- * functionalities of every module it is built on, and each module numbers its own from 1, so
26
- * "functionality 1" alone is ambiguous: the first listed may be base's 1 and the next top's 3.
24
+ * one another, the current module marked - and each tagged with its module and its func-id there. A codebase holds the
25
+ * functionalities of every module it is built on, and a func-id is unique only in its module, so a func-id alone can
26
+ * be ambiguous: `base` and `app` may each have an `export`.
27
27
  * (the reference implementation's implemented functional requirements prompt section, as tags.)
28
28
  */
29
29
  export function functionalitiesByModule(entries, currentModule, status) {
@@ -34,14 +34,14 @@ export function functionalitiesByModule(entries, currentModule, status) {
34
34
  .map((module) => {
35
35
  const items = entries
36
36
  .filter((entry) => entry.module === module)
37
- .map((entry) => `<functionality module="${entry.module}" id="${entry.frid}"${status !== undefined ? ` status="${status}"` : ""}>\n${entry.spec}\n</functionality>`);
37
+ .map((entry) => `<functionality module="${entry.module}" id="${entry.funcId}"${status !== undefined ? ` status="${status}"` : ""}>\n${entry.spec}\n</functionality>`);
38
38
  return `<module name="${module}"${module === currentModule ? ' role="current"' : ""}>\n${items.join("\n\n")}\n</module>`;
39
39
  })
40
40
  .join("\n\n");
41
41
  }
42
- /** "functionality 3 of module `app`": a functionality, named so that no other module's can be meant. */
43
- export function functionalityOf(frid, moduleName) {
44
- return `functionality ${frid} of module \`${moduleName}\``;
42
+ /** "functional spec `app:add-task`": a functional spec, named so that no other module's can be meant. */
43
+ export function functionalityOf(funcId, moduleName) {
44
+ return `functional spec \`${moduleName}:${funcId}\``;
45
45
  }
46
46
  /**
47
47
  * How to read a ***plain specification: what each section is, and what the predefined concepts
@@ -66,24 +66,29 @@ export function specPrimer(options) {
66
66
  ? "- <test reqs>: how the conformance tests are written and run."
67
67
  : "- <test reqs>: how the conformance tests are written and run. They are not given to you: the conformance tests\n" +
68
68
  " are written apart from the code, from the specification.",
69
- "- <functional specs>: what the system does, one functionality at a time, numbered in order, each built on the ones",
70
- ' before it. Each functional spec is a <functionality module="..." id="...">; those in <already implemented> are',
71
- " in the code.",
69
+ "- <functional specs>: what the system does, one functionality at a time, in order, each built on the ones before",
70
+ ' it. Each functional spec is a <functionality module="..." id="...">, its id the func-id it has in its module; those',
71
+ " in <already implemented> are in the code.",
72
72
  "- Modules: a ***plain specification is split into modules, each a .plain file of its own. Modules are how the",
73
73
  " specification is written, in ***plain's own syntax - not how the code is laid out. A module can require others:",
74
74
  " it is built on their code, in one codebase with them, so the codebase holds their definitions, implementation",
75
75
  " reqs and functionalities as well as its own. A module can also import others: those hold only definitions and",
76
76
  " reqs - shared ones, never functional specs or code of their own - and what a module imports is its own. Every",
77
77
  " section reaches you grouped under the module each item belongs to - <module name=\"...\">, the modules in the",
78
- " order they are built on one another, the current one marked role=\"current\". Each module numbers its",
79
- " functional specs from 1, so a functionality is always given with its module and its id there: functionality 1",
80
- " of `base` and functionality 1 of `app` are two different functionalities.",
78
+ " order they are built on one another, the current one marked role=\"current\". A func-id is unique only in its",
79
+ " module, so a functional spec is always given with its module and its func-id there, `<module>:<func-id>`:",
80
+ " `base:export` and `app:export` are two different functionalities.",
81
81
  "- You work only in the codebase of the current module: it is all you can reach, and it already holds the code of",
82
82
  " every module it requires. Organise and write the code as the <implementation reqs> say - its layout, its",
83
83
  " structure, its conventions - never by the ***plain modules: they say where the specification puts an item,",
84
84
  " not where its code goes.",
85
- "- <acceptance tests>: under a functional spec, specific checks that functionality's implementation must pass.",
86
- " They carry the same authority as the functional spec itself.",
85
+ ...(options.testReqs
86
+ ? [
87
+ "- <conformance tests> and <acceptance tests>: under a functional spec, its scenarios, each under its",
88
+ " `# <scenario-id>`: what its conformance tests check, and the checks its implementation must pass end to end.",
89
+ " They carry the same authority as the functional spec itself.",
90
+ ]
91
+ : []),
87
92
  "- <linked resources>: files the specification refers to as `[resource]name`. They are part of the specification.",
88
93
  " A linked resource is not in your workspace. When the code needs what one holds while it runs - a schema it",
89
94
  " validates against, a version it reports - it keeps exactly one copy, at the path its code-copy names in the",
@@ -93,6 +98,8 @@ export function specPrimer(options) {
93
98
  " code-copy - is code that lives elsewhere, linked so you know what your code plugs into: build on it as the",
94
99
  " specification says, and never copy it, nor recreate it or what it belongs to.",
95
100
  " An image among them is attached to your first message, numbered in the order they are listed: look at it.",
101
+ " One marked in-codebase is in your codebase already, at that path - it came from the base folder: use it from",
102
+ " there, and never copy it or make another.",
96
103
  "- <spec change>: a change to the specification, section by section, each section in its own tag - what it adds,",
97
104
  " what it changes (before and now), and what it removes.",
98
105
  "",
@@ -165,7 +172,7 @@ export const ERROR_CAUSE_RULES = [
165
172
  ];
166
173
  /** A section's tag - its name in every prompt: `<implementation reqs>`, `<acceptance tests>`. */
167
174
  export function sectionTag(key) {
168
- return key === ACCEPTANCE_TESTS ? "acceptance tests" : key;
175
+ return key === ACCEPTANCE_TESTS ? "acceptance tests" : key === CONFORMANCE_TESTS ? "conformance tests" : key;
169
176
  }
170
177
  export function section(key, items) {
171
178
  if (items.length === 0)
@@ -196,9 +203,35 @@ function linkedResourcesSection(resources) {
196
203
  return null;
197
204
  // An image is not text: the prompt says where it is - attached to the first message, by number - and what type.
198
205
  // A text resource has one home in the codebase, should the code need it: its path, made relative to the codebase.
199
- const rendered = resources.map((resource) => `<resource name="${resource.text}" path="${resource.target}"${resource.image !== undefined ? ` type="${resource.image.mimeType}"` : isCodeResource(resource.target) ? ' kind="code"' : ` code-copy="${codeCopyOf(resource.target)}"`}>\n${resource.content}\n</resource>`);
206
+ const rendered = resources.map((resource) => `<resource name="${resource.text}" path="${resource.target}"${resource.image !== undefined ? ` type="${resource.image.mimeType}"` : ""}${resource.inCodebase !== undefined ? ` in-codebase="${resource.inCodebase}"` : resource.image !== undefined ? "" : isCodeResource(resource.target) ? ' kind="code"' : ` code-copy="${codeCopyOf(resource.target)}"`}>\n${resource.content}\n</resource>`);
200
207
  return `<linked resources>\n${rendered.join("\n\n")}\n</linked resources>`;
201
208
  }
209
+ /** How many files of the base folder a coding agent is shown by name: the rest are counted. */
210
+ const BASE_LISTING = 200;
211
+ /**
212
+ * The base folder a codebase started from, for its coding agents (base-folder.ts): that it did, and the files that came
213
+ * from it - code to build on, files to use, or both: the block says neither, for it may be either.
214
+ */
215
+ export function baseFolderBlock(base) {
216
+ if (base === undefined)
217
+ return null;
218
+ const shown = base.files.slice(0, BASE_LISTING);
219
+ const more = base.files.length - shown.length;
220
+ return [
221
+ `<base folder name="${base.folder}">`,
222
+ `This codebase started from a copy of the base folder ${base.folder}. Whatever it holds - code to build on, files to`,
223
+ "use, or both - the specification may refer to, in words or by linking into it. These files came from it, and are here still:",
224
+ ...shown.map((file) => `- ${file}`),
225
+ ...(more > 0 ? [`- ... and ${more} more`] : []),
226
+ "</base folder>",
227
+ ].join("\n");
228
+ }
229
+ /** The rule for what came from the base folder, for an agent that changes the code: it stays, used where it is. */
230
+ export const BASE_FOLDER_RULES = [
231
+ "- What came from the base folder stays, used where it is, unless a functional spec asks otherwise: code in it is",
232
+ " built on - its structure, conventions and tests kept, its tests passing - and a file in it is used from its path,",
233
+ " never copied, moved or made again.",
234
+ ];
202
235
  /** How to work, for any agent writing code in this codebase from a spec. `mechanical`: the job writes no new tests. */
203
236
  function workingRules(hasCodebase, mechanical = false) {
204
237
  return [
@@ -235,7 +268,15 @@ export function reviewedSpec(input) {
235
268
  "",
236
269
  moduleSection(DEFINITIONS, specifications[DEFINITIONS] ?? [], input.moduleName, input.modules),
237
270
  moduleSection(NON_FUNCTIONAL_REQUIREMENTS, specifications[NON_FUNCTIONAL_REQUIREMENTS] ?? [], input.moduleName, input.modules),
238
- alreadyImplemented(input.moduleName, functionalities, required),
271
+ alreadyImplemented(input.moduleName, functionalities, input.funcIds, required),
272
+ // What came from the base folder is the codebase's start, not this work: kept, it is neither scope crept in nor left over.
273
+ ...(input.base !== undefined
274
+ ? [
275
+ baseFolderBlock(input.base),
276
+ "What came from the base folder is not this work's: a file from it that no functional spec names is kept as it is -",
277
+ "not scope crept in, nor something left to clean up. A change to it, or its removal, that no functional spec asks for is.",
278
+ ]
279
+ : []),
239
280
  "<new in this work>",
240
281
  specChangeBlock(input.change ?? "(the change is not known)"),
241
282
  "</new in this work>",
@@ -249,12 +290,28 @@ export function reviewedSpec(input) {
249
290
  */
250
291
  const RAW_CHANGE_NOTE = "The change could not be told section by section, so it is the diff of the .plain files, as the person made it. There,\n" +
251
292
  "each section is headed by its name in stars: ***definitions*** is <definitions>, ***implementation reqs*** is\n" +
252
- "<implementation reqs>, ***test reqs*** is <test reqs>, ***functional specs*** is <functional specs>, and\n" +
253
- "***acceptance tests*** under a functional spec is its <acceptance tests>.";
293
+ "<implementation reqs>, ***test reqs*** is <test reqs>, and ***functional specs*** is <functional specs>, where each\n" +
294
+ "functional spec is a `# <func-id>` heading and its text under it.";
295
+ /** The same, for a conformance agent: a functional spec's scenarios are in a test plain file of their own. */
296
+ const RAW_CHANGE_NOTE_SCENARIOS = `${RAW_CHANGE_NOTE}\nA test plain file - tests/<module>/<func-id>.plain, its module: and func_id: at its top - holds one functional\n` +
297
+ "spec's scenarios: ***conformance tests*** is its <conformance tests> and ***acceptance tests*** its <acceptance tests>,\n" +
298
+ "each scenario a `# <scenario-id>` heading and its text.";
299
+ /**
300
+ * A raw diff of the .plain files without the hunks of test plain files: a coding agent never sees scenarios, which are
301
+ * the conformance tests'. A test plain file is told by its own frontmatter - `func_id:` - since its folder is the
302
+ * project's to name.
303
+ */
304
+ export function withoutTestPlainFiles(diff) {
305
+ return diff
306
+ .split(/^(?=diff --git )/m)
307
+ .filter((file) => !/^[ +-]func_id:/m.test(file))
308
+ .join("");
309
+ }
254
310
  /** A change in its tag: told section by section, or - when it could not be - the raw diff, with which tag each heading is. */
255
- export function specChangeBlock(change) {
311
+ export function specChangeBlock(change, scenarios = false) {
256
312
  const raw = /^diff --git |^@@ |^[+-]{3} /m.test(change);
257
- return `<spec change>\n${raw ? `${RAW_CHANGE_NOTE}\n\n` : ""}${change.trim()}\n</spec change>`;
313
+ const shown = raw && !scenarios ? withoutTestPlainFiles(change) : change;
314
+ return `<spec change>\n${raw ? `${scenarios ? RAW_CHANGE_NOTE_SCENARIOS : RAW_CHANGE_NOTE}\n\n` : ""}${shown.trim()}\n</spec change>`;
258
315
  }
259
316
  /** The sections as the change is told, in the order a spec has them. Test reqs are the conformance tests', not the code's. */
260
317
  const CHANGE_SECTIONS = [
@@ -263,7 +320,7 @@ const CHANGE_SECTIONS = [
263
320
  { key: FUNCTIONAL_REQUIREMENTS, title: "functional specs" },
264
321
  { key: "required functional specs", title: "functional specs of the modules this one requires" },
265
322
  ];
266
- /** A changed item's text: its markdown, and its acceptance tests under it. */
323
+ /** A changed item's text: its markdown - a functional spec's body, named above it - and its scenarios under it, where the tree has them. */
267
324
  function itemText(serialized) {
268
325
  if (serialized === undefined)
269
326
  return "";
@@ -276,13 +333,8 @@ function itemText(serialized) {
276
333
  }
277
334
  if (typeof item === "string")
278
335
  return item;
279
- const { markdown, [ACCEPTANCE_TESTS]: tests } = (item ?? {});
280
- const lines = [markdown ?? JSON.stringify(item)];
281
- // Each test is an item of the tree, its text in its markdown - as spec-steps.ts `itemText` reads it.
282
- const testText = (test) => (typeof test === "string" ? test : (test?.markdown ?? JSON.stringify(test)));
283
- if (Array.isArray(tests) && tests.length > 0)
284
- lines.push(" <acceptance tests>", ...tests.map((test) => ` ${testText(test).replace(/\n/g, "\n ")}`), " </acceptance tests>");
285
- return lines.join("\n");
336
+ const { markdown, [CONFORMANCE_TESTS]: conformance, [ACCEPTANCE_TESTS]: acceptance } = (item ?? {});
337
+ return [markdown ?? JSON.stringify(item), ...scenarioLines("conformance tests", conformance), ...scenarioLines("acceptance tests", acceptance)].join("\n");
286
338
  }
287
339
  const indent = (text, by) => text.split("\n").map((line) => `${by}${line}`).join("\n");
288
340
  /** A changed item, read back: undefined when its line is not one. */
@@ -297,7 +349,7 @@ const parsed = (serialized) => {
297
349
  /**
298
350
  * A spec change as an agent reads it: section by section, what was added, what changed - before and now - and what
299
351
  * was removed. A definition taken out and put back reworded, or moved, is one change to it, not two. Every item is
300
- * named so no other module's can be meant: a functional spec by its module and number, and a definition or req of a
352
+ * named so no other module's can be meant: a functional spec by its module and func-id, and a definition or req of a
301
353
  * module below by its module; `moduleName` is the module whose codebase it is - whose own items carry no module.
302
354
  */
303
355
  export function describeSpecChange(changes, moduleName) {
@@ -309,14 +361,14 @@ export function describeSpecChange(changes, moduleName) {
309
361
  const here = changes.filter((change) => change.section === key);
310
362
  if (here.length === 0)
311
363
  continue;
312
- /** Which item it is: a functional spec by its module and number, a definition by its concept, and one of a module below with that module. */
364
+ /** Which item it is: a functional spec by its module and func-id, a definition by its concept, and one of a module below with that module. */
313
365
  const labelOf = (change, line) => {
314
366
  const item = parsed(line);
315
367
  const below = item?.module !== undefined && item.module !== moduleName ? ` of module \`${item.module}\`` : "";
316
368
  if (key === FUNCTIONAL_REQUIREMENTS)
317
- return moduleName !== undefined ? functionalityOf(String(change.position), moduleName) : `functionality ${change.position}`;
369
+ return moduleName !== undefined ? functionalityOf(item?.id ?? "", moduleName) : `functional spec \`${item?.id ?? ""}\``;
318
370
  if (key === REQUIRED_FUNCTIONAL_SPECS)
319
- return item?.module !== undefined && item.frid !== undefined ? functionalityOf(item.frid, item.module) : `functionality ${change.position}${below}`;
371
+ return item?.module !== undefined && item.id !== undefined ? functionalityOf(item.id, item.module) : `functional spec \`${item?.id ?? ""}\`${below}`;
320
372
  if (key === DEFINITIONS)
321
373
  return `${conceptOf(itemText(line)) ?? `item ${change.position}`}${below}`;
322
374
  return `item ${change.position}${below}`;
@@ -417,7 +469,7 @@ export function buildSpecDiffPrompt(input) {
417
469
  ...input.required,
418
470
  ...specifications[FUNCTIONAL_REQUIREMENTS].map((spec, index) => ({
419
471
  module: input.moduleName,
420
- frid: String(index + 1),
472
+ funcId: input.funcIds[index] ?? "",
421
473
  spec,
422
474
  })),
423
475
  ];
@@ -427,7 +479,9 @@ export function buildSpecDiffPrompt(input) {
427
479
  "",
428
480
  input.empty
429
481
  ? "Nothing is built yet: the working directory is empty, and the change is the whole specification."
430
- : "The working directory holds the code built so far, from the specs below - the specs before the change.",
482
+ : input.base !== undefined && input.funcIds.length === 0 && input.required.length === 0
483
+ ? "Nothing is built from the specification yet: the working directory holds what the base folder had (<base folder> below), and the change is the whole specification."
484
+ : "The working directory holds the code built so far, from the specs below - the specs before the change.",
431
485
  "The specification the code is built from follows - its functional specs in <already implemented> - and then",
432
486
  "<spec change>, the change to it, section by section: the specification below with the change applied is what the",
433
487
  "code has to do once you are done. Your task is to apply the change to the code - everything it adds, changes and",
@@ -437,19 +491,21 @@ export function buildSpecDiffPrompt(input) {
437
491
  moduleSection(NON_FUNCTIONAL_REQUIREMENTS, specifications[NON_FUNCTIONAL_REQUIREMENTS], input.moduleName, input.modules),
438
492
  all.length === 0 ? null : `<already implemented>\n${functionalitiesByModule(all, input.moduleName, "implemented")}\n</already implemented>`,
439
493
  input.codebase ? `<codebase>\n${input.codebase}\n</codebase>` : null,
494
+ baseFolderBlock(input.base),
440
495
  linkedResourcesSection(input.linkedResources),
441
496
  input.changes !== undefined && input.changes.length > 0
442
497
  ? specChangeBlock(describeSpecChange(input.changes, input.moduleName))
443
- : `<spec change>\n${RAW_CHANGE_NOTE}\n\n${input.diff.trim()}\n</spec change>`,
498
+ : `<spec change>\n${RAW_CHANGE_NOTE}\n\n${withoutTestPlainFiles(input.diff).trim()}\n</spec change>`,
444
499
  "",
445
500
  ...(input.cases !== undefined
446
- ? codeCasesBlock(input.cases)
501
+ ? codeCasesBlock(input.cases, input.base !== undefined)
447
502
  : [
448
503
  "- Implement what the change adds. Update what it changes. Remove what it removes - its code and its",
449
504
  " unit tests - where nothing that remains still needs it.",
450
505
  "- A change to a definition or an implementation req reaches every functional spec that relies on it. Follow it there.",
451
506
  ]),
452
507
  "- Everything the change does not touch must keep working, and its tests passing.",
508
+ ...(input.base !== undefined ? BASE_FOLDER_RULES : []),
453
509
  ...workingRules(Boolean(input.codebase), input.cases?.mechanical === true),
454
510
  ];
455
511
  return parts.filter((part) => part !== null).join("\n");
@@ -468,7 +524,7 @@ export function buildConformancePrompt(input) {
468
524
  const forChange = input.diff !== undefined;
469
525
  const target = functionalities[functionalities.length - 1];
470
526
  if (target === undefined && !forChange)
471
- throw new Error(`Functionality ${input.frid} has no specification text.`);
527
+ throw new Error(`Functional spec ${input.funcId} has no specification text.`);
472
528
  const parts = [
473
529
  specPrimer({ testReqs: true }),
474
530
  `You are writing conformance tests for the \`${input.moduleName}\` specification.`,
@@ -492,12 +548,11 @@ export function buildConformancePrompt(input) {
492
548
  "tests folder and nothing else - so do not look for it: no searching parent folders, absolute paths or /tmp.",
493
549
  ]),
494
550
  "An agent that explored the code wrote down what you need of it, in <how to invoke it>: how it is",
495
- "built and run, how each functionality is reached from outside, what it needs around it, and a plan for",
496
- "these tests. Write the conformance tests from this prompt - the specification, that account and its",
497
- "plan - and what is in your tests folder: the tests already there show how the suite is built and run.",
498
- "Every assertion comes from the specification, never from what the account says the code does. Where the",
499
- "code and the specification disagree, the test follows the specification and fails. The plan is a",
500
- "starting point: where it and the specification differ, the specification is right.",
551
+ "built and run, how each functionality is reached from outside, and what it needs around it. Write the",
552
+ "conformance tests from this prompt - the specification, its scenarios and that account - and what is in your",
553
+ "tests folder: the tests already there show how the suite is built and run. Every assertion comes from the",
554
+ "scenarios and the specification, never from what the account says the code does. Where the code and the",
555
+ "specification disagree, the test follows the specification and fails.",
501
556
  "The conformance testing script at the top of this prompt is how your tests are built and run: read what it",
502
557
  "builds or installs before your tests run, and so what they can depend on or start to reach the software.",
503
558
  "Write the tests' own build - its dependencies, how it starts the software - to fit that script.",
@@ -530,24 +585,24 @@ export function buildConformancePrompt(input) {
530
585
  // user registered before one logs in - so its tests can set that up through the same
531
586
  // public entry points. Only the functionality below is under test. For a diff, every
532
587
  // functionality is the spec as it now stands.
533
- alreadyImplemented(input.moduleName, input.diff !== undefined ? functionalities : functionalities.slice(0, -1), input.required ?? []),
588
+ alreadyImplemented(input.moduleName, input.diff !== undefined ? functionalities : functionalities.slice(0, -1), input.funcIds, input.required ?? []),
534
589
  ];
535
590
  const resources = linkedResourcesSection(input.linkedResources);
536
591
  if (resources !== null)
537
592
  parts.push(resources);
538
593
  parts.push(input.diff !== undefined
539
- ? specChangeBlock(input.diff)
540
- : `<functionality module="${input.moduleName}" id="${input.frid}">\n${target}\n</functionality>`, input.diff !== undefined
541
- ? touchedAcceptanceTests(input.touched ?? [])
542
- : numberedAcceptanceTests(input.frid, specifications[ACCEPTANCE_TESTS] ?? []), `<files in the build>\n${input.manifest.join("\n")}\n</files in the build>`, input.existingTests && input.existingTests.length > 0
594
+ ? specChangeBlock(input.diff, true)
595
+ : `<functionality module="${input.moduleName}" id="${input.funcId}">\n${target}\n</functionality>`, input.diff !== undefined
596
+ ? touchedScenarios(input.moduleName, input.touched ?? [])
597
+ : scenariosBlock(input.moduleName, [{ funcId: input.funcId, conformance: specifications[CONFORMANCE_TESTS] ?? [], acceptance: specifications[ACCEPTANCE_TESTS] ?? [] }]), `<files in the build>\n${input.manifest.join("\n")}\n</files in the build>`, input.existingTests && input.existingTests.length > 0
543
598
  ? [
544
599
  `<conformance tests already here>\n${input.existingTests.join("\n")}\n</conformance tests already here>`,
545
600
  "",
546
601
  "This directory holds the conformance tests of every functionality built so far - this module's",
547
602
  "and those of the modules it requires, copied up with their code. Tests for other functionalities",
548
603
  "must keep passing: leave them as they are. If tests for this functionality are among them, from an",
549
- "earlier version of the specification, update them to it: keep the coverage, change only what the",
550
- "specification changed.",
604
+ "earlier version of the specification, bring them in line with its scenarios as they are now: a test for",
605
+ "each scenario, and none for a scenario taken out.",
551
606
  ].join("\n")
552
607
  : null, "", ...(input.diff !== undefined
553
608
  ? [
@@ -555,24 +610,23 @@ export function buildConformancePrompt(input) {
555
610
  "conformance tests in line with the specs as they now stand.",
556
611
  "",
557
612
  ...(input.cases !== undefined
558
- ? conformanceCasesBlock(input.cases)
613
+ ? conformanceCasesBlock(input.cases, input.moduleName)
559
614
  : [
560
- "- Add tests for behaviour the change adds, change the tests of behaviour it changes, and remove the",
561
- " tests of behaviour it removes. Leave the tests of everything the change does not touch as they are.",
562
- "- The change is tested whatever it touches: a definition, an implementation req, the test reqs, as",
563
- " well as a functional spec. Where it changes what built software must do or how the tests reach it,",
564
- " the tests check that - with or without a functional spec that uses it yet.",
615
+ "- Write tests for the scenarios the change adds, change the tests of the scenarios it changes, and remove",
616
+ " the tests of the scenarios it removes. Leave the tests of everything the change does not touch as they are.",
617
+ "- Where the change is to a definition, an implementation req or the test reqs, adapt the tests there are",
618
+ " to it: write no test that is not a scenario's.",
565
619
  ]),
566
- "- A test is removed only because the spec no longer asks for what it checks. Say which, and why.",
620
+ "- A test is removed only because its scenario is gone, or no longer asks for what it checks. Say which, and why.",
567
621
  ]
568
622
  : [
569
- `Write conformance tests for ${functionalityOf(input.frid, input.moduleName)}.`,
623
+ `Write conformance tests for ${functionalityOf(input.funcId, input.moduleName)}.`,
570
624
  "",
571
- `- Test ${functionalityOf(input.frid, input.moduleName)} only. What is listed as already implemented is there to be`,
625
+ `- Test ${functionalityOf(input.funcId, input.moduleName)} only. What is listed as already implemented is there to be`,
572
626
  " used - to set up the state a test needs - not tested again.",
573
627
  ]), "- Write them in your working directory - the module's tests folder - and nowhere else: never in the", " code folder, never beside the unit tests. That folder is the conformance suite; the harness runs it,", " and nothing else, as one. When it runs, the tests are put together with a copy of the build, so refer", " to the built files by the paths in <files in the build>, resolved against the current working", " directory rather than against your test file's own location.", "- Organise the tests however suits them - files, folders, shared helpers. Make it clear which", forChange
574
628
  ? " functionality a test covers, so its tests can be found and updated later."
575
- : ` functionality a test covers, so the tests of ${functionalityOf(input.frid, input.moduleName)} can be found and updated later.`, ...(forChange ? changeTestKinds() : testKinds(input.frid, (specifications[ACCEPTANCE_TESTS] ?? []).length)), "- Test observable behaviour only: output, exit codes, files produced, values returned by the", " public entry point. Never assert on internal names, private helpers or implementation detail.", "- <test reqs> say how conformance tests must be written and run. Follow them.", ...(input.invocation !== undefined
629
+ : ` functionality a test covers, so the tests of ${functionalityOf(input.funcId, input.moduleName)} can be found and updated later.`, ...scenarioRules(input.moduleName, forChange ? undefined : input.funcId), "- Test observable behaviour only: output, exit codes, files produced, values returned by the", " public entry point. Never assert on internal names, private helpers or implementation detail.", "- <test reqs> say how conformance tests must be written and run. Follow them.", ...(input.invocation !== undefined
576
630
  ? [
577
631
  "- Your shell holds the tests folder, and the code read only. Run the tests with run_conformance_tests:",
578
632
  " it puts the whole suite together with a copy of the build and runs the project's conformance",
@@ -603,67 +657,46 @@ export function buildConformancePrompt(input) {
603
657
  return parts.filter((part) => part !== null).join("\n");
604
658
  }
605
659
  /**
606
- * For a spec change: the acceptance tests of the functionalities it touches, numbered as their tests
607
- * are labelled - each implemented as a test of its own, as a functionality's own run has them.
660
+ * Functional specs' scenarios as the conformance agent carries them out: each under its label, the conformance
661
+ * scenarios and then the acceptance ones, in the tag of each kind.
608
662
  */
609
- function touchedAcceptanceTests(touched) {
610
- const withTests = touched.filter((item) => item.acceptanceTests.length > 0);
611
- if (withTests.length === 0)
612
- return null;
613
- const rendered = withTests.flatMap((item) => item.acceptanceTests.map((test, index) => `${acceptanceLabel(item.frid, index + 1)}: ${acceptanceText(test)}`));
614
- return [
615
- "The acceptance tests of the functional specs the change touches:",
616
- `<acceptance tests>\n${rendered.join("\n\n")}\n</acceptance tests>`,
617
- "Each of these is implemented as a test of its own, doing every step it lists and asserting every expectation it",
618
- "states, and labelled as it is numbered here - bring the ones already there in line with the change.",
619
- ].join("\n");
663
+ export function scenariosBlock(module, specs) {
664
+ const kind = (name) => specs.flatMap((spec) => scenariosOf(spec).filter((scenario) => scenario.kind === name).map((scenario) => scenarioText(spec.module ?? module, spec.funcId, scenario)));
665
+ const conformance = kind("conformance");
666
+ const acceptance = kind("acceptance");
667
+ const blocks = [
668
+ ...(conformance.length > 0 ? [`<conformance tests>\n${conformance.join("\n\n")}\n</conformance tests>`] : []),
669
+ ...(acceptance.length > 0 ? [`<acceptance tests>\n${acceptance.join("\n\n")}\n</acceptance tests>`] : []),
670
+ ];
671
+ return blocks.length > 0 ? blocks.join("\n\n") : null;
620
672
  }
621
- /** The functionality's acceptance tests, numbered as their tests are labelled. */
622
- function numberedAcceptanceTests(frid, items) {
623
- if (items.length === 0)
673
+ /** For a spec change: the scenarios of the functional specs it touches - each carried out, and labelled as it is here. */
674
+ function touchedScenarios(module, touched) {
675
+ const block = scenariosBlock(module, touched);
676
+ if (block === null)
624
677
  return null;
625
- const rendered = items.map((item, i) => `${acceptanceLabel(frid, i + 1)}: ${acceptanceText(item)}`);
626
- return `<acceptance tests>\n${rendered.join("\n\n")}\n</acceptance tests>`;
627
- }
628
- /** The two kinds of test, for a spec change: each labelled with what it covers. */
629
- function changeTestKinds() {
630
678
  return [
631
- "- Conformance tests come from the specs themselves: what they say the software does, its edge cases, its",
632
- " errors - written as if no acceptance tests had been given. They do not copy the acceptance tests.",
633
- `- Label them: files whose names say conformance, each with a comment at its top naming the functionality it`,
634
- ` covers - "${conformanceLabel("<id>")}" - or, for one that covers no functional spec yet, "${CHANGE_LABEL}".`,
635
- "- Acceptance tests - the ones in <acceptance tests> - are implemented separately from the conformance tests,",
636
- " each as its own test, doing what it says, in files whose names say acceptance.",
637
- `- Label each acceptance test with a comment right above it: "${acceptanceLabel("<id>", 1)}:" and the text`,
638
- " of the acceptance test, numbered as the spec lists them.",
639
- ];
679
+ "The scenarios of the functional specs the change touches, as they are now:",
680
+ block,
681
+ "Each of these is carried out by at least one test of its own, doing every step it lists and asserting every",
682
+ "expectation it states, and labelled as it is here - bring the tests already there in line with the change.",
683
+ ].join("\n");
640
684
  }
641
- /** What a conformance test covering no functional spec yet - a definition, a req - is labelled. */
642
- export const CHANGE_LABEL = "conformance tests of the spec";
643
685
  /**
644
- * The two kinds of test, kept apart and labelled (see test-kinds.ts). `acceptance` is how many
645
- * acceptance tests the functionality has; for a spec change, it is left to the change.
686
+ * What the tests are, and how they are labelled (see test-kinds.ts): exactly the scenarios, at least one test each,
687
+ * and nothing invented beside them. `funcId`: the functional spec under test; for a spec change, those it touches.
646
688
  */
647
- function testKinds(frid, acceptance) {
648
- const conformance = [
649
- "- Conformance tests come from the functional spec itself: what it says the functionality does, its edge",
650
- " cases, its errors - written as if no acceptance tests had been given. They do not copy the acceptance",
651
- " tests, and do not stop where the acceptance tests stop.",
652
- `- Label them: files whose names say conformance, each with a comment "${conformanceLabel(frid)}" at its top`,
653
- " (for a spec change, the functionality each covers).",
654
- ];
655
- if (acceptance === 0)
656
- return conformance;
689
+ function scenarioRules(module, funcId) {
690
+ const id = funcId ?? "<func-id>";
657
691
  return [
658
- ...conformance,
659
- acceptance === undefined
660
- ? "- Acceptance tests - the ones in <acceptance tests> - are implemented separately from the"
661
- : `- Separately from them, implement each of the ${acceptance} acceptance test${acceptance === 1 ? "" : "s"} above as its own test, doing what it`,
662
- acceptance === undefined
663
- ? " conformance tests, each as its own test, doing what it says, in files whose names say acceptance."
664
- : " says, in files whose names say acceptance. Never fold one into a conformance test, even where they overlap.",
665
- `- Label each acceptance test with a comment right above it: "${acceptanceLabel(frid, 1)}:" and the text`,
666
- " of the acceptance test, numbered as above.",
692
+ "- The tests carry out the scenarios - those in <conformance tests> and <acceptance tests> - exactly: at least one",
693
+ " test for each, doing what it says and asserting what it expects. Write no test that is not a scenario's: no",
694
+ " edge case, error or behaviour of your own beyond them, however much the functional spec says - the scenarios",
695
+ " are what is tested.",
696
+ `- Label each file with a comment at its top naming the functional spec it tests: "${conformanceLabel(module, id)}" -`,
697
+ " a file that tests several has one such line for each.",
698
+ `- Label each test with a comment right above it naming the scenario it carries out: "${scenarioLabel("conformance", "<scenario-id>", module, id)}"`,
699
+ ` or "${scenarioLabel("acceptance", "<scenario-id>", module, id)}".`,
667
700
  ];
668
701
  }
669
702
  /**
@@ -679,14 +712,15 @@ export function buildRegressionFixPrompt(input) {
679
712
  specPrimer({ testReqs: false }),
680
713
  `The conformance suite for \`${input.moduleName}\` is failing.`,
681
714
  "",
682
- `It covers functionalities ${input.frids.join(", ")} of module \`${input.moduleName}\` together. A functionality that passed`,
715
+ `It covers functional specs ${input.funcIds.join(", ")} of module \`${input.moduleName}\` together. A functional spec that passed`,
683
716
  "on its own can still be broken by a later one, which is what this run is for.",
684
717
  "",
685
718
  moduleSection(DEFINITIONS, specifications[DEFINITIONS], input.moduleName, input.modules),
686
719
  moduleSection(NON_FUNCTIONAL_REQUIREMENTS, specifications[NON_FUNCTIONAL_REQUIREMENTS], input.moduleName, input.modules),
687
720
  // All of it is in the code, the required modules' functionalities included, and any of it
688
721
  // may be what the latest change broke.
689
- alreadyImplemented(input.moduleName, specifications[FUNCTIONAL_REQUIREMENTS], input.required ?? []),
722
+ alreadyImplemented(input.moduleName, specifications[FUNCTIONAL_REQUIREMENTS], input.funcIds, input.required ?? []),
723
+ baseFolderBlock(input.base),
690
724
  `<conformance failure>\n${input.failure}\n</conformance failure>`,
691
725
  "",
692
726
  "Change the implementation so every functionality listed as already implemented works at the same time.",
@@ -696,6 +730,7 @@ export function buildRegressionFixPrompt(input) {
696
730
  "- Do not special-case the test. Make the behaviour the specification describes actually true.",
697
731
  "- Keep the existing unit tests passing. Run any tests you like with bash; before you finish, call",
698
732
  " run_conformance_tests and run_unit_tests - the project's scripts, run as the harness runs them.",
733
+ ...(input.base !== undefined ? BASE_FOLDER_RULES : []),
699
734
  ...INTEGRITY_RULES,
700
735
  ...ERROR_CAUSE_RULES,
701
736
  "- Keep the build's identity as it is: its name, coordinates, version, package and main class. Change one only",