@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,7 +1,7 @@
1
1
  import { DEFAULT_CONCEPTS, extractConceptsFromDefinition, extractConceptsFromSpecText } from "../plain/concepts.js";
2
- import { DEFINITIONS, FUNCTIONAL_REQUIREMENTS, REQUIRED_FUNCTIONAL_SPECS } from "../plain/spec.js";
2
+ import { ACCEPTANCE_TESTS, CONFORMANCE_TESTS, DEFINITIONS, FUNCTIONAL_REQUIREMENTS, REQUIRED_FUNCTIONAL_SPECS } from "../plain/spec.js";
3
3
  import { codeTree, conformanceTarget, marksRegenerateAgain } from "./records.js";
4
- import { applyChanges, changesBetween, parseTree, resourcesChanged, serializeTree } from "./spec-steps.js";
4
+ import { applyChanges, changesBetween, isMove, parseTree, resourcesChanged, serializeTree } from "./spec-steps.js";
5
5
  /**
6
6
  * The work, derived from the records - see docs/regenerating.md. Nothing here is stored: what is left
7
7
  * to build is the difference between what a record says is built and what the spec says now, worked
@@ -9,7 +9,7 @@ import { applyChanges, changesBetween, parseTree, resourcesChanged, serializeTre
9
9
  *
10
10
  * - Code work: from the code record to the spec as the code sees it.
11
11
  * - Conformance work: from the tests record to what the conformance tests are to be written for now -
12
- * the chosen functional specs the code is built from.
12
+ * the functional specs the code is built from that have scenarios.
13
13
  *
14
14
  * A job is a set of those changes. What it tells its agents follows from the functional specs it
15
15
  * touches - its cases - and a running job is stopped once the spec no longer wants what it makes.
@@ -19,11 +19,52 @@ export function codeWork(record, spec) {
19
19
  return changesBetween(serializeTree(codeTree(record ?? {})), serializeTree(codeTree(spec)));
20
20
  }
21
21
  /** Everything left to do in the conformance tests: from what the tests record says they are written for, to what they are to be now. */
