@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,3 +1,4 @@
1
+ import { scenarioLabel } from "./test-kinds.js";
1
2
  /**
2
3
  * What a job's agents are told to do, by its cases - see docs/regenerating.md. A job's functional specs each fall in
3
4
  * one case - fresh, added, changed (patched), regenerated again, taken out - or, with none touched, the job is
@@ -5,12 +6,18 @@
5
6
  * functional specs of that case; their reviewers, what to hold each to. A job holding several cases gets a section
6
7
  * for each.
7
8
  */
8
- /** Functional specs, each by its number and - each module numbering its own from 1 - the module it belongs to. */
9
- const named = (specs) => specs.map((spec) => `fs ${spec.frid}${spec.module !== undefined ? ` of module \`${spec.module}\`` : ""} (${spec.text})`).join("; ");
10
- /** What the coding agent does, case by case. */
11
- export function codeCasesBlock(cases) {
9
+ /** Functional specs, each by its func-id - with the module it belongs to, a func-id being unique in its module only - and its first line. */
10
+ const named = (specs) => specs.map((spec) => `${spec.module !== undefined ? `\`${spec.module}:${spec.funcId}\`` : `\`${spec.funcId}\``} (${spec.text})`).join("; ");
11
+ /**
12
+ * What the coding agent does, case by case. `fromBase`: the codebase started from a base folder - nothing is built from
13
+ * the specification, but the folder is not empty, and a project in it is built on, never set up anew.
14
+ */
15
+ export function codeCasesBlock(cases, fromBase = false) {
12
16
  const lines = [];
13
- if (cases.fresh) {
17
+ if (cases.fresh && fromBase) {
18
+ lines.push("- Nothing is built from the specification yet, and the codebase holds what the base folder had. Where that is a", " project - a layout, a dependency manifest, unit tests - build on it: add to them, never set them up anew. Where it", " is files alone, set the project up around them - its layout, its dependency manifest, and its unit tests where", " the unit testing script runs them. Then build what follows, with its unit tests.");
19
+ }
20
+ else if (cases.fresh) {
14
21
  lines.push("- Nothing is built yet: set the project up first - its layout, its dependency manifest, and its unit tests", " where the unit testing script runs them - then build what follows, with its unit tests.");
15
22
  }
16
23
  if (cases.add.length > 0)
@@ -53,31 +60,80 @@ export function codeReviewCasesBlock(cases) {
53
60
  ...(cases.delete.length > 0 ? [`- Taken out: ${named(cases.delete)} - its code and the tests only it had removed, and nothing else.`] : []),
54
61
  ];
55
62
  }
56
- /** What the conformance agent does, case by case. */
57
- export function conformanceCasesBlock(cases) {
63
+ /** Scenarios, each by the label its tests carry. */
64
+ const labelled = (spec, scenarios, module) => scenarios.map((scenario) => `"${scenarioLabel(scenario.kind, scenario.id, spec.module ?? module, spec.funcId)}"`).join("; ");
65
+ /** Why a functional spec's code moved under its unchanged scenarios, in a few words. */
66
+ const CODE_CHANGED = {
67
+ text: "its text, or a file it links, changed",
68
+ again: "it was regenerated again",
69
+ concept: "a concept it uses changed",
70
+ };
71
+ /**
72
+ * What the conformance agent does with each functional spec, scenario by scenario - each scenario named by the label its
73
+ * tests carry - and, where the code moved under unchanged scenarios, what to do with their tests.
74
+ */
75
+ function scenarioCasesLines(specs, module) {
76
+ const lines = ["What this job does to the tests, scenario by scenario - each named by the label its tests carry:"];
77
+ for (const spec of specs) {
78
+ lines.push(`- \`${spec.module ?? module}:${spec.funcId}\` (${spec.text}):`);
79
+ if (spec.added.length > 0)
80
+ lines.push(` - added - write a test for each: ${labelled(spec, spec.added, module)}.`);
81
+ if (spec.changed.length > 0)
82
+ lines.push(` - changed - rewrite its tests to the scenario as it reads now: ${labelled(spec, spec.changed, module)}.`);
83
+ if (spec.removed.length > 0)
84
+ lines.push(` - removed - delete every test labelled with it, and its label, and nothing else: ${labelled(spec, spec.removed, module)}.`);
85
+ if (spec.unchanged.length > 0) {
86
+ lines.push(spec.codeChanged !== undefined
87
+ ? ` - unchanged, but its code changed - ${CODE_CHANGED[spec.codeChanged]} - run their tests on the new code: ${labelled(spec, spec.unchanged, module)}.`
88
+ : ` - unchanged - leave their tests exactly as they are: ${labelled(spec, spec.unchanged, module)}.`);
89
+ }
90
+ }
91
+ if (specs.some((spec) => spec.codeChanged !== undefined)) {
92
+ lines.push("- Where the code changed under unchanged scenarios: their tests were written against the code it replaced. Run them", " against the new code. Those that pass are tests of the new code too - conformance tests are black box - and are", " not rewritten. Fix what fails against its scenario, read with the functional spec's text: what a failing test checks", " that the scenario says stays checked, the test changed only as the new code needs it reached; what it checks that", " the scenario and the specification leave open - a path, a field, a message, a format the old code chose - is the", " new code's choice now: rewrite that check to what the new code does, or take it out. No test may hold the new code", " to what only the old code did.");
93
+ }
94
+ lines.push("- The tests of every other functional spec stay as they are.");
95
+ return lines;
96
+ }
97
+ /** What the conformance agent does, case by case - scenario by scenario, when the job has them. */
98
+ export function conformanceCasesBlock(cases, module = "") {
58
99
  const lines = [];
59
100
  if (cases.fresh) {
60
101
  lines.push("- There are no conformance tests yet: set the tests folder up first - the tests' own build and layout, so the", " conformance testing script, which brings the tests together with the code, runs them - then write what follows.");
61
102
  }
103
+ if (cases.scenarios !== undefined && cases.scenarios.length > 0)
104
+ return [...lines, ...scenarioCasesLines(cases.scenarios, module)];
62
105
  if (cases.add.length > 0)
63
- lines.push(`- New - write its conformance tests: ${named(cases.add)}.`);
106
+ lines.push(`- New - write a test for each of its scenarios: ${named(cases.add)}.`);
64
107
  if (cases.patch.length > 0) {
65
- lines.push(`- Changed - its text, its acceptance tests, or a concept it uses: ${named(cases.patch)}.`, " Its conformance tests exist: rewrite them to what it says now. The tests of everything else stay as they are.");
108
+ lines.push(`- Changed - its text, its scenarios, or a concept it uses: ${named(cases.patch)}.`, " Its conformance tests exist: bring them in line with its scenarios as they are now - a test for each, a", " scenario taken out losing its tests. The tests of everything else stay as they are.");
66
109
  }
67
110
  if (cases.again.length > 0) {
68
- lines.push(`- Regenerated again - its code was built anew, and its conformance tests were written against the code it replaced: ${named(cases.again)}.`, " Its text did not change, but the code did: write its tests again for the new code - that they still pass is", " no sign they fit it. What they check that the specification says stays checked, as the new code is reached.", " What they check that the specification leaves open - a path, a field, a message, a format the old code chose", " and the specification never names - is the new code's choice now: rewrite those checks to what the new code", " does, or take them out. No test may hold the new code to what only the old code did. A run that leaves every", " test file labelled with it as it was is sent back. The tests of everything else stay as they are.");
111
+ lines.push(`- Regenerated again - its code was built anew, and its conformance tests were written against the code it replaced: ${named(cases.again)}.`, " Its text did not change, but the code did. Run its tests against the new code. Those that pass are tests of the", " new code too - conformance tests are black box - and are not rewritten. Fix what fails against the", " specification: what a failing test checks that the specification says stays checked, the test changed only as", " the new code needs it reached; what it checks that the specification leaves open - a path, a field, a message,", " a format the old code chose and the specification never names - is the new code's choice now: rewrite that", " check to what the new code does, or take it out. No test may hold the new code to what only the old code did.", " The tests of everything else stay as they are.");
69
112
  }
70
113
  if (cases.delete.length > 0)
71
- lines.push(`- Taken out - remove its conformance tests: ${named(cases.delete)}.`);
114
+ lines.push(`- Taken out, or its scenarios all taken out - remove its conformance tests: ${named(cases.delete)}.`);
72
115
  if (cases.mechanical) {
73
116
  lines.push("- No functional spec is touched: the change is to the test reqs, or to definitions or implementation reqs alone.", " Adapt the existing tests to it. Write no new tests. A test may change only mechanically - how it is built,", " started or reaches the software - never what it asserts. Every test must still pass.");
74
117
  }
75
118
  return lines;
76
119
  }
