@turri-dev/cli 0.1.27 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +6 -6
- package/dist/advisor-chat.d.ts +63 -0
- package/dist/advisor-chat.js +290 -0
- package/dist/background-skills.d.ts +53 -0
- package/dist/background-skills.js +121 -0
- package/dist/bedrock-region.d.ts +66 -0
- package/dist/bedrock-region.js +144 -0
- package/dist/btw.d.ts +1 -1
- package/dist/btw.js +1 -1
- package/dist/change-plain-cli.js +20 -13
- package/dist/chat-idle.d.ts +7 -0
- package/dist/chat-idle.js +20 -0
- package/dist/chats.d.ts +10 -0
- package/dist/chats.js +23 -0
- package/dist/cli-options.d.ts +1 -2
- package/dist/cli-options.js +2 -7
- package/dist/cli.js +11 -3
- package/dist/config-command.d.ts +14 -0
- package/dist/config-command.js +35 -15
- package/dist/config-edit.d.ts +3 -3
- package/dist/config-edit.js +16 -12
- package/dist/config.d.ts +54 -34
- package/dist/config.js +40 -44
- package/dist/conform-cli.js +12 -10
- package/dist/conformance-switch.d.ts +34 -0
- package/dist/conformance-switch.js +108 -0
- package/dist/cultivate-cli.js +15 -5
- package/dist/drift-cli.js +2 -0
- package/dist/extension.d.ts +15 -0
- package/dist/extension.js +260 -83
- package/dist/host-session.d.ts +26 -0
- package/dist/host-session.js +30 -2
- package/dist/host.d.ts +5 -2
- package/dist/host.js +7 -1
- package/dist/init-config.d.ts +1 -1
- package/dist/init-config.js +20 -11
- package/dist/loop-cli.d.ts +3 -3
- package/dist/loop-cli.js +13 -16
- package/dist/loop-tools.js +18 -10
- package/dist/model-check.d.ts +8 -3
- package/dist/model-check.js +13 -8
- package/dist/new-project.js +12 -8
- package/dist/{validate-cli.js → parse-cli.js} +11 -5
- package/dist/parse-tool.d.ts +9 -0
- package/dist/parse-tool.js +33 -0
- package/dist/pi-launch.js +1 -1
- package/dist/plain/check.d.ts +53 -6
- package/dist/plain/check.js +217 -24
- package/dist/plain/concepts.d.ts +8 -0
- package/dist/plain/concepts.js +66 -23
- package/dist/plain/cycles.d.ts +21 -0
- package/dist/plain/cycles.js +190 -0
- package/dist/plain/errors.d.ts +4 -0
- package/dist/plain/errors.js +7 -0
- package/dist/plain/files.d.ts +18 -1
- package/dist/plain/files.js +39 -2
- package/dist/plain/graph.d.ts +33 -4
- package/dist/plain/graph.js +60 -12
- package/dist/plain/highlight.d.ts +3 -2
- package/dist/plain/highlight.js +4 -3
- package/dist/plain/index.d.ts +8 -5
- package/dist/plain/index.js +8 -5
- package/dist/plain/liquid.js +1 -6
- package/dist/plain/markdown.d.ts +32 -3
- package/dist/plain/markdown.js +146 -24
- package/dist/plain/module-concepts.js +2 -2
- package/dist/plain/parser.d.ts +83 -28
- package/dist/plain/parser.js +381 -260
- package/dist/plain/plain.tmLanguage.json +29 -3
- package/dist/plain/project.d.ts +48 -0
- package/dist/plain/project.js +155 -0
- package/dist/plain/spec.d.ts +90 -28
- package/dist/plain/spec.js +107 -133
- package/dist/plain/test-plain-file.d.ts +59 -0
- package/dist/plain/test-plain-file.js +232 -0
- package/dist/regenerate/activity.d.ts +20 -9
- package/dist/regenerate/activity.js +11 -4
- package/dist/regenerate/advisor-records.d.ts +93 -0
- package/dist/regenerate/advisor-records.js +110 -0
- package/dist/regenerate/advisor-runs.d.ts +87 -0
- package/dist/regenerate/advisor-runs.js +327 -0
- package/dist/regenerate/base-folder.d.ts +68 -0
- package/dist/regenerate/base-folder.js +168 -0
- package/dist/regenerate/cases.d.ts +9 -6
- package/dist/regenerate/cases.js +70 -14
- package/dist/regenerate/change-view.d.ts +8 -3
- package/dist/regenerate/change-view.js +71 -22
- package/dist/regenerate/chunk-plan.d.ts +36 -15
- package/dist/regenerate/chunk-plan.js +165 -59
- package/dist/regenerate/conform.d.ts +3 -0
- package/dist/regenerate/conform.js +6 -5
- package/dist/regenerate/conformance-agents.d.ts +18 -39
- package/dist/regenerate/conformance-agents.js +60 -88
- package/dist/regenerate/conformance.d.ts +23 -13
- package/dist/regenerate/conformance.js +119 -82
- package/dist/regenerate/diff-view.d.ts +1 -1
- package/dist/regenerate/diff-view.js +20 -4
- package/dist/regenerate/drift.d.ts +47 -15
- package/dist/regenerate/drift.js +101 -30
- package/dist/regenerate/jobs.d.ts +2 -2
- package/dist/regenerate/jobs.js +1 -1
- package/dist/regenerate/ledger.d.ts +19 -0
- package/dist/regenerate/loop.d.ts +62 -43
- package/dist/regenerate/loop.js +472 -341
- package/dist/regenerate/paths.d.ts +4 -0
- package/dist/regenerate/paths.js +7 -1
- package/dist/regenerate/plain-palette.js +2 -0
- package/dist/regenerate/prompt.d.ts +57 -19
- package/dist/regenerate/prompt.js +157 -122
- package/dist/regenerate/records.d.ts +43 -12
- package/dist/regenerate/records.js +102 -88
- package/dist/regenerate/regenerate.js +29 -11
- package/dist/regenerate/regression.d.ts +4 -4
- package/dist/regenerate/regression.js +14 -10
- package/dist/regenerate/renames.d.ts +42 -0
- package/dist/regenerate/renames.js +185 -0
- package/dist/regenerate/requires.d.ts +1 -1
- package/dist/regenerate/requires.js +10 -3
- package/dist/regenerate/resources.d.ts +5 -3
- package/dist/regenerate/resources.js +4 -4
- package/dist/regenerate/scripts-being-written.d.ts +19 -0
- package/dist/regenerate/scripts-being-written.js +30 -0
- package/dist/regenerate/spec-map-view.d.ts +2 -2
- package/dist/regenerate/spec-map-view.js +46 -15
- package/dist/regenerate/spec-map.d.ts +27 -5
- package/dist/regenerate/spec-map.js +92 -60
- package/dist/regenerate/spec-steps.d.ts +48 -5
- package/dist/regenerate/spec-steps.js +213 -28
- package/dist/regenerate/state.d.ts +4 -3
- package/dist/regenerate/stop-report.d.ts +1 -1
- package/dist/regenerate/stop-report.js +10 -10
- package/dist/regenerate/test-advisor.d.ts +101 -0
- package/dist/regenerate/test-advisor.js +379 -0
- package/dist/regenerate/test-kinds.d.ts +71 -28
- package/dist/regenerate/test-kinds.js +110 -63
- package/dist/regenerate/test-scripts.d.ts +2 -0
- package/dist/regenerate/test-scripts.js +4 -0
- package/dist/regenerate/testing-steps.d.ts +41 -0
- package/dist/regenerate/testing-steps.js +110 -0
- package/dist/regenerate/testing.d.ts +1 -1
- package/dist/regenerate/testing.js +5 -4
- package/dist/regenerate/unit-review.d.ts +1 -1
- package/dist/regenerate/unit-review.js +3 -3
- package/dist/regenerate-cli.js +11 -12
- package/dist/release-agent.js +4 -0
- package/dist/release-cli.js +2 -0
- package/dist/release-tools.js +2 -0
- package/dist/run-report.d.ts +2 -2
- package/dist/run-report.js +4 -3
- package/dist/self-invocation.d.ts +1 -1
- package/dist/self-invocation.js +1 -1
- package/dist/sentinel.js +1 -1
- package/dist/setup.d.ts +10 -0
- package/dist/setup.js +52 -4
- package/dist/skill.d.ts +10 -0
- package/dist/skill.js +6 -0
- package/dist/skills.js +3 -1
- package/dist/spec-tools.js +21 -31
- package/dist/test-plain-files.d.ts +41 -0
- package/dist/test-plain-files.js +177 -0
- package/docs/README.md +3 -1
- package/docs/authoring-plain.md +46 -13
- package/docs/chats.md +3 -1
- package/docs/config.md +20 -15
- package/docs/deploy.md +1 -1
- package/docs/development.md +2 -1
- package/docs/headless.md +15 -18
- package/docs/hosting.md +28 -12
- package/docs/installation.md +1 -0
- package/docs/parser.md +88 -27
- package/docs/regenerating.md +87 -52
- package/docs/sentinel.md +1 -1
- package/docs/skills-and-gates.md +3 -1
- package/docs/test-advisor.md +110 -0
- package/docs/theming.md +1 -1
- package/docs/using.md +17 -15
- package/genome/rules/bullet-continuation.md +8 -8
- package/genome/rules/concise-specs-examples.md +111 -80
- package/genome/rules/concise-specs-integration-examples.md +56 -41
- package/genome/rules/concise-specs.md +17 -16
- package/genome/rules/definitions.md +5 -5
- package/genome/rules/examples/integration-embedded-testing/run_conformance_tests_java.sh +1 -1
- package/genome/rules/func-specs.md +69 -74
- package/genome/rules/impl-reqs.md +4 -4
- package/genome/rules/integration-embedded-testing.md +20 -20
- package/genome/rules/integration-embedded.md +31 -31
- package/genome/rules/integration-standalone.md +9 -9
- package/genome/rules/integrations.md +18 -18
- package/genome/rules/linked-resources.md +19 -16
- package/genome/rules/module-structure.md +21 -14
- package/genome/rules/project-model.md +18 -17
- package/genome/rules/required-concepts.md +1 -1
- package/genome/rules/requires-modules.md +6 -5
- package/genome/rules/semantic-ids.md +49 -0
- package/genome/rules/stdlib.md +4 -5
- package/genome/rules/test-plain-files.md +130 -0
- package/genome/rules/test-reqs.md +8 -8
- package/genome/rules/turri-regeneration.md +23 -8
- package/genome/skills/analyze-2-func-specs/SKILL.md +36 -21
- package/genome/skills/analyze-func-specs/SKILL.md +46 -32
- package/genome/skills/break-down-func-spec/SKILL.md +91 -67
- package/genome/skills/consolidate-concepts/SKILL.md +10 -10
- package/genome/skills/convert-old-syntax/SKILL.md +68 -0
- package/genome/skills/convert-old-syntax/references/rubric.md +11 -0
- package/genome/skills/debug-specs/SKILL.md +62 -56
- package/genome/skills/implement-conformance-testing-script/SKILL.md +19 -15
- package/genome/skills/implement-conformance-testing-script/assets/run_conformance_tests_java.sh +2 -2
- package/genome/skills/implement-conformance-testing-script/assets/run_conformance_tests_python.sh +2 -2
- package/genome/skills/implement-unit-testing-script/SKILL.md +14 -10
- package/genome/skills/refactor-module/SKILL.md +35 -35
- package/genome/skills/resolve-section-ownership/SKILL.md +9 -9
- package/genome/skills/resolve-spec-conflict/SKILL.md +35 -29
- package/package.json +7 -6
- package/dist/regenerate/choose-conformance.d.ts +0 -32
- package/dist/regenerate/choose-conformance.js +0 -117
- /package/dist/{validate-cli.d.ts → parse-cli.d.ts} +0 -0
|
@@ -1,17 +1,26 @@
|
|
|
1
1
|
import { existsSync, readdirSync, readFileSync, statSync } from "node:fs";
|
|
2
2
|
import { basename, dirname, join, relative, resolve } from "node:path";
|
|
3
3
|
import { withParseCache } from "../plain/parser.js";
|
|
4
|
-
import { ACCEPTANCE_TESTS, DEFINITIONS, FUNCTIONAL_REQUIREMENTS, NON_FUNCTIONAL_REQUIREMENTS, REQUIRED_FUNCTIONAL_SPECS, TEST_REQUIREMENTS, } from "../plain/spec.js";
|
|
5
|
-
import {
|
|
4
|
+
import { ACCEPTANCE_TESTS, CONFORMANCE_TESTS, DEFINITIONS, FUNCTIONAL_REQUIREMENTS, NON_FUNCTIONAL_REQUIREMENTS, REQUIRED_FUNCTIONAL_SPECS, TEST_REQUIREMENTS, } from "../plain/spec.js";
|
|
5
|
+
import { readProjectConfig, testDirOf } from "../config.js";
|
|
6
|
+
import { testPlainFilesOf } from "../test-plain-files.js";
|
|
6
7
|
import { STDLIB_ROOT } from "../plain/stdlib.js";
|
|
7
8
|
import { conformanceDirFor } from "./conformance.js";
|
|
8
9
|
import { chainSpec } from "./loop.js";
|
|
9
10
|
import { modulesRoot, projectDirOf } from "./paths.js";
|
|
10
11
|
import { HISTORY_DIR } from "./lineage.js";
|
|
11
|
-
import { codeTree, marksRegenerateAgain, readRecord, RECORD_FILE } from "./records.js";
|
|
12
|
+
import { codeTree, hasScenarios, marksRegenerateAgain, readRecord, RECORD_FILE } from "./records.js";
|
|
13
|
+
import { pureRenames } from "./renames.js";
|
|
12
14
|
import { specFilesOf } from "./regenerate.js";
|
|
13
15
|
import { builtTips, resolveRequiresChain } from "./requires.js";
|
|
14
16
|
import { changesBetween, layoutOf, serializeTree } from "./spec-steps.js";
|
|
17
|
+
import { SCRIPTS_DIR } from "./test-scripts.js";
|
|
18
|
+
import { testingGap } from "./testing-steps.js";
|
|
19
|
+
const NO_SCENARIOS = { conformance: [], acceptance: [] };
|
|
20
|
+
/** Whether a functional spec has scenarios - and so conformance tests to write. */
|
|
21
|
+
export function hasScenarioViews(scenarios) {
|
|
22
|
+
return scenarios.conformance.length + scenarios.acceptance.length > 0;
|
|
23
|
+
}
|
|
15
24
|
const itemsOf = (tree, heading) => (tree?.[heading] ?? []).slice();
|
|
16
25
|
/** The sections an item of the code is in, as a page has them: a module below's functional specs among them. */
|
|
17
26
|
const CODE_SECTIONS = [DEFINITIONS, NON_FUNCTIONAL_REQUIREMENTS, FUNCTIONAL_REQUIREMENTS];
|
|
@@ -37,6 +46,20 @@ function changesOf(before, after) {
|
|
|
37
46
|
changesSeen.set(key, changes);
|
|
38
47
|
return changes;
|
|
39
48
|
}
|
|
49
|
+
/** A functional spec's scenarios, as the map shows them. */
|
|
50
|
+
function scenarioViews(item) {
|
|
51
|
+
const views = (list) => (list ?? []).map((scenario) => ({ id: scenario.id ?? "", text: scenario.text ?? "" }));
|
|
52
|
+
return { conformance: views(item[CONFORMANCE_TESTS]), acceptance: views(item[ACCEPTANCE_TESTS]) };
|
|
53
|
+
}
|
|
54
|
+
/** A functional spec's func-id, from its line in a serialized tree. */
|
|
55
|
+
const idOf = (line) => {
|
|
56
|
+
try {
|
|
57
|
+
return JSON.parse(line ?? "").id;
|
|
58
|
+
}
|
|
59
|
+
catch {
|
|
60
|
+
return undefined;
|
|
61
|
+
}
|
|
62
|
+
};
|
|
40
63
|
const markdownOf = (line) => {
|
|
41
64
|
try {
|
|
42
65
|
return JSON.parse(line ?? "").markdown ?? "";
|
|
@@ -57,13 +80,8 @@ export function pageItems(input) {
|
|
|
57
80
|
const before = serializeTree(moduleTree(input.record === null ? null : codeTree(input.record), input.module, input.own));
|
|
58
81
|
const after = serializeTree(codeTree(now));
|
|
59
82
|
const changes = changesOf(before, after);
|
|
60
|
-
// Each
|
|
61
|
-
const
|
|
62
|
-
for (const item of itemsOf(now, FUNCTIONAL_REQUIREMENTS)) {
|
|
63
|
-
const line = serializeTree(codeTree({ [FUNCTIONAL_REQUIREMENTS]: [item] })).split("\n").find((entry) => entry.startsWith("{"));
|
|
64
|
-
if (line !== undefined)
|
|
65
|
-
acceptance.set(line, (item[ACCEPTANCE_TESTS] ?? []).map((test) => test.markdown ?? ""));
|
|
66
|
-
}
|
|
83
|
+
// Each functional spec's scenarios now, by its func-id.
|
|
84
|
+
const scenariosNow = new Map(itemsOf(now, FUNCTIONAL_REQUIREMENTS).map((item) => [item.id ?? "", scenarioViews(item)]));
|
|
67
85
|
// The record's lines, with what is added in front of each and what becomes of each: the spec's order, the ghosts in theirs.
|
|
68
86
|
const lines = before.split("\n").slice(0, -1);
|
|
69
87
|
const removedBy = new Map();
|
|
@@ -77,13 +95,15 @@ export function pageItems(input) {
|
|
|
77
95
|
const items = [];
|
|
78
96
|
const push = (section, state, line, wasLine) => {
|
|
79
97
|
const text = markdownOf(line ?? wasLine);
|
|
98
|
+
const funcId = section === FUNCTIONAL_REQUIREMENTS ? idOf(line ?? wasLine) : undefined;
|
|
80
99
|
items.push({
|
|
81
100
|
key: "",
|
|
82
101
|
section,
|
|
83
102
|
state,
|
|
84
103
|
text,
|
|
85
104
|
...(state === "changed" ? { was: markdownOf(wasLine) } : {}),
|
|
86
|
-
|
|
105
|
+
...(funcId !== undefined ? { funcId } : {}),
|
|
106
|
+
scenarios: funcId !== undefined && line !== undefined ? (scenariosNow.get(funcId) ?? NO_SCENARIOS) : NO_SCENARIOS,
|
|
87
107
|
...(line !== undefined ? { line } : {}),
|
|
88
108
|
...(wasLine !== undefined ? { wasLine } : {}),
|
|
89
109
|
});
|
|
@@ -111,33 +131,34 @@ export function pageItems(input) {
|
|
|
111
131
|
const testReqs = new Set(itemsOf(input.tested ?? null, TEST_REQUIREMENTS).map((item) => item.markdown.trim()));
|
|
112
132
|
if (input.own) {
|
|
113
133
|
for (const item of itemsOf(input.spec, TEST_REQUIREMENTS)) {
|
|
114
|
-
items.push({ key: "", section: TEST_REQUIREMENTS, state: "test", text: item.markdown,
|
|
134
|
+
items.push({ key: "", section: TEST_REQUIREMENTS, state: "test", text: item.markdown, scenarios: NO_SCENARIOS, ...(testReqs.has(item.markdown.trim()) ? { tests: "now" } : {}) });
|
|
115
135
|
}
|
|
116
136
|
}
|
|
117
|
-
// Conformance tests: of the functional specs, those the tests record has as they read now
|
|
118
|
-
// Tests marked to regenerate again were written against code since
|
|
119
|
-
|
|
120
|
-
const
|
|
121
|
-
const
|
|
137
|
+
// Conformance tests: of the functional specs, by func-id, those the tests record has as they read now - their text and
|
|
138
|
+
// their scenarios - as they read before, or taken out. Tests marked to regenerate again were written against code since
|
|
139
|
+
// replaced: written for an earlier build, not now.
|
|
140
|
+
const testedItems = new Map(itemsOf(input.tested ?? null, FUNCTIONAL_REQUIREMENTS).map((item) => [item.id ?? "", item]));
|
|
141
|
+
const asNow = new Map(itemsOf(now, FUNCTIONAL_REQUIREMENTS).map((item) => [item.id ?? "", item]));
|
|
142
|
+
const reads = (item) => JSON.stringify({ markdown: item.markdown.trim(), scenarios: scenarioViews(item) });
|
|
122
143
|
for (const item of items) {
|
|
123
|
-
if (item.section !== FUNCTIONAL_REQUIREMENTS ||
|
|
144
|
+
if (item.section !== FUNCTIONAL_REQUIREMENTS || item.funcId === undefined)
|
|
124
145
|
continue;
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
item.tests = "
|
|
131
|
-
else
|
|
132
|
-
item.tests = "older";
|
|
146
|
+
const tested = testedItems.get(item.funcId);
|
|
147
|
+
if (tested === undefined)
|
|
148
|
+
continue;
|
|
149
|
+
const current = asNow.get(item.funcId);
|
|
150
|
+
if (item.state === "removed")
|
|
151
|
+
item.tests = "removed";
|
|
152
|
+
else
|
|
153
|
+
item.tests = !marksRegenerateAgain(tested) && current !== undefined && reads(tested) === reads(current) ? "now" : "older";
|
|
133
154
|
}
|
|
134
155
|
return keyed(items);
|
|
135
156
|
}
|
|
136
|
-
/** Each item's key: its section, whether it is a ghost, its text - and which of the same it is. */
|
|
157
|
+
/** Each item's key: its section, whether it is a ghost, its text - a functional spec's func-id - and which of the same it is. */
|
|
137
158
|
function keyed(items) {
|
|
138
159
|
const seen = new Map();
|
|
139
160
|
for (const item of items) {
|
|
140
|
-
const base = `${item.section}\n${item.state === "removed" ? "-" : "+"}${item.text.trim()}`;
|
|
161
|
+
const base = `${item.section}\n${item.state === "removed" ? "-" : "+"}${item.funcId !== undefined ? `# ${item.funcId}` : item.text.trim()}`;
|
|
141
162
|
const count = seen.get(base) ?? 0;
|
|
142
163
|
seen.set(base, count + 1);
|
|
143
164
|
item.key = count === 0 ? base : `${base}\n${count}`;
|
|
@@ -201,7 +222,9 @@ const started = (entry) => {
|
|
|
201
222
|
/** The files a module's page is read from: its spec files, its records, and its history - a commit moves the record read. */
|
|
202
223
|
function depsOf(entry, codebase, testsDir) {
|
|
203
224
|
const history = (dir) => [join(dirname(dir), HISTORY_DIR, "logs", "HEAD"), join(dir, RECORD_FILE)];
|
|
204
|
-
|
|
225
|
+
// Its test plain files, and their folder: one added or taken out changes what the map reads.
|
|
226
|
+
const testPlain = [join(testDirOf(entry.plainFile), entry.moduleName), ...testPlainFilesOf(entry.plainFile)];
|
|
227
|
+
return [entry.plainFile, ...testPlain, ...history(codebase.buildDir), ...history(testsDir), dirname(dirname(codebase.buildDir))];
|
|
205
228
|
}
|
|
206
229
|
/** The chain a file's map reads: where its code lives - its furthest-built tip - or its own requires chain. */
|
|
207
230
|
function chainOf(plainFile) {
|
|
@@ -258,7 +281,6 @@ export function readSpecMapWithDeps(files) {
|
|
|
258
281
|
imports: [],
|
|
259
282
|
items: [],
|
|
260
283
|
...(entry.requires !== null ? { requires: entry.requires } : {}),
|
|
261
|
-
conformance: conformanceOn(entry.plainFile),
|
|
262
284
|
};
|
|
263
285
|
const spec = specOf(home);
|
|
264
286
|
if (spec instanceof Error) {
|
|
@@ -266,14 +288,26 @@ export function readSpecMapWithDeps(files) {
|
|
|
266
288
|
return;
|
|
267
289
|
}
|
|
268
290
|
const projectDir = projectDirOf(entry.plainFile);
|
|
269
|
-
|
|
270
|
-
const
|
|
291
|
+
// A pure rename not made yet is no work: shown as built, under its new func-id, with the one it is renamed from.
|
|
292
|
+
const renamed = home <= furthest ? pureRenames(readRecord(codebase.buildDir), readRecord(conformanceDirFor(codebase.moduleName, modulesRoot(projectDir))), spec, codebase.moduleName) : undefined;
|
|
293
|
+
const record = renamed?.code ?? null;
|
|
294
|
+
// What the conformance tests are written for, in the codebase that holds its code: the tip's suite holds the chain's.
|
|
295
|
+
const testsRecord = renamed?.tests ?? null;
|
|
296
|
+
const tested = testsRecord === null ? null : { ...testsRecord, [FUNCTIONAL_REQUIREMENTS]: moduleTree(testsRecord, entry.moduleName, home === at)[FUNCTIONAL_REQUIREMENTS] };
|
|
271
297
|
const paths = specFilesOf(entry.plainFile);
|
|
272
|
-
for (const path of [...paths, ...depsOf(entry, codebase, conformanceDirFor(
|
|
298
|
+
for (const path of [...paths, ...depsOf(entry, codebase, conformanceDirFor(codebase.moduleName, modulesRoot(projectDir)))])
|
|
273
299
|
deps.add(path);
|
|
274
300
|
const texts = paths.map((path, index) => ({ ...(index > 0 ? { label: importLabel(path, projectDir) } : {}), text: readText(path) }));
|
|
275
|
-
const
|
|
276
|
-
|
|
301
|
+
const renamedFrom = new Map((renamed?.renames ?? []).flatMap((rename) => (rename.kind === "func-id" && rename.module === entry.moduleName ? [[rename.to, rename.from]] : [])));
|
|
302
|
+
const items = pageItems({ module: entry.moduleName, own: home === at, spec, record, tested }).map((item) => item.funcId !== undefined && renamedFrom.has(item.funcId) && item.state !== "removed" ? { ...item, renamedFrom: renamedFrom.get(item.funcId) } : item);
|
|
303
|
+
const own = home === at ? (spec[FUNCTIONAL_REQUIREMENTS] ?? []) : (spec[REQUIRED_FUNCTIONAL_SPECS] ?? []).filter((item) => item.module === entry.moduleName);
|
|
304
|
+
// Its scenarios untested for a step of adding tests skipped in the codebase that writes their tests: said on its row.
|
|
305
|
+
// Read again once a script is added to the project, or taken out.
|
|
306
|
+
if (own.some(hasScenarios))
|
|
307
|
+
for (const path of [projectDir, join(projectDir, SCRIPTS_DIR)])
|
|
308
|
+
deps.add(path);
|
|
309
|
+
const gap = own.some(hasScenarios) ? testingGap({ moduleName: codebase.moduleName, plainFile: codebase.plainFile, tree: spec, buildDir: codebase.buildDir }) : undefined;
|
|
310
|
+
pages.push({ ...page, conformance: own.some(hasScenarios), ...(gap !== undefined ? { testingMissing: gap.short } : {}), imports: texts.slice(1).map((text) => text.label), items: layOut(items, texts) });
|
|
277
311
|
});
|
|
278
312
|
}
|
|
279
313
|
return pages;
|
|
@@ -294,15 +328,6 @@ function fingerprint(paths) {
|
|
|
294
328
|
})
|
|
295
329
|
.join("\n");
|
|
296
330
|
}
|
|
297
|
-
/** Whether a module's config has its conformance tests on: `conformance-tests:` is all (true) or chosen - off when not set. */
|
|
298
|
-
function conformanceOn(plainFile) {
|
|
299
|
-
try {
|
|
300
|
-
return conformanceSwitchedOn(configForSpec(plainFile).conformanceTests);
|
|
301
|
-
}
|
|
302
|
-
catch {
|
|
303
|
-
return false;
|
|
304
|
-
}
|
|
305
|
-
}
|
|
306
331
|
function readText(path) {
|
|
307
332
|
try {
|
|
308
333
|
return readFileSync(path, "utf8");
|
|
@@ -318,7 +343,7 @@ function linesOf(pulse) {
|
|
|
318
343
|
let seen = linesSeen.get(pulse);
|
|
319
344
|
if (seen === undefined) {
|
|
320
345
|
const set = (text) => new Set(text.split("\n").filter((line) => line.startsWith("{")));
|
|
321
|
-
// A conformance pulse's trees are the tests record's: its functional specs' lines - each with its
|
|
346
|
+
// A conformance pulse's trees are the tests record's: its functional specs' lines - each with its func-id and scenarios.
|
|
322
347
|
const specLines = (text, of = FUNCTIONAL_REQUIREMENTS) => {
|
|
323
348
|
const out = [];
|
|
324
349
|
let section = "";
|
|
@@ -336,25 +361,28 @@ function linesOf(pulse) {
|
|
|
336
361
|
return new Set(specLines(pulse.after, TEST_REQUIREMENTS).filter((line) => !before.has(line)).map((line) => markdownOf(line).trim()));
|
|
337
362
|
};
|
|
338
363
|
const testing = () => {
|
|
339
|
-
// What it writes tests for: each functional spec whose line is new - its text
|
|
340
|
-
//
|
|
364
|
+
// What it writes tests for: each functional spec whose line is new - its text or its scenarios changed - by its
|
|
365
|
+
// func-id; none, and it is the definitions, reqs or test reqs that changed: it tests every one again.
|
|
341
366
|
const before = new Set(specLines(pulse.before));
|
|
342
367
|
const after = specLines(pulse.after);
|
|
343
368
|
const changed = after.filter((line) => !before.has(line));
|
|
344
|
-
return new Set((changed.length > 0 ? changed : after).map((line) =>
|
|
369
|
+
return new Set((changed.length > 0 ? changed : after).map((line) => idOf(line) ?? ""));
|
|
345
370
|
};
|
|
346
371
|
seen = pulse.track === "code" ? { before: set(pulse.before), after: set(pulse.after), testing: new Set(), testReqs: new Set() } : { before: new Set(), after: new Set(), testing: testing(), testReqs: testReqs() };
|
|
347
372
|
linesSeen.set(pulse, seen);
|
|
348
373
|
}
|
|
349
374
|
return seen;
|
|
350
375
|
}
|
|
351
|
-
/**
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
376
|
+
/**
|
|
377
|
+
* Whether a conformance pulse writes tests for a functional spec, by its func-id: by its trees, or - following the code
|
|
378
|
+
* still to build, with none - by the functional specs it names, `module:func-id`.
|
|
379
|
+
*/
|
|
380
|
+
function writesTestsFor(pulse, item, module) {
|
|
381
|
+
if (item.funcId === undefined)
|
|
382
|
+
return false;
|
|
355
383
|
if (pulse.specs !== undefined)
|
|
356
|
-
return pulse.specs.
|
|
357
|
-
return linesOf(pulse).testing.has(item.
|
|
384
|
+
return pulse.specs.includes(`${module}:${item.funcId}`);
|
|
385
|
+
return linesOf(pulse).testing.has(item.funcId);
|
|
358
386
|
}
|
|
359
387
|
/** Whether a pulse regenerates an item: brings its line now, or takes out its line in the record. */
|
|
360
388
|
function regenerates(pulse, item) {
|
|
@@ -383,6 +411,9 @@ function needsText(stuck) {
|
|
|
383
411
|
* for in the running conformance pulse, planned in one, or not yet - none, where no conformance test is written at all.
|
|
384
412
|
*/
|
|
385
413
|
function testReqMark(item, codebase, page) {
|
|
414
|
+
// No functional spec of the module has scenarios: nothing uses its test reqs.
|
|
415
|
+
if (page.conformance === false)
|
|
416
|
+
return { mark: "none", unused: true };
|
|
386
417
|
if (item.tests === "now")
|
|
387
418
|
return { mark: "built" };
|
|
388
419
|
const text = item.text.trim();
|
|
@@ -409,16 +440,15 @@ export function markSpecMap(pages, plan) {
|
|
|
409
440
|
const stuckPulse = codebase?.stuck?.pulse;
|
|
410
441
|
const failed = stuckPulse !== undefined ? { number: stuckPulse.number, title: "", track: "code", running: false, rowId: "", before: stuckPulse.before, after: stuckPulse.after } : undefined;
|
|
411
442
|
const conformance = codebase?.pulses.find((pulse) => pulse.running && pulse.track === "conformance");
|
|
412
|
-
// Test reqs
|
|
413
|
-
const
|
|
414
|
-
const items = page.items.filter((item) => item.state !== "test" || conformanceIsOn).map((item) => {
|
|
443
|
+
// Test reqs are always shown: where no functional spec has scenarios, as not used.
|
|
444
|
+
const items = page.items.map((item) => {
|
|
415
445
|
const testing = conformance !== undefined && item.section === FUNCTIONAL_REQUIREMENTS && item.state !== "removed" && (() => {
|
|
416
|
-
return linesOf(conformance).testing.has(item.
|
|
446
|
+
return item.funcId !== undefined && linesOf(conformance).testing.has(item.funcId);
|
|
417
447
|
})();
|
|
418
448
|
// Not being tested now: the conformance pulse planned to - its tests written for it as it reads now, or not.
|
|
419
449
|
const testsPulse = testing || item.section !== FUNCTIONAL_REQUIREMENTS || item.state === "removed"
|
|
420
450
|
? undefined
|
|
421
|
-
: codebase?.pulses.find((pulse) => !pulse.running && pulse.track === "conformance" && writesTestsFor(pulse, item));
|
|
451
|
+
: codebase?.pulses.find((pulse) => !pulse.running && pulse.track === "conformance" && writesTestsFor(pulse, item, page.module));
|
|
422
452
|
const extra = testing ? { testing: true } : testsPulse !== undefined ? { testsPulse } : {};
|
|
423
453
|
if (item.state === "test")
|
|
424
454
|
return { ...item, ...testReqMark(item, codebase, page), ...extra };
|
|
@@ -460,7 +490,9 @@ export function markSpecMap(pages, plan) {
|
|
|
460
490
|
: differing === 0
|
|
461
491
|
? "all built"
|
|
462
492
|
: (codebase.why ?? "waiting");
|
|
463
|
-
|
|
493
|
+
// A step of adding tests skipped: said after whatever else is happening, on the module's row.
|
|
494
|
+
const said = page.testingMissing !== undefined ? `${status} · ${page.testingMissing}` : status;
|
|
495
|
+
return { ...page, items, tracked: codebase !== undefined, ...(codebase !== undefined ? { codebase } : {}), status: said, stuck: codebase?.stuck !== undefined && running === undefined };
|
|
464
496
|
});
|
|
465
497
|
}
|
|
466
498
|
/**
|
|
@@ -1,13 +1,14 @@
|
|
|
1
|
-
import { type MarshalledPlainSource } from "../plain/spec.ts";
|
|
1
|
+
import { ACCEPTANCE_TESTS, CONFORMANCE_TESTS, type MarshalledPlainSource } from "../plain/spec.ts";
|
|
2
2
|
/**
|
|
3
3
|
* The steps from one spec to another: a sequence of spec diffs that, rendered one after the
|
|
4
4
|
* other, take code built from the first spec to code built from the second.
|
|
5
5
|
*
|
|
6
6
|
* Both specs are marshalled trees, written out as text with one JSON value per line - a
|
|
7
7
|
* section's name, then each of its items on a line of its own - and compared with git. So an
|
|
8
|
-
* item is the unit of change: a functional spec with its
|
|
8
|
+
* item is the unit of change: a functional spec with its scenarios and linked resources,
|
|
9
9
|
* a definition, a req. Git lines them up, so a spec inserted in the middle is one addition and
|
|
10
|
-
* the specs after it are untouched
|
|
10
|
+
* the specs after it are untouched. A functional spec is known by its func-id: one taken out and
|
|
11
|
+
* one put in with the same func-id are the same functional spec, changed - or, the same text, moved.
|
|
11
12
|
*
|
|
12
13
|
* The planner (`chunk-plan.ts`) cuts the changes into steps - which go together, which go first. It
|
|
13
14
|
* picks changes by id; it never writes a diff. `makeStep` applies what it picked, and `inconsistencies`
|
|
@@ -63,10 +64,39 @@ export interface SpecChange {
|
|
|
63
64
|
}
|
|
64
65
|
/** The changes that take one serialized tree to another, in the order git lists them. */
|
|
65
66
|
export declare function changesBetween(before: string, after: string): SpecChange[];
|
|
67
|
+
/** Whether a change moves a functional spec and leaves what it says as it was. */
|
|
68
|
+
export declare function isMove(change: SpecChange): boolean;
|
|
66
69
|
/** How alike two items are: the share of their words they have in common. */
|
|
67
70
|
export declare function similarity(a: string, b: string): number;
|
|
68
71
|
/** A selection of changes, applied to the serialized tree they were found in. */
|
|
69
72
|
export declare function applyChanges(before: string, changes: SpecChange[]): string;
|
|
73
|
+
/** A functional spec's scenarios of one kind, as a person reads them: each under its scenario-id, in its section's tag. */
|
|
74
|
+
export declare function scenarioLines(tag: string, scenarios: Array<{
|
|
75
|
+
id?: string;
|
|
76
|
+
text?: string;
|
|
77
|
+
}> | undefined): string[];
|
|
78
|
+
type ScenarioItem = {
|
|
79
|
+
id?: string;
|
|
80
|
+
text?: string;
|
|
81
|
+
};
|
|
82
|
+
type ScenarioCarrier = {
|
|
83
|
+
id?: string;
|
|
84
|
+
[CONFORMANCE_TESTS]?: ScenarioItem[];
|
|
85
|
+
[ACCEPTANCE_TESTS]?: ScenarioItem[];
|
|
86
|
+
};
|
|
87
|
+
/** A functional spec's test plain file, as it would read: its binding, then each section that has scenarios, each scenario under its id. */
|
|
88
|
+
export declare function testPlainFileText(module: string, item: ScenarioCarrier): string;
|
|
89
|
+
/** One file's two texts as git diffs them, named `path` on both sides: an empty side is the file added or taken out whole. */
|
|
90
|
+
export declare function diffFileTexts(before: string, after: string, path: string): string;
|
|
91
|
+
/** Where a functional spec's test plain file is, by its func-id and - a module below's - its module: the path a diff names it by. */
|
|
92
|
+
export type TestPlainPath = (funcId: string, module?: string) => string;
|
|
93
|
+
/**
|
|
94
|
+
* The functional specs' test plain files a change between two serialized trees touches, each as a diff of its own,
|
|
95
|
+
* rebuilt from the trees as the module's file is: only the trees that carry scenarios - the conformance tests' - have
|
|
96
|
+
* any. A functional spec that had no scenarios has no test plain file before, so its file shows as added whole; one whose
|
|
97
|
+
* scenarios all went, as deleted whole - an empty one is a syntax error.
|
|
98
|
+
*/
|
|
99
|
+
export declare function diffTestPlainFiles(before: string, after: string, module: string, pathOf: TestPlainPath): string;
|
|
70
100
|
/** A serialized tree as a person reads it, section by section. */
|
|
71
101
|
export declare function treeText(serialized: string): string;
|
|
72
102
|
/** A change as the planning agent is shown it. */
|
|
@@ -107,7 +137,7 @@ export interface SpecLayout {
|
|
|
107
137
|
sections: string[];
|
|
108
138
|
/** Each concept's place in the file's definitions - the parser orders them by what uses what. */
|
|
109
139
|
concepts: Map<string, number>;
|
|
110
|
-
/** The sections whose items the file writes with no blank line between them. */
|
|
140
|
+
/** The sections whose items the file writes with no blank line between them - definitions and reqs: functional specs never are. */
|
|
111
141
|
tight: Set<string>;
|
|
112
142
|
}
|
|
113
143
|
export declare function layoutOf(text: string): SpecLayout;
|
|
@@ -122,6 +152,11 @@ export declare function layoutOf(text: string): SpecLayout;
|
|
|
122
152
|
export declare function plainText(serialized: string, layout?: SpecLayout): string;
|
|
123
153
|
/** git's diff of two serialized trees, as ***plain text - see plainText. */
|
|
124
154
|
export declare function diffSpecText(before: string, after: string, file?: string, layout?: SpecLayout): string;
|
|
155
|
+
/**
|
|
156
|
+
* The functional specs of the modules below that a change between two serialized trees touches, each module's as a diff
|
|
157
|
+
* of its own file's `***functional specs***`: the tip is built with them, but its own file does not hold them.
|
|
158
|
+
*/
|
|
159
|
+
export declare function diffRequiredSpecs(before: string, after: string, file?: string): string;
|
|
125
160
|
/** The linked resources of a changed item whose content is different now - each hashed on its own: by their path, with both hashes. */
|
|
126
161
|
export declare function resourcesChanged(before: string | undefined, after: string | undefined): Array<{
|
|
127
162
|
target: string;
|
|
@@ -134,4 +169,12 @@ export declare function resourcesChanged(before: string | undefined, after: stri
|
|
|
134
169
|
* held before - so the hunk says that it changed, and what it holds now is the file itself.
|
|
135
170
|
*/
|
|
136
171
|
export declare function resourceDiff(changes: SpecChange[], file?: string): string;
|
|
137
|
-
|
|
172
|
+
/**
|
|
173
|
+
* A step of the work. `testPlain`: for the conformance tests' steps, the module and where its functional specs' test plain
|
|
174
|
+
* files are - a scenario changed shows as a diff of its test plain file, after the module's.
|
|
175
|
+
*/
|
|
176
|
+
export declare function makeStep(title: string, reason: string, changes: SpecChange[], before: string, after: string, file: string, layout?: SpecLayout, testPlain?: {
|
|
177
|
+
module: string;
|
|
178
|
+
pathOf: TestPlainPath;
|
|
179
|
+
}): SpecStep;
|
|
180
|
+
export {};
|