@turri-dev/cli 0.1.27-next.763d091 → 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 +71 -15
  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 -20
  95. package/dist/regenerate/conformance.js +118 -121
  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 +61 -49
  104. package/dist/regenerate/loop.js +473 -340
  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 +69 -31
  135. package/dist/regenerate/test-kinds.js +109 -80
  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 +89 -54
  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 +24 -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,5 +1,4 @@
1
1
  import { type MarshalledPlainSource } from "../plain/spec.ts";
2
- import { type Chosen } from "./records.ts";
3
2
  import { type SpecChange } from "./spec-steps.ts";
4
3
  /**
5
4
  * The work, derived from the records - see docs/regenerating.md. Nothing here is stored: what is left
@@ -8,7 +7,7 @@ import { type SpecChange } from "./spec-steps.ts";
8
7
  *
9
8
  * - Code work: from the code record to the spec as the code sees it.
10
9
  * - Conformance work: from the tests record to what the conformance tests are to be written for now -
11
- * the chosen functional specs the code is built from.
10
+ * the functional specs the code is built from that have scenarios.
12
11
  *
13
12
  * A job is a set of those changes. What it tells its agents follows from the functional specs it
14
13
  * touches - its cases - and a running job is stopped once the spec no longer wants what it makes.
@@ -16,13 +15,13 @@ import { type SpecChange } from "./spec-steps.ts";
16
15
  /** Everything left to build in the code: from what the code record says it is built from, to the spec now. */
17
16
  export declare function codeWork(record: MarshalledPlainSource | null, spec: MarshalledPlainSource): SpecChange[];
18
17
  /** 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. */
19
- export declare function conformanceWork(tests: MarshalledPlainSource | null, code: MarshalledPlainSource | null, spec: MarshalledPlainSource, chosen?: Chosen): SpecChange[];
18
+ export declare function conformanceWork(tests: MarshalledPlainSource | null, code: MarshalledPlainSource | null, spec: MarshalledPlainSource): SpecChange[];
20
19
  /**
21
- * A functional spec a job touches: its number - in the spec after the job, or, taken out, before it - its first line,
22
- * and the module it belongs to: each module numbers its own from 1, so a number alone can mean two.
20
+ * A functional spec a job touches: its func-id, its first line, and the module it belongs to - a func-id is unique in its
21
+ * module, so a func-id alone can mean two.
23
22
  */
24
23
  export interface TouchedSpec {
25
- frid: string;
24
+ funcId: string;
26
25
  text: string;
27
26
  module?: string;
28
27
  }