22
- export function conformanceWork(tests, code, spec, chosen) {
23
- return changesBetween(serializeTree(tests ?? {}), serializeTree(conformanceTarget(code, tests, spec, chosen)));
22
+ export function conformanceWork(tests, code, spec) {
23
+ return changesBetween(serializeTree(tests ?? {}), serializeTree(conformanceTarget(code, tests, spec)));
24
+ }
25
+ /** Whether an item of a tree carries scenarios - a tree of the conformance tests' - rather than being the code's. */
26
+ const carriesScenarios = (item) => item !== undefined && (CONFORMANCE_TESTS in item || ACCEPTANCE_TESTS in item);
27
+ /** Every scenario of an item, by its kind and scenario-id. */
28
+ function scenariosIn(item) {
29
+ const found = new Map();
30
+ for (const kind of ["conformance", "acceptance"]) {
31
+ for (const scenario of (item?.[kind === "conformance" ? CONFORMANCE_TESTS : ACCEPTANCE_TESTS] ?? [])) {
32
+ found.set(`${kind} ${scenario.id}`, { kind, id: scenario.id, text: scenario.text, line: JSON.stringify([scenario.text.trim(), scenario.linked_resources ?? []]) });
33
+ }
34
+ }
35
+ return found;
36
+ }
37
+ /** What of a functional spec the code is built from: everything but its scenarios. */
38
+ function codeOf(item) {
39
+ if (item === undefined)
40
+ return "";
41
+ const { [CONFORMANCE_TESTS]: _conformance, [ACCEPTANCE_TESTS]: _acceptance, regenerate_again: _again, ...rest } = item;
42
+ return JSON.stringify({ ...rest, markdown: rest.markdown.trim() });
43
+ }
44
+ /** A functional spec's scenarios by case, from the item its tests were written for to the one they are to be. */
45
+ function scenarioCasesOf(spec, was, is, code) {
46
+ const before = scenariosIn(was);
47
+ const after = scenariosIn(is);
48
+ const plain = ({ kind, id, text }) => ({ kind, id, text });
49
+ const cases = { ...spec, added: [], changed: [], removed: [], unchanged: [] };
50
+ for (const [key, scenario] of after) {
51
+ const old = before.get(key);
52
+ if (old === undefined)
53
+ cases.added.push(plain(scenario));
54
+ else if (old.line !== scenario.line)
55
+ cases.changed.push(plain(scenario));
56
+ else
57
+ cases.unchanged.push(plain(scenario));
58
+ }
59
+ for (const [key, scenario] of before)
60
+ if (!after.has(key))
61
+ cases.removed.push(plain(scenario));
62
+ const moved = code ?? (was !== undefined && is !== undefined && codeOf(was) !== codeOf(is) ? "text" : undefined);
63
+ return moved !== undefined && cases.unchanged.length > 0 ? { ...cases, codeChanged: moved } : cases;
24
64
  }
25
65
  const functionalSpecs = (tree) => tree[FUNCTIONAL_REQUIREMENTS] ?? [];
26
- const firstLine = (markdown) => markdown.trim().split("\n")[0].replace(/^-\s*/, "").trim();
66
+ const inheritedSpecs = (tree) => tree[REQUIRED_FUNCTIONAL_SPECS] ?? [];
67
+ const firstLine = (markdown) => markdown.trim().split("\n")[0].trim();
27
68
  /** An item of a serialized tree, read back. */
28
69
  const itemOfLine = (line) => {
29
70
  if (line === undefined)
@@ -74,38 +115,44 @@ export function conceptsUsed(markdown, definitions) {
74
115
  export function casesOf(before, changes, options = {}) {
75
116
  const beforeText = serializeTree(before);
76
117
  const after = parseTree(applyChanges(beforeText, changes));
77
- const numberIn = (tree, item, position) => {
78
- if (item?.frid !== undefined)
79
- return item.frid;
80
- const specs = functionalSpecs(tree);
81
- const at = item === undefined ? -1 : specs.findIndex((spec) => spec.markdown === item.markdown);
82
- return String(at === -1 ? position : at + 1);
83
- };
84
118
  const own = options.module !== undefined ? { module: options.module } : {};
119
+ const touchedOf = (item, required) => ({
120
+ funcId: item.id,
121
+ text: firstLine(item.markdown),
122
+ ...(required ? (item.module !== undefined ? { module: item.module } : {}) : own),
123
+ });
85
124
  const add = [];
86
125
  const patch = [];
87
126
  const again = [];
88
127
  const removed = [];
128
+ /** Each functional spec touched, as the tests record has it and as it is to be: its scenarios' cases are worked out from them. */
129
+ const items = new Map();
130
+ const keyOf = (spec) => `${spec.module ?? ""}:${spec.funcId}`;
131
+ const seen = (spec, was, is) => {
132
+ items.set(keyOf(spec), { ...(was !== undefined ? { was } : {}), ...(is !== undefined ? { is } : {}) });
133
+ return spec;
134
+ };
89
135
  for (const change of changes) {
136
+ // A module below's functional specs are cased as the codebase's own are: the codebase at the tip holds their code.
137
+ if (change.section !== FUNCTIONAL_REQUIREMENTS && change.section !== REQUIRED_FUNCTIONAL_SPECS)
138
+ continue;
139
+ const required = change.section === REQUIRED_FUNCTIONAL_SPECS;
90
140
  const was = itemOfLine(change.before);
91
141
  const is = itemOfLine(change.after);
92
142
  // Marked to regenerate again: regenerated again, whatever else changed in its text - its own or, required, another module's.
93
143
  if (change.kind === "change" && is !== undefined && marksRegenerateAgain(was)) {
94
- const required = change.section === REQUIRED_FUNCTIONAL_SPECS ? is : undefined;
95
- if (change.section === FUNCTIONAL_REQUIREMENTS)
96
- again.push({ frid: numberIn(after, is, change.position), text: firstLine(is.markdown), ...own });
97
- else if (required !== undefined)
98
- again.push({ frid: `${required.frid ?? change.position}`, text: firstLine(is.markdown), ...(required.module !== undefined ? { module: required.module } : {}) });
144
+ again.push(seen(touchedOf(is, required), was, is));
99
145
  continue;
100
146
  }
101
- if (change.section !== FUNCTIONAL_REQUIREMENTS)
147
+ // Moved, and nothing else: none.
148
+ if (isMove(change))
102
149
  continue;
103
150
  if (change.kind === "add" && is !== undefined)
104
- add.push({ frid: numberIn(after, is, change.position), text: firstLine(is.markdown), ...own });
151
+ add.push(seen(touchedOf(is, required), undefined, is));
105
152
  else if (change.kind === "remove" && was !== undefined)
106
- removed.push({ frid: numberIn(before, was, change.position), text: firstLine(was.markdown), ...own });
153
+ removed.push(seen(touchedOf(was, required), was, undefined));
107
154
  else if (change.kind === "change" && is !== undefined)
108
- patch.push({ frid: numberIn(after, is, change.position), text: firstLine(is.markdown), ...own });
155
+ patch.push(seen(touchedOf(is, required), was, is));
109
156
  }
110
157
  // The concepts the job defines anew, changes or takes out.
111
158
  const redefined = new Set();
@@ -119,20 +166,43 @@ export function casesOf(before, changes, options = {}) {
119
166
  redefined.add(concept);
120
167
  }
121
168
  }
169
+ // Every functional spec of the chain, as it is after the job and as it was: those of the modules below, then the
170
+ // codebase's own - the order every case lists them in.
171
+ const chainOf = (tree) => [...inheritedSpecs(tree).map((item) => ({ item, required: true })), ...functionalSpecs(tree).map((item) => ({ item, required: false }))];
172
+ const chain = chainOf(after);
122
173
  if (redefined.size > 0) {
123
- const touched = new Set([...add, ...patch, ...again].filter((spec) => spec.module === options.module).map((spec) => spec.frid));
174
+ const touched = new Set([...add, ...patch, ...again].map(keyOf));
124
175
  const definitionsAfter = after[DEFINITIONS] ?? [];
125
176
  const definitionsBefore = before[DEFINITIONS] ?? [];
126
- functionalSpecs(after).forEach((item, index) => {
127
- const frid = item.frid ?? String(index + 1);
128
- if (touched.has(frid))
129
- return;
177
+ for (const { item, required } of chain) {
178
+ const spec = touchedOf(item, required);
179
+ if (touched.has(keyOf(spec)))
180
+ continue;
130
181
  const uses = new Set([...conceptsUsed(item.markdown, definitionsAfter), ...conceptsUsed(item.markdown, definitionsBefore)]);
131
182
  if ([...redefined].some((concept) => uses.has(concept)))
132
- patch.push({ frid, text: firstLine(item.markdown), ...own });
133
- });
134
- patch.sort((a, b) => Number(a.frid) - Number(b.frid));
183
+ patch.push(seen(spec, item, item));
184
+ }
135
185
  }
186
+ // In the chain's order: one taken out where it was.
187
+ const orderIn = (specs) => new Map(specs.map(({ item, required }, index) => [keyOf(touchedOf(item, required)), index]));
188
+ const inOrder = (list, order) => list.sort((a, b) => (order.get(keyOf(a)) ?? Number.MAX_SAFE_INTEGER) - (order.get(keyOf(b)) ?? Number.MAX_SAFE_INTEGER));
189
+ const order = orderIn(chain);
190
+ for (const list of [add, patch, again])
191
+ inOrder(list, order);
192
+ inOrder(removed, orderIn(chainOf(before)));
193
+ // The conformance tests' trees carry the scenarios: each functional spec touched, scenario by scenario.
194
+ const touchedItems = [...items.values()];
195
+ const scenarios = touchedItems.some(({ was, is }) => carriesScenarios(was) || carriesScenarios(is))
196
+ ? [
197
+ ...add.map((spec) => scenarioCasesOf(spec, undefined, items.get(keyOf(spec))?.is)),
198
+ ...patch.map((spec) => {
199
+ const { was, is } = items.get(keyOf(spec)) ?? {};
200
+ return scenarioCasesOf(spec, was, is, was === is ? "concept" : undefined);
201
+ }),
202
+ ...again.map((spec) => scenarioCasesOf(spec, items.get(keyOf(spec))?.was, items.get(keyOf(spec))?.is, "again")),
203
+ ...removed.map((spec) => scenarioCasesOf(spec, items.get(keyOf(spec))?.was, undefined)),
204
+ ]
205
+ : undefined;
136
206
  return {
137
207
  fresh: options.fresh === true,
138
208
  add,
@@ -140,6 +210,7 @@ export function casesOf(before, changes, options = {}) {
140
210
  again,
141
211
  delete: removed,
142
212
  mechanical: add.length === 0 && patch.length === 0 && again.length === 0 && removed.length === 0,
213
+ ...(scenarios !== undefined ? { scenarios } : {}),
143
214
  };
144
215
  }
145
216
  /** Two changes are the same change when they do the same to the same item. */
@@ -8,10 +8,10 @@ import { type AgentTally, type BuildingSpec, type SpecConflict, type TranscriptE
8
8
  * carries on while the agents work.
9
9
  */
10
10
  export type JobStatus = "queued" | "running" | "succeeded" | "failed" | "stopped";
11
- /** A functional spec a pulse covers: the module whose spec it is - the pulse's own, or one below it - and its number there. */
11
+ /** A functional spec a pulse covers: the module whose spec it is - the pulse's own, or one below it - and its func-id there. */
12
12
  export interface PulseSpec {
13
13
  module: string;
14
- frid: string;
14
+ funcId: string;
15
15
  }
16
16
  /**
17
17
  * The functional specs a pulse covers, by what it does to each (`cases.ts`): new, changed, regenerated again - its text
@@ -137,7 +137,7 @@ export function createJobRegistry(options) {
137
137
  ...(input.module !== undefined ? { module: input.module } : {}),
138
138
  specConflict(conflict) {
139
139
  job.specConflict = conflict;
140
- this.transcript({ kind: "stage", text: `the spec contradicts itself: functionalities ${conflict.specs.map((spec) => `${spec.module} ${spec.frid}`).join(" and ")} cannot both hold` });
140
+ this.transcript({ kind: "stage", text: `the spec contradicts itself: functional specs ${conflict.specs.map((spec) => `${spec.module}:${spec.funcId}`).join(" and ")} cannot both hold` });
141
141
  changed();
142
142
  },
143
143
  stage(label) {
@@ -117,6 +117,12 @@ export type LedgerEvent = {
117
117
  } | {
118
118
  kind: "loop.stopped";
119
119
  why: string;
120
+ } | {
121
+ kind: "loop.renamed";
122
+ what: "func-id" | "scenario-id";
123
+ from: string;
124
+ to: string;
125
+ spec: string;
120
126
  } | {
121
127
  kind: "planner.failed";
122
128
  step: "group" | "place" | "title";
@@ -233,6 +239,19 @@ export type LedgerEvent = {
233
239
  missing: string;
234
240
  source: string;
235
241
  sandbox: boolean;
242
+ } | {
243
+ kind: "advisor.classified";
244
+ spec: string;
245
+ verdict: "yes" | "no";
246
+ questions: string[];
247
+ asked: boolean;
248
+ } | {
249
+ kind: "advisor.dropped";
250
+ spec: string;
251
+ } | {
252
+ kind: "advisor.failed";
253
+ spec: string;
254
+ reason: string;
236
255
  };
237
256
  export type LedgerKind = LedgerEvent["kind"];
238
257
  /** One line of the ledger: the event, and where and when it happened. */
@@ -1,3 +1,4 @@
1
+ import { type MarshalledPlainSource } from "../plain/spec.ts";
1
2
  import { type ChunkAgents } from "./chunk-plan.ts";
2
3
  import { type SpecGap } from "./notes.ts";
3
4
  import { type Job, type JobRegistry, type PulseCases } from "./jobs.ts";
@@ -6,7 +7,7 @@ import { type ChainEntry } from "./requires.ts";
6
7
  import { type SpecSnapshot } from "./regenerate.ts";
7
8
  import { type JobSteering } from "./steering.ts";
8
9
  import { type SpecStep } from "./spec-steps.ts";
9
- import { type JobCases, type TouchedSpec } from "./drift.ts";
10
+ import { type JobCases } from "./drift.ts";
10
11
  /**
11
12
  * The work loop - see docs/regenerating.md.
12
13
  *
@@ -67,18 +68,18 @@ export interface ModuleStatus {
67
68
  steered?: string[];
68
69
  };
69
70
  /**
70
- * The jobs planned after it, in order: the module's code jobs, then its conformance jobs. A conformance job scheduled
71
- * for functional specs not all built yet - `waitsFor`, those that are not - comes after the module's last code job.
72
- * The module's conformance tests, wanted for all of it while its code is still to build - `afterCode` - come last:
73
- * they are planned into pulses only once the code is built, but are shown, so asking for them is seen to have worked.
71
+ * The jobs planned after it, in order: the module's code jobs, then its conformance jobs. The conformance tests of
72
+ * its functional specs with scenarios, behind while its code is still to build - `afterCode`, `specs` those
73
+ * functional specs as `module:func-id` - come last: they are planned into pulses only once the code is built, but are
74
+ * shown, so what follows the code is seen.
74
75
  */
75
76
  planned: Array<{
76
77
  title: string;
77
78
  cases: JobCases;
78
79
  track: Track;
79
- waitsFor?: string[];
80
80
  planning?: true;
81
81
  afterCode?: true;
82
+ specs?: string[];
82
83
  }>;
83
84
  /**
84
85
  * What the plan of its code did not do as asked: the person's wishes its planner did not follow and why, an answer
@@ -87,19 +88,33 @@ export interface ModuleStatus {
87
88
  planNotes?: string[];
88
89
  /** Why nothing runs there, when nothing does. */
89
90
  why?: string;
90
- /** What asking for conformance tests changed there, when it changed nothing: they were on for the whole module already. */
91
+ /** What asking for conformance tests changed there, when it changed nothing: they are written for what is built already. */
91
92
  conformanceNote?: string;
92
93
  /**
93
94
  * It now requires modules its code was not built on - `requires` - and is built only once its code is started
94
95
  * over on them: these codebases' code and tests taken out, kept in their history, and built again from the
95
- * bottom of the chain. Never done unasked: `startOver` does it.
96
+ * bottom of the chain. Or - `old` - these codebases were built when functional specs were numbered, before they had
97
+ * func-ids: nothing is built on them, and the whole chain is regenerated from scratch. Never done unasked:
98
+ * `startOver` does it.
96
99
  */
97
100
  startOver?: {
98
101
  requires: string[];
99
102
  codebases: string[];
103
+ old?: true;
100
104
  };
101
105
  /** The stack it is built with - its config.turri.<stack>.yaml over config.turri.yaml - when one is. */
102
106
  stack?: string;
107
+ /** Pure renames turri makes once the running pulse ends - a func-id or a scenario-id, nothing else with it: nothing regenerated. */
108
+ renames?: string[];
109
+ /**
110
+ * The base folder its codebase started from (base-folder.ts): the folder as `base-folder:` named it, and how many of
111
+ * the files that came from it the codebase has still - carried up with the code to a module built on it. None when it
112
+ * started from none, or has no code yet.
113
+ */
114
+ base?: {
115
+ folder: string;
116
+ files: number;
117
+ };
103
118
  }
104
119
  /** What is left to regenerate in a codebase - see `WorkLoop.drift`. */
105
120
  export interface DriftReport {
@@ -109,11 +124,6 @@ export interface DriftReport {
109
124
  code: JobCases | null | undefined;
110
125
  /** The conformance tests' work: null, none; undefined, none are written here. */
111
126
  conformance: JobCases | null | undefined;
112
- /** Conformance tests scheduled ahead of their code, and which of their functional specs are not built yet. */
113
- scheduled: Array<{
114
- frids: string[];
115
- unbuilt: string[];
116
- }>;
117
127
  running?: {
118
128
  jobId: string;
119
129
  title: string;
@@ -121,9 +131,23 @@ export interface DriftReport {
121
131
  /** Why nothing is regenerated there: halted, or its spec does not parse. */
122
132
  why?: string;
123
133
  startOver?: ModuleStatus["startOver"];
134
+ /** Pure renames not made yet - a func-id or a scenario-id, nothing else changed with it: made by turri, nothing regenerated. */
135
+ renames?: string[];
136
+ /** How its base folder stands against what its codebase started from, when they do not agree (base-folder.ts). */
137
+ baseFolder?: string;
138
+ /** conform named these functional specs, as `module:func-id`: the conformance tests are written for them alone until the module is asked for again. */
139
+ conformanceOnly?: string[];
140
+ /** Conformance tests are off here (`conformance-tests:`): none written or run. */
141
+ conformanceOff?: true;
124
142
  }
125
143
  /** What is left to regenerate, as the conversation and the command line read it. */
126
144
  export declare function describeDrift(reports: DriftReport[]): string;
145
+ /** A codebase as the loop has just looked at it: its chain, its spec now, and what its code is built from (`codeTree` form). */
146
+ export interface SeenSpec {
147
+ chain: ChainEntry[];
148
+ tree: MarshalledPlainSource;
149
+ built: MarshalledPlainSource;
150
+ }
127
151
  export interface WorkLoop {
128
152
  /**
129
153
  * Bring a spec's module up to the spec as the .plain files have it now - in the codebases where its
@@ -138,12 +162,12 @@ export interface WorkLoop {
138
162
  specChange?: boolean;
139
163
  }): Promise<ModuleStatus[]>;
140
164
  /**
141
- * Regenerate functional specs of a module again, their spec changed or not: each is marked to regenerate again in
165
+ * Regenerate functional specs of a module again, their spec changed or not - each named by its func-id: each is marked to regenerate again in
142
166
  * the record of every codebase it is built in - committed - and they are brought up to their spec, the marked ones
143
167
  * planned as any change is. Their code is there: the jobs that regenerate them are told to regenerate it again, not
144
168
  * to build it anew.
145
169
  */
146
- regenerateAgain(plainFile: string, frids: string | string[], options?: {
170
+ regenerateAgain(plainFile: string, funcIds: string | string[], options?: {
147
171
  model?: string;
148
172
  guidance?: string;
149
173
  }): Promise<ModuleStatus[]>;
@@ -158,10 +182,9 @@ export interface WorkLoop {
158
182
  * again, it clears a halt. Says what it planned.
159
183
  */
160
184
  conformance(plainFile: string, options?: {
161
- frids?: string[];
185
+ funcIds?: string[];
162
186
  model?: string;
163
187
  separately?: boolean;
164
- groups?: string[][];
165
188
  }): Promise<ModuleStatus[]>;
166
189
  /**
167
190
  * Start over the codebases of a spec's module that now require a module their code was not built on (see
@@ -229,8 +252,8 @@ export interface MapPulse {
229
252
  before: string;
230
253
  after: string;
231
254
  /**
232
- * A conformance pulse scheduled ahead of its code - not planned, so with no trees: the texts of the functional specs
233
- * it writes tests for.
255
+ * The conformance tests that follow the code still to build - not planned, so with no trees: the functional specs they
256
+ * are written for, each as `module:func-id`.
234
257
  */
235
258
  specs?: string[];
236
259
  }
@@ -245,11 +268,11 @@ export interface MapCodebase {
245
268
  planning?: Track;
246
269
  /** The module below it that it waits for. */
247
270
  waitsFor?: string;
248
- /** Its conformance tests are on: by the config, or asked for this session. */
271
+ /** Whether any of its functional specs has scenarios, and so conformance tests. */
249
272
  conformance: boolean;
250
273
  /** Why nothing runs there, when nothing does. */
251
274
  why?: string;
252
- /** What asking for conformance tests changed there, when it changed nothing: they were on for the whole module already. */
275
+ /** What asking for conformance tests changed there, when it changed nothing: they are written for what is built already. */
253
276
  conformanceNote?: string;
254
277
  /**
255
278
  * Halted, or the planner found no plan: it needs the person. `pulse`, the pulse that did not land; `cause`, what went
@@ -279,11 +302,13 @@ export interface ModuleCompletion {
279
302
  specified: number;
280
303
  /** Of those, the ones the delivered codebase's code record has as the spec has them now. */
281
304
  completed: number;
282
- /** Of the completed ones, those its conformance tests are recorded as written for, as the spec has them now. */
305
+ /** Its own functional specs with scenarios: those that are tested - one with no test plain file is not. */
306
+ withScenarios: number;
307
+ /** Of the completed ones, those its conformance tests are recorded as written for, as the spec has them now - their scenarios too. */
283
308
  conformanceTested: number;
284
- /** Those conformance-tested ones, by number: for a host that shows each functional spec's state. */
309
+ /** Those conformance-tested ones, by func-id: for a host that shows each functional spec's state. */
285
310
  tested: string[];
286
- /** Its functional specs not completed, by number. */
311
+ /** Its functional specs not completed, by func-id. */
287
312
  incomplete: string[];
288
313
  }
289
314
  /**
@@ -313,25 +338,6 @@ export declare function createWorkLoop(options: {
313
338
  run?: JobRunner;
314
339
  /** Writes and runs a conformance job's tests. Default: a spec diff's conformance run, the whole suite with it. */
315
340
  runConformance?: JobRunner;
316
- /** Whether the config has a module's conformance tests on. Default: its `conformance-tests:`, off unless it turns them on. */
317
- conformanceOn?: (plainFile: string) => boolean;
318
- /**
319
- * Whether a module's conformance tests are for the functional specs chosen, not every one: those with acceptance
320
- * tests, those the conversation asked for, and those tested already. Default: `conformance-tests: chosen`.
321
- */
322
- conformanceChosen?: (plainFile: string) => boolean;
323
- /**
324
- * Under `conformance-tests: chosen`, asked what a code job that landed built or changed that no conformance test is
325
- * for - by whoever chooses without a conversation (`turri regenerate`) - before the job ends: the functional specs it
326
- * names are scheduled as conform schedules them. The conversation is told in the job's report instead.
327
- */
328
- onChoose?: (plainFile: string, specs: TouchedSpec[], budget: number | undefined, when: "pulse" | "module") => Promise<{
329
- frids: string[];
330
- groups?: string[][];
331
- why?: string;
332
- } | undefined>;
333
- /** When the chooser is asked: after each code pulse, once the module is regenerated, or never. Default: `conformance-tests:`. */
334
- conformanceChoosing?: (plainFile: string) => "pulse" | "module" | undefined;
335
341
  /**
336
342
  * Under `release-after-pulse: true`, what releases a pulse that landed, as its last stage - before the pulse ends, so
337
343
  * the module's next pulse starts on what is released. Its last reply goes in the pulse's report. Not given: nothing is.
@@ -344,9 +350,22 @@ export declare function createWorkLoop(options: {
344
350
  }) => Promise<string>;
345
351
  /** Plan, and keep the job each codebase would start (`firsts`) - but start none. */
346
352
  dryRun?: boolean;
353
+ /**
354
+ * Told each time the loop looks at a codebase whose spec parses: its chain, the spec as the .plain files have it now,
355
+ * and what its code is built from - what the test advisor watches (advisor-runs.ts). Nothing it does is the loop's
356
+ * concern: one that throws is ignored.
357
+ */
358
+ onSpec?: (seen: SeenSpec) => void;
347
359
  }): WorkLoop;
348
360
  /** Why nothing runs in a codebase whose code - and conformance tests, where they are wanted - is built from its spec as it is. */
349
361
  export declare const UP_TO_DATE = "built from the ***plain as it is";
362
+ /** Functional specs as a person names them: `module:func-id`, each. */
363
+ export declare function namesOf(module: string, funcIds: string[]): string;
364
+ /**
365
+ * The func-ids a person names in a module, each as `module:func-id` - a bare func-id, or another module's, is refused:
366
+ * a func-id is unique in its module only.
367
+ */
368
+ export declare function funcIdsNamed(module: string, names: string[]): string[];
350
369
  /** The codebases the loop looks after, as the person reads them: what runs, what is planned after it, and why nothing does. */
351
370
  /**
352
371
  * The coding agent's task for a dry run's first job, built as the render builds it - and all it did rolled back. Not for