77
- /** What the conformance test reviewer holds the tests to, when the job is mechanical: in place of its checks. */
78
- export function conformanceReviewCasesBlock(cases) {
79
- if (!cases.mechanical)
80
- return [];
120
+ /** What the conformance test reviewer holds the tests to: scenario by scenario - or, when the job is mechanical, in place of its checks. */
121
+ export function conformanceReviewCasesBlock(cases, module = "") {
122
+ if (!cases.mechanical) {
123
+ if (cases.scenarios === undefined || cases.scenarios.length === 0)
124
+ return [];
125
+ const all = (pick) => cases.scenarios.flatMap((spec) => (pick(spec).length > 0 ? [labelled(spec, pick(spec), module)] : [])).join("; ");
126
+ const kept = all((spec) => (spec.codeChanged === undefined ? spec.unchanged : []));
127
+ const rerun = all((spec) => (spec.codeChanged !== undefined ? spec.unchanged : []));
128
+ const removed = all((spec) => spec.removed);
129
+ return [
130
+ "What this job does to the tests, scenario by scenario - hold it to that:",
131
+ ...(kept !== "" ? [`- Unchanged - their tests come out exactly as they were, not rewritten or reworded: ${kept}.`] : []),
132
+ ...(rerun !== "" ? [`- Unchanged, the code changed under them - a test changed only where it failed on the new code, and only to what its scenario says: ${rerun}.`] : []),
133
+ ...(removed !== "" ? [`- Removed - no test labelled with them is left: ${removed}.`] : []),
134
+ "",
135
+ ];
136
+ }
81
137
  return [
82
138
  "This work touches no functional spec - only the test reqs, or definitions or implementation reqs alone. Review",
83
139
  "only that the tests follow them, and that no test was removed or weakened: an assertion loosened or taken out, a",
@@ -1,16 +1,21 @@
1
1
  import type { DiffWhere } from "./diff-view.ts";
2
- import { type SpecChange } from "./spec-steps.ts";
2
+ import { type SpecChange, type TestPlainPath } from "./spec-steps.ts";
3
3
  /**
4
4
  * `changes` as a diff of `file` - the module's .plain file, which the diff names - section by section: a removed
5
5
  * item's lines taken out, an added one's put in, a changed one's both. A change to what a linked file holds
6
6
  * follows, as the step's own diff has it.
7
7
  */
8
- export declare function changeDiff(changes: SpecChange[], file: string): string;
8
+ export declare function changeDiff(changes: SpecChange[], file: string, testPlain?: TestPlainView): string;
9
+ /** For a conformance job: the module, and where its functional specs' test plain files are, as the panels name them. */
10
+ export interface TestPlainView {
11
+ module: string;
12
+ pathOf: TestPlainPath;
13
+ }
9
14
  /**
10
15
  * `changeDiff`, each section's items in the order they are written - by file, then by line - rather than in the
11
16
  * order the spec is read in, which puts a concept after those it uses: placed by `locate`, as the panels place it.
12
17
  */
13
- export declare function changeDiffInFiles(changes: SpecChange[], file: string, locate: (diff: string) => DiffWhere): {
18
+ export declare function changeDiffInFiles(changes: SpecChange[], file: string, locate: (diff: string) => DiffWhere, testPlain?: TestPlainView): {
14
19
  diff: string;
15
20
  where: DiffWhere;
16
21
  };
@@ -1,11 +1,11 @@
1
- import { ACCEPTANCE_TESTS, DEFINITIONS, FUNCTIONAL_REQUIREMENTS, NON_FUNCTIONAL_REQUIREMENTS, REQUIRED_FUNCTIONAL_SPECS, TEST_REQUIREMENTS } from "../plain/spec.js";
2
- import { resourceDiff } from "./spec-steps.js";
1
+ import { DEFINITIONS, FUNCTIONAL_REQUIREMENTS, NON_FUNCTIONAL_REQUIREMENTS, REQUIRED_FUNCTIONAL_SPECS, TEST_REQUIREMENTS } from "../plain/spec.js";
2
+ import { diffFileTexts, resourceDiff, testPlainFileText } from "./spec-steps.js";
3
3
  /**
4
4
  * A job's change as its agent is given it, for the panels: the same items `describeSpecChange` writes into
5
5
  * `<spec change>` - what the job's functional specs use, from the module's own file and from the files it
6
6
  * imports and requires alike - laid out as a diff the panels already show (`diff-view.ts`). Each section is
7
- * a block under its header, each item as its file writes it - its markdown, its acceptance tests indented
8
- * under it - so placed in the .plain files (`locateDiffLines`) every line is shown in the file it is written
7
+ * a block under its header, each item as its file writes it - a functional spec under its `# <func-id>` heading -
8
+ * so placed in the .plain files (`locateDiffLines`) every line is shown in the file it is written
9
9
  * in, with its line there: split by file and by block. A text diff of the module's file would show other
10
10
  * things - the lines around a change, the items no job takes yet - and leave out what an import adds.
11
11
  */
@@ -13,7 +13,7 @@ import { resourceDiff } from "./spec-steps.js";
13
13
  const ORDER = [DEFINITIONS, NON_FUNCTIONAL_REQUIREMENTS, TEST_REQUIREMENTS, REQUIRED_FUNCTIONAL_SPECS, FUNCTIONAL_REQUIREMENTS];
14
14
  /** A section's header as a .plain file writes it: a required module's functional specs are under its own `***functional specs***`. */
15
15
  const heading = (section) => `***${section === REQUIRED_FUNCTIONAL_SPECS ? FUNCTIONAL_REQUIREMENTS : section}***`;
16
- /** An item's lines as a .plain file writes them: its markdown, then its acceptance tests indented under it. */
16
+ /** An item's lines as a .plain file writes them: a functional spec's `# <func-id>` heading over its text, anything else its text. */
17
17
  function itemLines(serialized) {
18
18
  if (serialized === undefined)
19
19
  return [];
@@ -26,31 +26,24 @@ function itemLines(serialized) {
26
26
  }
27
27
  if (typeof item === "string")
28
28
  return item.split("\n");
29
- const { markdown, [ACCEPTANCE_TESTS]: tests } = (item ?? {});
29
+ const { id, markdown } = (item ?? {});
30
30
  const lines = (markdown ?? JSON.stringify(item)).split("\n");
31
- if (Array.isArray(tests) && tests.length > 0) {
32
- lines.push("", " ***acceptance tests***", "");
33
- for (const test of tests) {
34
- const text = typeof test === "string" ? test : (test.markdown ?? JSON.stringify(test));
35
- lines.push(...text.split("\n").map((line) => ` ${line}`));
36
- }
37
- }
38
- return lines;
31
+ return id !== undefined ? [`# ${id}`, ...lines] : lines;
39
32
  }
40
33
  /**
41
34
  * `changes` as a diff of `file` - the module's .plain file, which the diff names - section by section: a removed
42
35
  * item's lines taken out, an added one's put in, a changed one's both. A change to what a linked file holds
43
36
  * follows, as the step's own diff has it.
44
37
  */
45
- export function changeDiff(changes, file) {
46
- return laidOut(changes, file).diff;
38
+ export function changeDiff(changes, file, testPlain) {
39
+ return laidOut(changes, file, false, testPlain).diff;
47
40
  }
48
41
  /**
49
42
  * `changeDiff`, each section's items in the order they are written - by file, then by line - rather than in the
50
43
  * order the spec is read in, which puts a concept after those it uses: placed by `locate`, as the panels place it.
51
44
  */
52
- export function changeDiffInFiles(changes, file, locate) {
53
- const first = laidOut(changes, file);
45
+ export function changeDiffInFiles(changes, file, locate, testPlain) {
46
+ const first = laidOut(changes, file, false, testPlain);
54
47
  const where = locate(first.diff);
55
48
  const placeOf = (change) => {
56
49
  const at = where[first.starts.get(change) ?? -1];
@@ -65,11 +58,14 @@ export function changeDiffInFiles(changes, file, locate) {
65
58
  return a.position - b.position;
66
59
  return x.file === y.file ? x.line - y.line : (x.file === file ? -1 : y.file === file ? 1 : x.file.localeCompare(y.file));
67
60
  });
68
- const diff = laidOut(ordered, file, true).diff;
61
+ const diff = laidOut(ordered, file, true, testPlain).diff;
69
62
  return { diff, where: locate(diff) };
70
63
  }
71
- /** The diff, and the line each change starts on in it. `asGiven`: each section's items in the order given. */
72
- function laidOut(changes, file, asGiven = false) {
64
+ /**
65
+ * The diff, and the line each change starts on in it. `asGiven`: each section's items in the order given. `testPlain`: a
66
+ * conformance job's - a functional spec whose scenarios changed has its test plain file's diff after the module's.
67
+ */
68
+ function laidOut(changes, file, asGiven = false, testPlain) {
73
69
  const starts = new Map();
74
70
  const sections = [...new Set(changes.map((change) => change.section))].sort((a, b) => (ORDER.indexOf(a) === -1 ? ORDER.length : ORDER.indexOf(a)) - (ORDER.indexOf(b) === -1 ? ORDER.length : ORDER.indexOf(b)));
75
71
  const lines = [`diff --git a/${file} b/${file}`, `--- a/${file}`, `+++ b/${file}`];
@@ -81,6 +77,9 @@ function laidOut(changes, file, asGiven = false) {
81
77
  // Where this section's lines start in the diff: after the lines so far, its hunk header and its section header.
82
78
  const offset = lines.length + 2;
83
79
  for (const change of own) {
80
+ // Only its scenarios changed: they are its test plain file's, whose diff follows - the module's file shows nothing of it.
81
+ if (testPlain !== undefined && scenariosOnly(change))
82
+ continue;
84
83
  const before = change.kind === "add" ? [] : itemLines(change.before);
85
84
  const after = change.kind === "remove" ? [] : itemLines(change.after);
86
85
  // Where it is found: its first line now, or - removed - its first line as it was.
@@ -96,7 +95,57 @@ function laidOut(changes, file, asGiven = false) {
96
95
  continue;
97
96
  lines.push("@@ @@", ` ${heading(section)}`, ...body);
98
97
  }
99
- return { diff: `${lines.join("\n")}\n${resourceDiff(changes, file)}`, starts };
98
+ // Nothing of the module's file changed - a conformance job's scenarios alone: no block for it at all.
99
+ const own = lines.length > 3 ? `${lines.join("\n")}\n` : "";
100
+ return { diff: `${own}${testPlain !== undefined ? testPlainDiffs(changes, testPlain) : ""}${resourceDiff(changes, file)}`, starts };
101
+ }
102
+ /** Whether a change to a functional spec is to its scenarios alone: its text, and all else of it, as it was. */
103
+ function scenariosOnly(change) {
104
+ if (change.kind !== "change" || change.before === undefined || change.after === undefined)
105
+ return false;
106
+ const rest = (serialized) => {
107
+ try {
108
+ const { conformance_tests: _conformance, acceptance_tests: _acceptance, ...others } = JSON.parse(serialized);
109
+ return JSON.stringify(others);
110
+ }
111
+ catch {
112
+ return undefined;
113
+ }
114
+ };
115
+ const [was, now] = [rest(change.before), rest(change.after)];
116
+ return was !== undefined && was === now && change.before !== change.after;
117
+ }
118
+ /**
119
+ * Each test plain file a conformance job's changes touch, as git diffs it: the lines that changed, among the lines around
120
+ * them - a functional spec with no scenarios has no file, so a new one is added whole and one whose scenarios all went is
121
+ * taken out whole.
122
+ */
123
+ function testPlainDiffs(changes, testPlain) {
124
+ const scenarioCarrier = (line) => {
125
+ if (line === undefined)
126
+ return undefined;
127
+ try {
128
+ const item = JSON.parse(line);
129
+ return item.id !== undefined && (item.conformance_tests !== undefined || item.acceptance_tests !== undefined) ? item : undefined;
130
+ }
131
+ catch {
132
+ return undefined;
133
+ }
134
+ };
135
+ return changes
136
+ // A module below's functional specs too: the tip's conformance suite holds the chain's.
137
+ .filter((change) => change.section === FUNCTIONAL_REQUIREMENTS || change.section === REQUIRED_FUNCTIONAL_SPECS)
138
+ .flatMap((change) => {
139
+ const [was, now] = [scenarioCarrier(change.kind === "add" ? undefined : change.before), scenarioCarrier(change.kind === "remove" ? undefined : change.after)];
140
+ const funcId = now?.id ?? was?.id;
141
+ if (funcId === undefined)
142
+ return [];
143
+ const module = now?.module ?? was?.module ?? testPlain.module;
144
+ const fileOf = (carrier) => carrier !== undefined && (carrier.conformance_tests ?? []).length + (carrier.acceptance_tests ?? []).length > 0 ? testPlainFileText(module, carrier) : "";
145
+ const diff = diffFileTexts(fileOf(was), fileOf(now), testPlain.pathOf(funcId, module));
146
+ return diff === "" ? [] : [diff];
147
+ })
148
+ .join("");
100
149
  }
101
150
  /** The changes a job's agent works from: a code job's leave out the test reqs, which are the conformance job's. */
102
151
  export function agentChanges(changes, track) {
@@ -1,6 +1,6 @@
1
1
  import { type MarshalledPlainSource } from "../plain/spec.ts";
2
2
  import { type Steering } from "./steer.ts";
3
- import { type SpecChange, type SpecStep } from "./spec-steps.ts";
3
+ import { type SpecChange, type SpecStep, type TestPlainPath } from "./spec-steps.ts";
4
4
  /**
5
5
  * The queue's work, cut into jobs of a size worth rendering.
6
6
  *
@@ -18,8 +18,9 @@ import { type SpecChange, type SpecStep } from "./spec-steps.ts";
18
18
  * 3. Where each new implementation req goes - the group it is about, or the first, when it is about
19
19
  * everything - and each new test req: the group whose conformance tests need it first. An agent judges it.
20
20
  *
21
- * Changes to what is built already - an item edited or taken out, a functional spec built changed - go first, in a
22
- * job of their own. Every answer is checked; one that does not hold goes back to its agent, twice at most, and then
21
+ * Changes to what is built already - an item edited or taken out, a functional spec built changed - go where the
22
+ * grouping agent places each: with the new work it is about, by default, or first, in a job of their own, when it
23
+ * reshapes what the new work is built on or has nothing to do with it. Every answer is checked; one that does not hold goes back to its agent, twice at most, and then
23
24
  * the plan falls back to a functional spec a job. A job past `max-lines-per-pulse` (MAX_LINES_PER_JOB unset) is split. The plan is the queue
24
25
  * master's proposal: it reviews it, and may change it, held to the same checks.
25
26
  */
@@ -47,11 +48,8 @@ export interface PlanInput {
47
48
  * in one job, as far as the guardrail (`max-lines-per-pulse`) lets.
48
49
  */
49
50
  separately?: boolean;
50
- /**
51
- * For the conformance tests: the functional specs asked for together, a group a request - `conform` called for 1-3, then
52
- * for 4-6. Never two groups in one job; the functional specs in none are tested together, as before.
53
- */
54
- groups?: string[][];
51
+ /** For the conformance tests: where each functional spec's test plain file is, as a step's ***plain diff names it. */
52
+ testPlainPath?: TestPlainPath;
55
53
  /** How big a pulse may be - the config's `max-specs-per-pulse` and `max-lines-per-pulse`; unset, MAX_SPECS_PER_JOB and MAX_LINES_PER_JOB. */
56
54
  limits?: PulseLimits;
57
55
  }
@@ -90,9 +88,9 @@ export declare function shortTitle(text: string, chars?: number, words?: number)
90
88
  export declare const MAX_LINES_PER_JOB = 120;
91
89
  /**
92
90
  * A functional spec to build, for the grouping: the number the agent answers with, its text, and whether it is built
93
- * already and regenerated again. `id` and `module` say which it is: each module numbers its own from 1, and a codebase
94
- * regenerates again the functional specs of the modules it is built on too. No module: the codebase's own. No id: the
95
- * number.
91
+ * already and regenerated again. `id` and `module` say which it is: its func-id, unique in its module, and a codebase
92
+ * regenerates again the functional specs of the modules it is built on too. No module: the codebase's own. `place` is
93
+ * where it is among its module's functional specs - the ones next to it there are next to it. No place: its number.
96
94
  */
97
95
  export interface NewSpec {
98
96
  number: number;
@@ -100,12 +98,31 @@ export interface NewSpec {
100
98
  again?: boolean;
101
99
  id?: string;
102
100
  module?: string;
101
+ place?: number;
102
+ }
103
+ /**
104
+ * A change to what is built already, for the grouping: the number the agent answers with, whether it is changed, taken
105
+ * out or put in among what is built, what it is - a functional spec, a definition, a req - and its text. `id` and
106
+ * `module` say which functional spec it is.
107
+ */
108
+ export interface BuiltChange {
109
+ number: number;
110
+ kind: "changed" | "taken out" | "added";
111
+ what: string;
112
+ text: string;
113
+ id?: string;
114
+ module?: string;
103
115
  }
104
116
  export interface GroupAnswer {
105
117
  groups: Array<{
106
118
  specs: number[];
107
119
  title: string;
108
120
  }>;
121
+ /** Where each change to what is built goes: with a group, by its number, or "first" - a pulse of its own, before the new work. */
122
+ built?: Array<{
123
+ change: number;
124
+ to: number | "first";
125
+ }>;
109
126
  /** What of the person's wishes it did not follow, and why. */
110
127
  notes?: string;
111
128
  }
@@ -161,18 +178,22 @@ export interface TitleAnswer {
161
178
  * first functional spec's first line; without it, they keep that.
162
179
  */
163
180
  export interface ChunkAgents {
164
- group(specs: NewSpec[], check: (answer: GroupAnswer) => string[], steering?: Steering[], maxSpecs?: number): Promise<GroupAnswer | undefined>;
181
+ group(specs: NewSpec[], check: (answer: GroupAnswer) => string[], steering?: Steering[], maxSpecs?: number, built?: BuiltChange[]): Promise<GroupAnswer | undefined>;
165
182
  place(input: PlaceInput, check: (answer: PlaceAnswer) => string[]): Promise<PlaceAnswer | undefined>;
166
183
  title?(groups: TitleGroup[], check: (answer: TitleAnswer) => string[], track: "code" | "conformance"): Promise<TitleAnswer | undefined>;
167
184
  }
168
- /** What is wrong with a grouping: every new functional spec once, in order, next to each other, none too big. */
169
- export declare function groupProblems(specs: NewSpec[], answer: GroupAnswer, maxSpecs?: number): string[];
185
+ /**
186
+ * What is wrong with a grouping: every new functional spec once, in order, next to each other, none too big - and
187
+ * every change to what is built placed once, with a group or first. A functional spec changed counts in its group: it
188
+ * is built as the new ones are. One taken out builds nothing, and does not.
189
+ */
190
+ export declare function groupProblems(specs: NewSpec[], answer: GroupAnswer, maxSpecs?: number, built?: BuiltChange[]): string[];
170
191
  /** What is wrong with a titling: every group titled once, by its number, none empty, none too long. */
171
192
  export declare function titleProblems(groups: TitleGroup[], answer: TitleAnswer): string[];
172
193
  /** What is wrong with a placement: every new req once, each where there is such a job. */
173
194
  export declare function placeProblems(input: PlaceInput, answer: PlaceAnswer): string[];
174
195
  export declare function planChunks(input: PlanInput, agents?: ChunkAgents, notes?: string[]): Promise<DraftJob[]>;
175
- export declare function groupPrompt(specs: NewSpec[], steering?: Steering[], maxSpecs?: number): string;
196
+ export declare function groupPrompt(specs: NewSpec[], steering?: Steering[], maxSpecs?: number, built?: BuiltChange[]): string;
176
197
  export declare function placePrompt(input: PlaceInput): string;
177
198
  export declare function titlePrompt(groups: TitleGroup[], track: "code" | "conformance"): string;
178
199
  /** The chunking's agents, on a model - fresh for every plan. */