@@ -30,17 +29,19 @@ export interface TouchedSpec {
30
29
  * What a job does to the functional specs, and so what its agents are told:
31
30
  *
32
31
  * - fresh: the module requires none and nothing is built - the project is set up first.
33
- * - add: functional specs that are new.
34
- * - patch: functional specs changed - their text, their acceptance tests, or a concept they use: a
35
- * change to what is built, never built again from scratch.
32
+ * - add: functional specs that are new - a func-id the spec did not have.
33
+ * - patch: functional specs changed - their text, their scenarios, or a concept they use: a
34
+ * change to what is built, never built again from scratch. A functional spec moved, and nothing else, is none.
36
35
  * - again: functional specs marked to regenerate again (`regenerate` with functionalities named): their code is built, and is regenerated again - rewritten
37
- * or started over, doing what the spec says now - with what it replaces taken out. A functional spec of a module
38
- * this one requires, regenerated again here, is one too.
39
- * - delete: functional specs taken out.
40
- * - mechanical: none of those - only reqs, or definitions no functional spec uses. Existing tests may
41
- * change only mechanically, and no new ones are written.
36
+ * or started over, doing what the spec says now - with what it replaces taken out.
37
+ * - delete: functional specs taken out - a func-id the spec no longer has. One renamed with any other change is a delete
38
+ * and an add; renamed and nothing else, turri renames it itself, and it is no job's (renames.ts).
39
+ * - mechanical: none of those - no functional spec of any module of the chain touched: only reqs, or definitions no
40
+ * functional spec uses. Existing tests may change only mechanically, and no new ones are written.
42
41
  *
43
- * A job can hold several of add, patch and delete at once: its prompt has a section for each.
42
+ * The functional specs of the modules below are cased as the codebase's own are, each with its module: the codebase at
43
+ * the tip of a chain holds their code. A job can hold several of add, patch and delete at once: its prompt has a section
44
+ * for each.
44
45
  */
45
46
  export interface JobCases {
46
47
  fresh: boolean;
@@ -49,6 +50,37 @@ export interface JobCases {
49
50
  again: TouchedSpec[];
50
51
  delete: TouchedSpec[];
51
52
  mechanical: boolean;
53
+ /**
54
+ * A conformance job's: each functional spec it touches, its scenarios by case - in the order of add, patch, again and
55
+ * delete. None for a code job: the code record holds no scenario.
56
+ */
57
+ scenarios?: ScenarioCases[];
58
+ }
59
+ /** A scenario as a conformance job names it: its kind, its scenario-id, its text. */
60
+ export interface ScenarioRef {
61
+ kind: "conformance" | "acceptance";
62
+ id: string;
63
+ text: string;
64
+ }
65
+ /**
66
+ * What a conformance job does to the tests of one functional spec, scenario by scenario - compared with the tests record
67
+ * by kind and scenario-id, as functional specs are by func-id:
68
+ *
69
+ * - added: write its tests; changed: rewrite them to the scenario as it reads now; removed: delete the tests labelled
70
+ * with it; unchanged: its tests stay as they are.
71
+ * - A scenario-id renamed with any other change, or a scenario moved between conformance and acceptance - its label
72
+ * changes - is one removed and one added; one renamed and nothing else turri relabels itself (renames.ts). One moved
73
+ * between test plain files of the same functional spec is nothing.
74
+ * - `codeChanged`: the functional spec's code moved under its unchanged scenarios - its text or a file it links changed
75
+ * (`text`), it was regenerated again (`again`), or a concept it uses changed (`concept`): their tests are run on the
76
+ * new code, and those that fail are fixed against the scenario.
77
+ */
78
+ export interface ScenarioCases extends TouchedSpec {
79
+ added: ScenarioRef[];
80
+ changed: ScenarioRef[];
81
+ removed: ScenarioRef[];
82
+ unchanged: ScenarioRef[];
83
+ codeChanged?: "text" | "again" | "concept";
52
84
  }
53
85
  /**
54
86
  * Every concept a functional spec uses: those its text names, and those their definitions name in
@@ -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. */
@@ -7,7 +7,7 @@ import { type ChainEntry } from "./requires.ts";
7
7
  import { type SpecSnapshot } from "./regenerate.ts";
8
8
  import { type JobSteering } from "./steering.ts";
9
9
  import { type SpecStep } from "./spec-steps.ts";
10
- import { type JobCases, type TouchedSpec } from "./drift.ts";
10
+ import { type JobCases } from "./drift.ts";
11
11
  /**
12
12
  * The work loop - see docs/regenerating.md.
13
13
  *
@@ -68,18 +68,18 @@ export interface ModuleStatus {
68
68
  steered?: string[];
69
69
  };
70
70
  /**
71
- * The jobs planned after it, in order: the module's code jobs, then its conformance jobs. A conformance job scheduled
72
- * for functional specs not all built yet - `waitsFor`, those that are not - comes after the module's last code job.
73
- * The module's conformance tests, wanted for all of it while its code is still to build - `afterCode` - come last:
74
- * 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.
75
75
  */
76
76
  planned: Array<{
77
77
  title: string;
78
78
  cases: JobCases;
79
79
  track: Track;
80
- waitsFor?: string[];
81
80
  planning?: true;
82
81
  afterCode?: true;
82
+ specs?: string[];
83
83
  }>;
84
84
  /**
85
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
@@ -88,19 +88,33 @@ export interface ModuleStatus {
88
88
  planNotes?: string[];
89
89
  /** Why nothing runs there, when nothing does. */
90
90
  why?: string;
91
- /** 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. */
92
92
  conformanceNote?: string;
93
93
  /**
94
94
  * It now requires modules its code was not built on - `requires` - and is built only once its code is started
95
95
  * over on them: these codebases' code and tests taken out, kept in their history, and built again from the
96
- * 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.
97
99
  */
98
100
  startOver?: {
99
101
  requires: string[];
100
102
  codebases: string[];
103
+ old?: true;
101
104
  };
102
105
  /** The stack it is built with - its config.turri.<stack>.yaml over config.turri.yaml - when one is. */
103
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
+ };
104
118
  }
105
119
  /** What is left to regenerate in a codebase - see `WorkLoop.drift`. */
106
120
  export interface DriftReport {
@@ -110,11 +124,6 @@ export interface DriftReport {
110
124
  code: JobCases | null | undefined;
111
125
  /** The conformance tests' work: null, none; undefined, none are written here. */
112
126
  conformance: JobCases | null | undefined;
113
- /** Conformance tests scheduled ahead of their code, and which of their functional specs are not built yet. */
114
- scheduled: Array<{
115
- frids: string[];
116
- unbuilt: string[];
117
- }>;
118
127
  running?: {
119
128
  jobId: string;
120
129
  title: string;
@@ -122,9 +131,23 @@ export interface DriftReport {
122
131
  /** Why nothing is regenerated there: halted, or its spec does not parse. */
123
132
  why?: string;
124
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;
125
142
  }
126
143
  /** What is left to regenerate, as the conversation and the command line read it. */
127
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
+ }
128
151
  export interface WorkLoop {
129
152
  /**
130
153
  * Bring a spec's module up to the spec as the .plain files have it now - in the codebases where its
@@ -139,12 +162,12 @@ export interface WorkLoop {
139
162
  specChange?: boolean;
140
163
  }): Promise<ModuleStatus[]>;
141
164
  /**
142
- * 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
143
166
  * the record of every codebase it is built in - committed - and they are brought up to their spec, the marked ones
144
167
  * planned as any change is. Their code is there: the jobs that regenerate them are told to regenerate it again, not
145
168
  * to build it anew.
146
169
  */
147
- regenerateAgain(plainFile: string, frids: string | string[], options?: {
170
+ regenerateAgain(plainFile: string, funcIds: string | string[], options?: {
148
171
  model?: string;
149
172
  guidance?: string;
150
173
  }): Promise<ModuleStatus[]>;
@@ -159,10 +182,9 @@ export interface WorkLoop {
159
182
  * again, it clears a halt. Says what it planned.
160
183
  */
161
184
  conformance(plainFile: string, options?: {
162
- frids?: string[];
185
+ funcIds?: string[];
163
186
  model?: string;
164
187
  separately?: boolean;
165
- groups?: string[][];
166
188
  }): Promise<ModuleStatus[]>;
167
189
  /**
168
190
  * Start over the codebases of a spec's module that now require a module their code was not built on (see
@@ -230,8 +252,8 @@ export interface MapPulse {
230
252
  before: string;
231
253
  after: string;
232
254
  /**
233
- * A conformance pulse scheduled ahead of its code - not planned, so with no trees: the texts of the functional specs
234
- * 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`.
235
257
  */
236
258
  specs?: string[];
237
259
  }
@@ -246,11 +268,11 @@ export interface MapCodebase {
246
268
  planning?: Track;
247
269
  /** The module below it that it waits for. */
248
270
  waitsFor?: string;
249
- /** 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. */
250
272
  conformance: boolean;
251
273
  /** Why nothing runs there, when nothing does. */
252
274
  why?: string;
253
- /** 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. */
254
276
  conformanceNote?: string;
255
277
  /**
256
278
  * Halted, or the planner found no plan: it needs the person. `pulse`, the pulse that did not land; `cause`, what went
@@ -280,11 +302,13 @@ export interface ModuleCompletion {
280
302
  specified: number;
281
303
  /** Of those, the ones the delivered codebase's code record has as the spec has them now. */
282
304
  completed: number;
283
- /** 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. */
284
308
  conformanceTested: number;
285
- /** 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. */
286
310
  tested: string[];
287
- /** Its functional specs not completed, by number. */
311
+ /** Its functional specs not completed, by func-id. */
288
312
  incomplete: string[];
289
313
  }
290
314
  /**
@@ -314,25 +338,6 @@ export declare function createWorkLoop(options: {
314
338
  run?: JobRunner;
315
339
  /** Writes and runs a conformance job's tests. Default: a spec diff's conformance run, the whole suite with it. */
316
340
  runConformance?: JobRunner;
317
- /** Whether the config has a module's conformance tests on. Default: its `conformance-tests:`, off unless it turns them on. */
318
- conformanceOn?: (plainFile: string) => boolean;
319
- /**
320
- * Whether a module's conformance tests are for the functional specs chosen, not every one: those with acceptance
321
- * tests, those the conversation asked for, and those tested already. Default: `conformance-tests: chosen`.
322
- */
323
- conformanceChosen?: (plainFile: string) => boolean;
324
- /**
325
- * Under `conformance-tests: chosen`, asked what a code job that landed built or changed that no conformance test is
326
- * for - by whoever chooses without a conversation (`turri regenerate`) - before the job ends: the functional specs it
327
- * names are scheduled as conform schedules them. The conversation is told in the job's report instead.
328
- */
329
- onChoose?: (plainFile: string, specs: TouchedSpec[], budget: number | undefined, when: "pulse" | "module") => Promise<{
330
- frids: string[];
331
- groups?: string[][];
332
- why?: string;
333
- } | undefined>;
334
- /** When the chooser is asked: after each code pulse, once the module is regenerated, or never. Default: `conformance-tests:`. */
335
- conformanceChoosing?: (plainFile: string) => "pulse" | "module" | undefined;
336
341
  /**
337
342
  * Under `release-after-pulse: true`, what releases a pulse that landed, as its last stage - before the pulse ends, so
338
343
  * the module's next pulse starts on what is released. Its last reply goes in the pulse's report. Not given: nothing is.
@@ -345,9 +350,22 @@ export declare function createWorkLoop(options: {
345
350
  }) => Promise<string>;
346
351
  /** Plan, and keep the job each codebase would start (`firsts`) - but start none. */
347
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;
348
359
  }): WorkLoop;
349
360
  /** Why nothing runs in a codebase whose code - and conformance tests, where they are wanted - is built from its spec as it is. */
350
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[];
351
369
  /** The codebases the loop looks after, as the person reads them: what runs, what is planned after it, and why nothing does. */
352
370
  /**
353
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
@@ -388,12 +406,6 @@ export declare function describeModules(statuses: ModuleStatus[], options?: {
388
406
  }): string;
389
407
  /** The functional specs a pulse covers, by case, each with the module whose spec it is: a host's to light and mark. */
390
408
  export declare function pulseCases(cases: JobCases, moduleName: string): PulseCases;
391
- /**
392
- * What a conformance job records its tests are written for: its step's tree, but a functional spec of the module it
393
- * regenerates again keeps its mark unless the run wrote its tests - tests that passed as they were are still the old
394
- * code's, and still conformance work.
395
- */
396
- export declare function keepAgainMarks(tree: MarshalledPlainSource, cases: JobCases, moduleName: string, rewritten: string[]): MarshalledPlainSource;
397
409
  /**
398
410
  * A module's first pulse when it builds one functional spec - the project set up and the smallest thing that runs: it lands on its
399
411
  * unit tests alone, with no review. A reviewer, and an arbiter after it, on a scaffold is time spent before anything can