@printpractical/cruze 0.0.13 → 0.0.14

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 (298) hide show
  1. package/README.md +61 -118
  2. package/dist/adapters/inbound/cli/cli_context.js +0 -27
  3. package/dist/adapters/inbound/cli/commands/code.js +5 -18
  4. package/dist/adapters/inbound/cli/commands/documents.js +11 -37
  5. package/dist/adapters/inbound/cli/commands/init.js +1 -1
  6. package/dist/adapters/inbound/cli/commands/review.js +2 -9
  7. package/dist/adapters/inbound/cli/commands/status.js +16 -63
  8. package/dist/adapters/inbound/cli/commands.js +10 -37
  9. package/dist/adapters/inbound/cli/parse_command.js +0 -54
  10. package/dist/adapters/outbound/git_repository.js +2 -71
  11. package/dist/app/ports/repository.js +0 -12
  12. package/dist/app/project_context.js +15 -25
  13. package/dist/app/use_cases/approve_document.js +29 -0
  14. package/dist/app/use_cases/check_code.js +21 -56
  15. package/dist/app/use_cases/new_document.js +25 -0
  16. package/dist/app/use_cases/project_status.js +43 -67
  17. package/dist/app/use_cases/run_review.js +11 -30
  18. package/dist/app/use_cases/validate_project.js +4 -4
  19. package/dist/domain/approvals.js +51 -0
  20. package/dist/domain/check/check_source.js +2 -31
  21. package/dist/domain/config.js +8 -84
  22. package/dist/domain/hashing.js +16 -30
  23. package/dist/domain/ids.js +13 -27
  24. package/dist/domain/next_step.js +41 -0
  25. package/dist/domain/project/features.js +71 -0
  26. package/dist/domain/project/layout.js +11 -27
  27. package/dist/domain/project/project_docs.js +48 -0
  28. package/dist/domain/project/release.js +30 -0
  29. package/dist/domain/scaffold.js +0 -1
  30. package/dist/domain/skill_budget.js +4 -4
  31. package/dist/domain/trace.js +8 -25
  32. package/dist/domain/validation/document_rules.js +70 -0
  33. package/dist/domain/validation/release_rules.js +45 -0
  34. package/dist/domain/validation/validate_project.js +20 -27
  35. package/dist/domain/versions/cruze_version.js +3 -9
  36. package/dist/main.js +1 -1
  37. package/package.json +1 -1
  38. package/skills/asking/SKILL.md +51 -0
  39. package/skills/behavioural-testing/SKILL.md +14 -16
  40. package/skills/behavioural-testing/examples.md +1 -2
  41. package/skills/behavioural-testing/fakes.md +2 -2
  42. package/skills/build/SKILL.md +34 -38
  43. package/skills/dependency-approval/SKILL.md +34 -31
  44. package/skills/envision/SKILL.md +23 -18
  45. package/skills/envision/architecture.md +16 -0
  46. package/skills/envision/breakdown.md +14 -0
  47. package/skills/envision/possibilities.md +6 -7
  48. package/skills/fix/SKILL.md +34 -0
  49. package/skills/formats/SKILL.md +39 -87
  50. package/skills/formats/config.md +47 -0
  51. package/skills/formats/templates/architecture.md +18 -74
  52. package/skills/formats/templates/feature.md +18 -34
  53. package/skills/formats/templates/vision.md +15 -27
  54. package/skills/hexagonal-design/SKILL.md +11 -11
  55. package/skills/hexagonal-design/contracts.md +3 -3
  56. package/skills/hexagonal-design/fundamentals.md +1 -1
  57. package/skills/hexagonal-design/languages/cpp.md +1 -1
  58. package/skills/hexagonal-design/languages/rust.md +1 -1
  59. package/skills/hexagonal-design/languages/swift.md +2 -2
  60. package/skills/hexagonal-design/runtime.md +2 -2
  61. package/skills/research/SKILL.md +4 -4
  62. package/skills/research/evaluation.md +1 -1
  63. package/skills/review/SKILL.md +27 -0
  64. package/skills/roles/SKILL.md +8 -59
  65. package/skills/roles/code-reviewer.md +21 -32
  66. package/skills/roles/researcher.md +3 -3
  67. package/skills/shape/SKILL.md +37 -0
  68. package/skills/shape/self-check.md +12 -0
  69. package/src/adapters/inbound/cli/cli_context.ts +0 -27
  70. package/src/adapters/inbound/cli/commands/code.ts +5 -18
  71. package/src/adapters/inbound/cli/commands/documents.ts +11 -37
  72. package/src/adapters/inbound/cli/commands/init.ts +1 -1
  73. package/src/adapters/inbound/cli/commands/review.ts +2 -9
  74. package/src/adapters/inbound/cli/commands/status.ts +16 -63
  75. package/src/adapters/inbound/cli/commands.ts +10 -37
  76. package/src/adapters/inbound/cli/parse_command.ts +0 -54
  77. package/src/adapters/outbound/git_repository.ts +2 -71
  78. package/src/app/ports/repository.ts +0 -12
  79. package/src/app/project_context.ts +15 -25
  80. package/src/app/use_cases/approve_document.ts +29 -0
  81. package/src/app/use_cases/check_code.ts +21 -56
  82. package/src/app/use_cases/new_document.ts +25 -0
  83. package/src/app/use_cases/project_status.ts +49 -73
  84. package/src/app/use_cases/run_review.ts +11 -30
  85. package/src/app/use_cases/validate_project.ts +4 -4
  86. package/src/domain/approvals.ts +51 -0
  87. package/src/domain/check/check_source.ts +2 -31
  88. package/src/domain/config.ts +9 -85
  89. package/src/domain/hashing.ts +16 -30
  90. package/src/domain/ids.ts +13 -27
  91. package/src/domain/next_step.ts +41 -0
  92. package/src/domain/project/features.ts +71 -0
  93. package/src/domain/project/layout.ts +11 -27
  94. package/src/domain/project/project_docs.ts +48 -0
  95. package/src/domain/project/release.ts +30 -0
  96. package/src/domain/scaffold.ts +0 -1
  97. package/src/domain/skill_budget.ts +4 -4
  98. package/src/domain/trace.ts +8 -25
  99. package/src/domain/validation/document_rules.ts +70 -0
  100. package/src/domain/validation/release_rules.ts +45 -0
  101. package/src/domain/validation/validate_project.ts +21 -28
  102. package/src/domain/versions/cruze_version.ts +3 -9
  103. package/src/main.ts +1 -1
  104. package/templates/init/AGENTS.md +4 -12
  105. package/templates/init/ci.yml +3 -3
  106. package/templates/init/config.yaml +8 -17
  107. package/templates/init/review/claude.yaml +2 -2
  108. package/dist/adapters/inbound/cli/argument_files.js +0 -29
  109. package/dist/adapters/inbound/cli/commands/diff.js +0 -23
  110. package/dist/adapters/inbound/cli/commands/journal.js +0 -32
  111. package/dist/adapters/inbound/cli/commands/progress.js +0 -55
  112. package/dist/adapters/inbound/cli/commands/realign.js +0 -28
  113. package/dist/adapters/inbound/cli/commands/show.js +0 -96
  114. package/dist/app/use_cases/abandon_work.js +0 -42
  115. package/dist/app/use_cases/approve_artifact.js +0 -197
  116. package/dist/app/use_cases/journal_events.js +0 -64
  117. package/dist/app/use_cases/land_change.js +0 -134
  118. package/dist/app/use_cases/new_project_doc.js +0 -48
  119. package/dist/app/use_cases/new_work_item.js +0 -75
  120. package/dist/app/use_cases/realign_done.js +0 -45
  121. package/dist/app/use_cases/realign_status.js +0 -106
  122. package/dist/app/use_cases/record_progress.js +0 -118
  123. package/dist/app/use_cases/show_elements.js +0 -18
  124. package/dist/app/use_cases/show_notes.js +0 -11
  125. package/dist/app/use_cases/show_scope.js +0 -51
  126. package/dist/app/use_cases/upstream_diff.js +0 -55
  127. package/dist/domain/approvals/agent_approval.js +0 -68
  128. package/dist/domain/approvals/evaluate.js +0 -36
  129. package/dist/domain/approvals/fingerprint.js +0 -52
  130. package/dist/domain/approvals/rebase.js +0 -39
  131. package/dist/domain/approvals/records.js +0 -50
  132. package/dist/domain/approvals/upstream_text.js +0 -36
  133. package/dist/domain/architecture_sections.js +0 -23
  134. package/dist/domain/check/decision_comments.js +0 -38
  135. package/dist/domain/decision_citations.js +0 -62
  136. package/dist/domain/edits/agents_file.js +0 -58
  137. package/dist/domain/edits/managed_tables.js +0 -60
  138. package/dist/domain/edits/progress_block.js +0 -33
  139. package/dist/domain/edits/status_lines.js +0 -52
  140. package/dist/domain/elements.js +0 -126
  141. package/dist/domain/journal.js +0 -109
  142. package/dist/domain/land/bookkeeping.js +0 -54
  143. package/dist/domain/land/merge_plan.js +0 -137
  144. package/dist/domain/land/text_ops.js +0 -66
  145. package/dist/domain/line_diff.js +0 -56
  146. package/dist/domain/project/artifact_ref.js +0 -42
  147. package/dist/domain/project/deltas.js +0 -49
  148. package/dist/domain/project/element_lookup.js +0 -124
  149. package/dist/domain/project/living_model.js +0 -55
  150. package/dist/domain/project/new_items.js +0 -48
  151. package/dist/domain/project/notes.js +0 -74
  152. package/dist/domain/project/plan_parts.js +0 -111
  153. package/dist/domain/project/progress.js +0 -43
  154. package/dist/domain/project/project_view.js +0 -72
  155. package/dist/domain/project/roadmap.js +0 -43
  156. package/dist/domain/project/scope_view.js +0 -134
  157. package/dist/domain/project/work_items.js +0 -68
  158. package/dist/domain/status/next_step.js +0 -181
  159. package/dist/domain/status/pending_rethink.js +0 -22
  160. package/dist/domain/status/verification.js +0 -29
  161. package/dist/domain/status/work_status.js +0 -125
  162. package/dist/domain/validation/decision_rules.js +0 -76
  163. package/dist/domain/validation/delta_rules.js +0 -81
  164. package/dist/domain/validation/element_rules.js +0 -104
  165. package/dist/domain/validation/future_rules.js +0 -40
  166. package/dist/domain/validation/living_rules.js +0 -99
  167. package/dist/domain/validation/plan_rules.js +0 -124
  168. package/dist/domain/validation/project_rules.js +0 -115
  169. package/dist/domain/validation/size_rules.js +0 -45
  170. package/dist/domain/validation/vision_rules.js +0 -41
  171. package/dist/domain/validation/work_item_rules.js +0 -175
  172. package/dist/domain/versions/realign_notes.js +0 -72
  173. package/dist/domain/versions/rule_versions.js +0 -29
  174. package/dist/domain/versions/scheduled_exceptions.js +0 -41
  175. package/skills/about/SKILL.md +0 -21
  176. package/skills/architect/SKILL.md +0 -49
  177. package/skills/architect/feature.md +0 -40
  178. package/skills/architect/project.md +0 -71
  179. package/skills/architect/tweak.md +0 -22
  180. package/skills/architect/walkthrough.md +0 -35
  181. package/skills/domain-language/SKILL.md +0 -37
  182. package/skills/envision/feature.md +0 -25
  183. package/skills/envision/intake.md +0 -28
  184. package/skills/envision/project.md +0 -24
  185. package/skills/explore/SKILL.md +0 -39
  186. package/skills/formats/reference/architecture.md +0 -84
  187. package/skills/formats/reference/config.md +0 -80
  188. package/skills/formats/reference/durable-docs.md +0 -74
  189. package/skills/formats/reference/specs.md +0 -65
  190. package/skills/formats/reference/work-items.md +0 -100
  191. package/skills/formats/templates/adr.md +0 -7
  192. package/skills/formats/templates/change.md +0 -29
  193. package/skills/formats/templates/glossary.md +0 -8
  194. package/skills/formats/templates/roadmap.md +0 -32
  195. package/skills/formats/templates/spec.md +0 -11
  196. package/skills/formats/templates/standalone-change.md +0 -46
  197. package/skills/grilling/SKILL.md +0 -90
  198. package/skills/grilling/illustrating.md +0 -38
  199. package/skills/improve/SKILL.md +0 -51
  200. package/skills/land/SKILL.md +0 -48
  201. package/skills/next/SKILL.md +0 -31
  202. package/skills/plan/SKILL.md +0 -62
  203. package/skills/plan/tasks.md +0 -35
  204. package/skills/plan/test-plan.md +0 -14
  205. package/skills/realign/SKILL.md +0 -37
  206. package/skills/realign/notes/0.0.10.md +0 -131
  207. package/skills/realign/notes/0.0.11.md +0 -15
  208. package/skills/realign/notes/0.0.12.md +0 -31
  209. package/skills/realign/notes/0.0.13.md +0 -11
  210. package/skills/realign/notes/0.0.2.md +0 -69
  211. package/skills/realign/notes/0.0.3.md +0 -11
  212. package/skills/realign/notes/0.0.4.md +0 -27
  213. package/skills/realign/notes/0.0.5.md +0 -38
  214. package/skills/realign/notes/0.0.6.md +0 -44
  215. package/skills/realign/notes/0.0.7.md +0 -11
  216. package/skills/realign/notes/0.0.8.md +0 -97
  217. package/skills/realign/notes/0.0.9.md +0 -59
  218. package/skills/realign/notes.md +0 -40
  219. package/skills/rethink/SKILL.md +0 -62
  220. package/skills/rethink/levels.md +0 -54
  221. package/skills/roadmap/SKILL.md +0 -54
  222. package/skills/roles/auditor.md +0 -35
  223. package/skills/roles/design-reviewer.md +0 -43
  224. package/skills/roles/design-rubric.md +0 -41
  225. package/skills/roles/plan-rubric.md +0 -11
  226. package/skills/roles/surveyor.md +0 -41
  227. package/skills/roles/verifier.md +0 -42
  228. package/skills/triage/SKILL.md +0 -37
  229. package/skills/verify/SKILL.md +0 -45
  230. package/skills/verify/manual-test.md +0 -23
  231. package/src/adapters/inbound/cli/argument_files.ts +0 -29
  232. package/src/adapters/inbound/cli/commands/diff.ts +0 -23
  233. package/src/adapters/inbound/cli/commands/journal.ts +0 -32
  234. package/src/adapters/inbound/cli/commands/progress.ts +0 -55
  235. package/src/adapters/inbound/cli/commands/realign.ts +0 -28
  236. package/src/adapters/inbound/cli/commands/show.ts +0 -96
  237. package/src/app/use_cases/abandon_work.ts +0 -42
  238. package/src/app/use_cases/approve_artifact.ts +0 -197
  239. package/src/app/use_cases/journal_events.ts +0 -64
  240. package/src/app/use_cases/land_change.ts +0 -134
  241. package/src/app/use_cases/new_project_doc.ts +0 -48
  242. package/src/app/use_cases/new_work_item.ts +0 -75
  243. package/src/app/use_cases/realign_done.ts +0 -45
  244. package/src/app/use_cases/realign_status.ts +0 -106
  245. package/src/app/use_cases/record_progress.ts +0 -118
  246. package/src/app/use_cases/show_elements.ts +0 -18
  247. package/src/app/use_cases/show_notes.ts +0 -11
  248. package/src/app/use_cases/show_scope.ts +0 -51
  249. package/src/app/use_cases/upstream_diff.ts +0 -55
  250. package/src/domain/approvals/agent_approval.ts +0 -68
  251. package/src/domain/approvals/evaluate.ts +0 -36
  252. package/src/domain/approvals/fingerprint.ts +0 -52
  253. package/src/domain/approvals/rebase.ts +0 -39
  254. package/src/domain/approvals/records.ts +0 -50
  255. package/src/domain/approvals/upstream_text.ts +0 -36
  256. package/src/domain/architecture_sections.ts +0 -23
  257. package/src/domain/check/decision_comments.ts +0 -38
  258. package/src/domain/decision_citations.ts +0 -62
  259. package/src/domain/edits/agents_file.ts +0 -58
  260. package/src/domain/edits/managed_tables.ts +0 -60
  261. package/src/domain/edits/progress_block.ts +0 -33
  262. package/src/domain/edits/status_lines.ts +0 -52
  263. package/src/domain/elements.ts +0 -126
  264. package/src/domain/journal.ts +0 -109
  265. package/src/domain/land/bookkeeping.ts +0 -54
  266. package/src/domain/land/merge_plan.ts +0 -137
  267. package/src/domain/land/text_ops.ts +0 -66
  268. package/src/domain/line_diff.ts +0 -56
  269. package/src/domain/project/artifact_ref.ts +0 -42
  270. package/src/domain/project/deltas.ts +0 -49
  271. package/src/domain/project/element_lookup.ts +0 -124
  272. package/src/domain/project/living_model.ts +0 -55
  273. package/src/domain/project/new_items.ts +0 -48
  274. package/src/domain/project/notes.ts +0 -74
  275. package/src/domain/project/plan_parts.ts +0 -111
  276. package/src/domain/project/progress.ts +0 -43
  277. package/src/domain/project/project_view.ts +0 -72
  278. package/src/domain/project/roadmap.ts +0 -43
  279. package/src/domain/project/scope_view.ts +0 -134
  280. package/src/domain/project/work_items.ts +0 -68
  281. package/src/domain/status/next_step.ts +0 -181
  282. package/src/domain/status/pending_rethink.ts +0 -22
  283. package/src/domain/status/verification.ts +0 -29
  284. package/src/domain/status/work_status.ts +0 -125
  285. package/src/domain/validation/decision_rules.ts +0 -76
  286. package/src/domain/validation/delta_rules.ts +0 -81
  287. package/src/domain/validation/element_rules.ts +0 -104
  288. package/src/domain/validation/future_rules.ts +0 -40
  289. package/src/domain/validation/living_rules.ts +0 -99
  290. package/src/domain/validation/plan_rules.ts +0 -124
  291. package/src/domain/validation/project_rules.ts +0 -115
  292. package/src/domain/validation/size_rules.ts +0 -45
  293. package/src/domain/validation/vision_rules.ts +0 -41
  294. package/src/domain/validation/work_item_rules.ts +0 -175
  295. package/src/domain/versions/realign_notes.ts +0 -72
  296. package/src/domain/versions/rule_versions.ts +0 -29
  297. package/src/domain/versions/scheduled_exceptions.ts +0 -41
  298. package/templates/init/gitattributes +0 -2
@@ -1,111 +0,0 @@
1
- import { findIds } from "../ids.js";
2
- import { findSection, } from "../markdown.js";
3
- import { tablesIn } from "../markdown_tables.js";
4
-
5
- /** The parts of feature.md and change.md that describe what gets built and how it is proved. */
6
-
7
-
8
-
9
-
10
-
11
-
12
-
13
-
14
-
15
-
16
-
17
-
18
-
19
-
20
-
21
-
22
-
23
-
24
-
25
-
26
-
27
-
28
-
29
-
30
-
31
-
32
-
33
-
34
-
35
-
36
-
37
-
38
-
39
- const TASK = /^- (T\d+): `([^`]+)`[^`]*? in ((?:`[^`]+`(?:, )?)+), proves (.+)$/;
40
-
41
- export function readScope(doc ) {
42
- const section = findSection(doc, "Scope");
43
- if (section === null) return null;
44
- const scope = { delivers: [], builds: [], removes: [], line: section.heading.line };
45
- for (let i = section.start; i < section.end; i++) {
46
- const match = /^- (Delivers|Builds|Removes): (.*)$/.exec(doc.lines[i] ?? "");
47
- if (match?.[1] === undefined || match[2] === undefined) continue;
48
- const key = match[1].toLowerCase() ;
49
- scope[key] = findIds(match[2]);
50
- }
51
- return scope;
52
- }
53
-
54
- /** Task lines, and the lines under `## Tasks` that don't parse as tasks. */
55
- export function readTasks(doc ) {
56
- const section = findSection(doc, "Tasks");
57
- const tasks = [];
58
- const malformed = [];
59
- if (section === null) return { tasks, malformed };
60
- for (let i = section.start; i < section.end; i++) {
61
- const line = doc.lines[i] ?? "";
62
- if (!line.startsWith("- ")) continue;
63
- const match = TASK.exec(line);
64
- if (match?.[1] === undefined || match[2] === undefined || match[3] === undefined || match[4] === undefined) {
65
- malformed.push(i);
66
- continue;
67
- }
68
- const paths = [...match[3].matchAll(/`([^`]+)`/g)].map((m) => m[1] ?? "");
69
- tasks.push({ number: match[1], owner: match[2], paths, proves: findIds(match[4]), line: i });
70
- }
71
- return { tasks, malformed };
72
- }
73
-
74
- /**
75
- * Whether a change has been planned. A standalone change is designed first, with an
76
- * empty test plan and no tasks; plan fills both.
77
- */
78
- export function isPlanned(doc ) {
79
- const { tasks, malformed } = readTasks(doc);
80
- return readTestPlan(doc).length > 0 || tasks.length > 0 || malformed.length > 0;
81
- }
82
-
83
- export function readTestPlan(doc ) {
84
- const section = findSection(doc, "Test plan");
85
- if (section === null) return [];
86
- const table = tablesIn(doc.lines, doc.inFence, section.start, section.end)[0];
87
- return (table?.rows ?? []).map(({ cells, line }) => ({
88
- subject: cells[0] ?? "",
89
- seam: cells[1] ?? "",
90
- file: (cells[2] ?? "").replace(/`/g, ""),
91
- kind: cells[3] ?? "",
92
- line,
93
- }));
94
- }
95
-
96
- export function readChangesTable(doc ) {
97
- const section = findSection(doc, "Changes");
98
- if (section === null) return [];
99
- const table = tablesIn(doc.lines, doc.inFence, section.start, section.end)[0];
100
- if (table === undefined) return [];
101
- const column = (name ) => table.header.findIndex((cell) => cell.toLowerCase() === name);
102
- const cell = (cells , name ) => cells[column(name)] ?? "";
103
- return table.rows.map(({ cells, line }) => ({
104
- change: cells[0] ?? "",
105
- delivers: findIds(cell(cells, "delivers")),
106
- builds: findIds(cell(cells, "builds")),
107
- removes: findIds(cell(cells, "removes")),
108
- dependsOn: cell(cells, "depends on").split(",").map((s) => s.trim()).filter((s) => s !== ""),
109
- line,
110
- }));
111
- }
@@ -1,43 +0,0 @@
1
- import { MANAGED_CLOSE, MANAGED_OPEN, } from "../markdown.js";
2
-
3
- /** The managed `## Progress` block of a feature or change. */
4
-
5
-
6
-
7
-
8
-
9
-
10
-
11
-
12
-
13
-
14
- export function readProgress(doc ) {
15
- const progress = { tasks: new Map(), deviations: [], changes: new Map() };
16
- const range = progressRange(doc);
17
- if (range === null) return progress;
18
- for (let i = range.start; i < range.end; i++) {
19
- const line = doc.lines[i] ?? "";
20
- let match = /^- Branch: (.+)$/.exec(line);
21
- if (match?.[1] !== undefined) progress.branch = match[1].trim();
22
- match = /^- \[([ x])\] (T\d+)(?: \(([0-9a-f]+)\))?/.exec(line);
23
- if (match?.[2] !== undefined) progress.tasks.set(match[2], match[1] === "x" ? (match[3] ?? "") : null);
24
- match = /^- Deviation (.+)$/.exec(line);
25
- if (match?.[1] !== undefined) progress.deviations.push(match[1]);
26
- match = /^- Landed: (.+)$/.exec(line);
27
- if (match?.[1] !== undefined) progress.landed = match[1];
28
- match = /^- (\d\d-[a-z0-9-]+): (.+)$/.exec(line);
29
- if (match?.[1] !== undefined && match[2] !== undefined) progress.changes.set(match[1], match[2]);
30
- }
31
- return progress;
32
- }
33
-
34
- /** Lines [start, end) inside the managed block that holds `## Progress`, or null. */
35
- export function progressRange(doc ) {
36
- const heading = doc.headings.find((h) => h.level === 2 && h.text === "Progress");
37
- if (heading === undefined || !doc.inManaged[heading.line]) return null;
38
- let open = heading.line;
39
- while (open > 0 && doc.lines[open]?.trim() !== MANAGED_OPEN) open--;
40
- let end = heading.line;
41
- while (end < doc.lines.length && doc.lines[end]?.trim() !== MANAGED_CLOSE) end++;
42
- return { open, start: heading.line + 1, end };
43
- }
@@ -1,72 +0,0 @@
1
- import { parseApprovals, } from "../approvals/records.js";
2
- import { parseConfig, } from "../config.js";
3
- import { journalPath, parseJournal, } from "../journal.js";
4
- import { PATHS, } from "./layout.js";
5
- import { buildLivingModel, } from "./living_model.js";
6
- import { readDelta } from "./deltas.js";
7
- import { listWorkItems, } from "./work_items.js";
8
-
9
- /** Everything the rules need, loaded once from a snapshot. */
10
-
11
-
12
-
13
-
14
-
15
-
16
-
17
-
18
-
19
-
20
-
21
-
22
-
23
-
24
-
25
-
26
-
27
- export function buildProjectView(snapshot ) {
28
- const items = listWorkItems(snapshot);
29
- const approvals = new Map ();
30
- const approvalProblems = new Map ();
31
- for (const folder of [".cruze", ...items.map((item) => item.folder)]) {
32
- const path = approvalsPath(folder);
33
- const { file, problem } = parseApprovals(snapshot.get(path));
34
- approvals.set(folder, file);
35
- if (problem !== undefined) approvalProblems.set(path, problem);
36
- }
37
- const retired = new Set(
38
- items
39
- .filter((item) => item.archived)
40
- .flatMap((item) => readDelta(item.doc).entries.filter((e) => e.element.op === "REMOVED").map((e) => e.element.id)),
41
- );
42
- const journal = [];
43
- const journalProblems = new Map ();
44
- for (const folder of [".cruze", ...items.map((item) => item.folder)]) {
45
- const path = journalPath(folder);
46
- const { entries, badLines } = parseJournal(snapshot.get(path));
47
- journal.push(...entries);
48
- if (badLines.length > 0) journalProblems.set(path, badLines);
49
- }
50
- journal.sort((a, b) => a.at.localeCompare(b.at));
51
- const configText = snapshot.get(PATHS.config);
52
- return {
53
- snapshot,
54
- living: buildLivingModel(snapshot),
55
- items,
56
- config: configText === undefined ? null : parseConfig(configText),
57
- approvals,
58
- approvalProblems,
59
- retired,
60
- journal,
61
- journalProblems,
62
- };
63
- }
64
-
65
- export function approvalsPath(folder ) {
66
- return `${folder}/approvals.json`;
67
- }
68
-
69
- /** The folder that holds an artifact's approval: the work item's folder, or `.cruze` for project documents. */
70
- export function approvalFolder(view , artifactPath ) {
71
- return view.items.find((item) => item.path === artifactPath)?.folder ?? ".cruze";
72
- }
@@ -1,43 +0,0 @@
1
- import { readRows } from "../edits/managed_tables.js";
2
- import { parseMarkdown } from "../markdown.js";
3
- import { tablesIn } from "../markdown_tables.js";
4
-
5
- /** The items of `docs/roadmap.md`, as the cruze-formats skill defines them. */
6
-
7
-
8
-
9
-
10
-
11
-
12
-
13
-
14
-
15
-
16
-
17
-
18
-
19
-
20
-
21
-
22
- /** Every item in the roadmap's phases, in the order they appear. */
23
- export function readRoadmapItems(text ) {
24
- const doc = parseMarkdown(text);
25
- const items = [];
26
- for (const phase of doc.headings.filter((h) => h.level === 3 && /^Phase \d+: /.test(h.text))) {
27
- const number = Number(/^Phase (\d+)/.exec(phase.text)?.[1] ?? 0);
28
- const end = doc.headings.find((h) => h.line > phase.line && h.level <= 3)?.line ?? doc.lines.length;
29
- for (const table of tablesIn(doc.lines, doc.inFence, phase.line + 1, end)) {
30
- for (const { cells, line } of table.rows) {
31
- const [slug = "", kind = "", goals = "", blocked = ""] = cells;
32
- const blockedBy = blocked.split(",").map((s) => s.trim()).filter((s) => s !== "");
33
- items.push({ slug, kind, goals, blockedBy, phase: number, line });
34
- }
35
- }
36
- }
37
- return items;
38
- }
39
-
40
- /** The managed `## Status` rows: each started item's folder ID and state. */
41
- export function readRoadmapStatus(text ) {
42
- return readRows(text, "Status").map(([slug = "", id = "", state = ""]) => ({ slug, id, state }));
43
- }
@@ -1,134 +0,0 @@
1
- import { elementLines, } from "../elements.js";
2
- import { findIds } from "../ids.js";
3
- import { findSection, } from "../markdown.js";
4
- import { readDelta } from "./deltas.js";
5
- import { readScope } from "./plan_parts.js";
6
-
7
- import { featureOf, } from "./work_items.js";
8
-
9
- /**
10
- * The design a piece of work builds, cut out of the documents that hold it, so a step reads
11
- * the elements in its scope instead of the whole architecture and the whole feature.
12
- */
13
-
14
-
15
-
16
-
17
-
18
-
19
-
20
-
21
-
22
-
23
-
24
-
25
-
26
-
27
-
28
-
29
-
30
-
31
-
32
-
33
-
34
-
35
-
36
-
37
-
38
-
39
-
40
-
41
-
42
-
43
-
44
-
45
-
46
-
47
-
48
-
49
-
50
-
51
-
52
-
53
- export function scopeView(view , item ) {
54
- const feature = featureOf(view.items, item);
55
- const deltaItem = feature ?? item;
56
- const locate = locator(view, deltaItem);
57
- const wanted = item.kind === "change" || item.kind === "standalone" ? changeIds(item) : featureIds(deltaItem);
58
- const missing = [];
59
- const show = (ids ) =>
60
- ids.flatMap((id) => {
61
- const found = locate(id);
62
- if (found === undefined) {
63
- missing.push(id);
64
- return [];
65
- }
66
- const shown = { id, title: found.element.title, source: `${found.path}:${found.element.start + 1}`, body: elementLines(found.doc, found.element) };
67
- if (found.element.op !== undefined) shown.op = found.element.op;
68
- return [shown];
69
- });
70
- const elements = show(wanted.elements);
71
- const scenarios = withoutRepeatedScenarios(show(wanted.scenarios));
72
- const shownIds = new Set([...elements, ...scenarios].map((e) => e.id));
73
- const cited = [...new Set([...elements, ...scenarios].flatMap((e) => findIds(e.body.join("\n"))))]
74
- .filter((id) => !shownIds.has(id))
75
- .flatMap((id) => {
76
- const found = locate(id);
77
- return found === undefined ? [] : [{ id, title: found.element.title, source: `${found.path}:${found.element.start + 1}` }];
78
- });
79
- const decisions = decisionLines((feature ?? item).doc);
80
- const changeDecisions = feature === undefined ? [] : decisionLines(item.doc);
81
- const result = { ref: item.ref, kind: item.kind, elements, scenarios, cited, decisions, changeDecisions, missing };
82
- if (feature !== undefined) result.feature = feature.ref;
83
- return result;
84
- }
85
-
86
- /**
87
- * A requirement shown beside its own scenarios keeps only its own text: its body would repeat
88
- * each of them, which made up a third of one change's output.
89
- */
90
- function withoutRepeatedScenarios(shown ) {
91
- const ids = new Set(shown.map((e) => e.id));
92
- return shown.map((element) => {
93
- if (!element.id.startsWith("REQ-")) return element;
94
- const first = element.body.findIndex((line, i) => i > 0 && /^#{2,6} /.test(line) && findIds(line).some((id) => ids.has(id)));
95
- return first === -1 ? element : { ...element, body: element.body.slice(0, first) };
96
- });
97
- }
98
-
99
- /** A change shows what its scope builds, removes and delivers. */
100
- function changeIds(change ) {
101
- const scope = readScope(change.doc);
102
- return { elements: [...(scope?.builds ?? []), ...(scope?.removes ?? [])], scenarios: scope?.delivers ?? [] };
103
- }
104
-
105
- /** A feature shows its whole delta: every operation, with a requirement's scenarios inside its body. */
106
- function featureIds(feature ) {
107
- const entries = readDelta(feature.doc).entries;
108
- return {
109
- elements: entries.filter((e) => e.section === "architecture").map((e) => e.element.id),
110
- scenarios: entries.filter((e) => e.section === "spec").map((e) => e.element.id),
111
- };
112
- }
113
-
114
- /** Finds an element's body in the work's own delta first, since that is the text the work builds, then in the living docs. */
115
- function locator(view , deltaItem ) {
116
- const delta = readDelta(deltaItem.doc);
117
- const inDelta = new Map ();
118
- for (const entry of delta.entries) {
119
- inDelta.set(entry.element.id, entry.element);
120
- for (const scenario of entry.scenarios) inDelta.set(scenario.id, scenario);
121
- }
122
- return (id) => {
123
- const own = inDelta.get(id);
124
- if (own !== undefined) return { doc: deltaItem.doc, path: deltaItem.path, element: own };
125
- const living = view.living.elements.get(id);
126
- return living === undefined ? undefined : { doc: living.doc, path: living.path, element: living.element };
127
- };
128
- }
129
-
130
- function decisionLines(doc ) {
131
- const section = findSection(doc, "Settled decisions");
132
- if (section === null) return [];
133
- return doc.lines.slice(section.start, section.end).filter((line) => line.startsWith("- "));
134
- }
@@ -1,68 +0,0 @@
1
- import { parseMarkdown, } from "../markdown.js";
2
- import { PATHS, filesUnder, } from "./layout.js";
3
-
4
-
5
-
6
- /** A feature, a change inside a feature, or a standalone change. */
7
-
8
-
9
-
10
-
11
-
12
-
13
-
14
-
15
-
16
-
17
-
18
-
19
-
20
-
21
- const FEATURE = /^\.cruze\/(features|archive)\/([^/]+)\/feature\.md$/;
22
- const FEATURE_CHANGE = /^\.cruze\/(features|archive)\/([^/]+)\/changes\/([^/]+)\/change\.md$/;
23
- const STANDALONE = /^\.cruze\/(changes|archive)\/([^/]+)\/change\.md$/;
24
-
25
- export function listWorkItems(snapshot ) {
26
- const paths = [...filesUnder(snapshot, PATHS.featuresDir), ...filesUnder(snapshot, PATHS.changesDir), ...filesUnder(snapshot, PATHS.archiveDir)];
27
- return paths.flatMap((path) => {
28
- const item = classify(path);
29
- return item === null ? [] : [{ ...item, path, doc: parseMarkdown(snapshot.get(path) ?? "") }];
30
- });
31
- }
32
-
33
- export function findWorkItem(items , ref ) {
34
- return items.find((item) => item.ref === ref || item.path === ref || item.folder === ref);
35
- }
36
-
37
- /** The feature a change belongs to. */
38
- export function featureOf(items , change ) {
39
- return change.featureRef === undefined ? undefined : items.find((item) => item.kind === "feature" && item.ref === change.featureRef);
40
- }
41
-
42
- export function changesOf(items , feature ) {
43
- return items.filter((item) => item.featureRef === feature.ref).sort((a, b) => a.ref.localeCompare(b.ref));
44
- }
45
-
46
- function classify(path ) {
47
- const folder = path.slice(0, path.lastIndexOf("/"));
48
- let match = FEATURE.exec(path);
49
- if (match?.[2] !== undefined) {
50
- return { kind: "feature", ref: match[2], folderName: match[2], folder, archived: match[1] === "archive" };
51
- }
52
- match = FEATURE_CHANGE.exec(path);
53
- if (match?.[2] !== undefined && match[3] !== undefined) {
54
- return {
55
- kind: "change",
56
- ref: `${match[2]}/${match[3]}`,
57
- folderName: match[3],
58
- folder,
59
- archived: match[1] === "archive",
60
- featureRef: match[2],
61
- };
62
- }
63
- match = STANDALONE.exec(path);
64
- if (match?.[2] !== undefined) {
65
- return { kind: "standalone", ref: match[2], folderName: match[2], folder, archived: match[1] === "archive" };
66
- }
67
- return null;
68
- }
@@ -1,181 +0,0 @@
1
- import { evaluateApproval, } from "../approvals/evaluate.js";
2
- import { rebaseNeeded } from "../approvals/rebase.js";
3
- import { PATHS } from "../project/layout.js";
4
- import { isPlanned, readChangesTable } from "../project/plan_parts.js";
5
- import { readProgress } from "../project/progress.js";
6
-
7
- import { readRoadmapItems, readRoadmapStatus } from "../project/roadmap.js";
8
- import { changesOf, featureOf, } from "../project/work_items.js";
9
- import { compareVersions } from "../versions/cruze_version.js";
10
- import { FIRST_RELEASE } from "../versions/rule_versions.js";
11
- import { pendingReason, pendingRethinks } from "./pending_rethink.js";
12
- import { awaitsRethink, isVerified } from "./verification.js";
13
- import { activeChange } from "./work_status.js";
14
-
15
- /** The Cruze step to run next, computed from the project's state and the current branch. */
16
-
17
-
18
-
19
-
20
-
21
-
22
-
23
-
24
-
25
-
26
-
27
-
28
-
29
-
30
-
31
-
32
- export function nextSteps(view , branch ) {
33
- const project = projectStep(view);
34
- if (project !== null) return { next: project, also: [] };
35
- const active = activeChange(view, branch);
36
- const inFlight = inRoadmapOrder(view, view.items.filter((item) => !item.archived && item.kind !== "change")).flatMap((item) => itemSteps(view, item, branch));
37
- const roadmap = roadmapStep(view);
38
- const realign = realignStep(view);
39
- const candidates = [
40
- ...(active === undefined ? [] : [changeStep(view, active, branch)]),
41
- ...inFlight,
42
- ...(roadmap === null ? [] : [roadmap]),
43
- ...(realign === null ? [] : [realign]),
44
- ];
45
- const unique = candidates.filter((c, i) => candidates.findIndex((o) => o.step === c.step && o.target === c.target) === i);
46
- const [next, ...also] = unique;
47
- return { next: next ?? { step: "roadmap", reason: "every item on the roadmap has landed; close the release and plan the next" }, also };
48
- }
49
-
50
- /**
51
- * Work in flight in the roadmap's order, with items whose roadmap blockers haven't all landed
52
- * last: a feature designed early still waits for the prefactors the user put before it.
53
- */
54
- function inRoadmapOrder(view , items ) {
55
- const text = view.snapshot.get(PATHS.roadmap);
56
- if (text === undefined) return items;
57
- const rows = readRoadmapStatus(text);
58
- const state = new Map(rows.map((row) => [row.slug, row.state]));
59
- const slugOf = new Map(rows.map((row) => [row.id, row.slug]));
60
- const roadmap = readRoadmapItems(text);
61
- const rank = (item ) => {
62
- const at = roadmap.findIndex((r) => r.slug === slugOf.get(item.ref));
63
- const blocked = at !== -1 && (roadmap[at]?.blockedBy ?? []).some((b) => state.get(b) !== "landed");
64
- return [blocked ? 1 : 0, at === -1 ? roadmap.length : at];
65
- };
66
- return items
67
- .map((item, i) => ({ item, key: [...rank(item), i] }))
68
- .sort((a, b) => (a.key[0] ?? 0) - (b.key[0] ?? 0) || (a.key[1] ?? 0) - (b.key[1] ?? 0) || (a.key[2] ?? 0) - (b.key[2] ?? 0))
69
- .map(({ item }) => item);
70
- }
71
-
72
- /** Realign comes when nothing else is due: the installed skills expect more than the code meets. */
73
- function realignStep(view ) {
74
- const config = view.config?.config;
75
- if (config?.cruze === undefined) return null;
76
- const standards = config.standards ?? FIRST_RELEASE;
77
- if (compareVersions(standards, config.cruze) >= 0) return null;
78
- return { step: "realign", target: config.cruze, reason: `the code meets Cruze ${standards}'s expectations, and the installed skills are ${config.cruze}` };
79
- }
80
-
81
- /** The project documents come first: nothing else can proceed without them. */
82
- function projectStep(view ) {
83
- const docs = [
84
- {
85
- path: PATHS.vision,
86
- missing: { step: "envision", reason: "the project has no vision yet" },
87
- unapproved: { step: "envision", reason: "finish the vision and approve it" },
88
- changed: { step: "envision", reason: "the vision changed since its approval; review the change and approve it again" },
89
- },
90
- {
91
- path: PATHS.architecture,
92
- missing: { step: "architect", reason: "the project has no architecture yet" },
93
- unapproved: { step: "architect", reason: "finish the architecture, review it and approve it" },
94
- changed: { step: "rethink", target: "architecture", reason: "the architecture changed since its approval" },
95
- },
96
- {
97
- path: PATHS.roadmap,
98
- missing: { step: "architect", reason: "the project has no roadmap yet" },
99
- unapproved: { step: "roadmap", reason: "approve the release ordering" },
100
- changed: { step: "roadmap", reason: "the roadmap changed since its approval; approve the new ordering" },
101
- },
102
- ];
103
- for (const doc of docs) {
104
- if (!view.snapshot.has(doc.path)) return doc.missing;
105
- const state = evaluateApproval(view, doc.path).state;
106
- if (state === "unapproved") return doc.unapproved;
107
- if (state !== "approved") return doc.changed;
108
- }
109
- return null;
110
- }
111
-
112
- function itemSteps(view , item , branch ) {
113
- if (item.kind === "standalone") return [changeStep(view, item, branch)];
114
- const state = evaluateApproval(view, item.path);
115
- if (readChangesTable(item.doc).length === 0) return [{ step: "architect", target: item.ref, reason: "the feature has no design or changes yet" }];
116
- if (state.state === "unapproved") return [{ step: "architect", target: item.ref, reason: "review the feature's design and approve it" }];
117
- if (state.state !== "approved") return [{ step: "rethink", target: item.ref, reason: staleReason(view, item, "the feature", state) }];
118
- const pending = pendingRethinks(view.journal, item);
119
- if (pending.length > 0) return [{ step: "rethink", target: item.ref, reason: pendingReason(item, pending) }];
120
- const changes = changesOf(view.items, item);
121
- const landed = new Set(changes.filter((c) => readProgress(c.doc).landed !== undefined).map((c) => c.folderName));
122
- const steps = [];
123
- for (const row of readChangesTable(item.doc)) {
124
- const change = changes.find((c) => c.folderName === row.change);
125
- if (change !== undefined) {
126
- if (!landed.has(row.change)) steps.push(changeStep(view, change, branch));
127
- } else if (row.dependsOn.every((d) => landed.has(d))) {
128
- steps.push({ step: "plan", target: `${item.ref}/${row.change}`, reason: "the next change of an approved feature, with its dependencies landed" });
129
- }
130
- }
131
- return steps;
132
- }
133
-
134
- function changeStep(view , change , branch ) {
135
- const feature = featureOf(view.items, change);
136
- const featureState = feature === undefined ? undefined : evaluateApproval(view, feature.path);
137
- if (feature !== undefined && featureState !== undefined && featureState.state !== "approved") {
138
- return { step: "rethink", target: feature.ref, reason: staleReason(view, feature, `the feature of ${change.ref}`, featureState) };
139
- }
140
- for (const part of [feature, change]) {
141
- const pending = part === undefined ? [] : pendingRethinks(view.journal, part);
142
- if (part !== undefined && pending.length > 0) return { step: "rethink", target: part.ref, reason: pendingReason(part, pending) };
143
- }
144
- const bound = readProgress(change.doc).branch;
145
- const state = evaluateApproval(view, change.path);
146
- if (!isPlanned(change.doc)) return { step: "plan", target: change.ref, reason: "the change is designed but has no test plan or tasks yet" };
147
- if (state.state === "unapproved") return { step: "plan", target: change.ref, reason: "review the plan and approve it on the branch that will build it" };
148
- if (state.state !== "approved") return { step: "rethink", target: change.ref, reason: staleReason(view, change, "the change", state) };
149
- if (awaitsRethink(view.journal, change, feature)) return { step: "rethink", target: feature?.ref ?? change.ref, reason: `verify sent ${change.ref} back for a design change` };
150
- if (bound !== undefined && bound !== branch) return { step: "switch-branch", target: bound, reason: `${change.ref} is built on branch ${bound}` };
151
- const progress = readProgress(change.doc);
152
- if ([...progress.tasks.values()].some((commit) => commit === null)) return { step: "build", target: change.ref, reason: "the change has open tasks" };
153
- if (!isVerified(view.journal, change)) return { step: "verify", target: change.ref, reason: "every task is done; verify the change" };
154
- return { step: "land", target: change.ref, reason: "the change is verified and accepted" };
155
- }
156
-
157
- /**
158
- * Why work went stale, and what the rethink will take: a rebase when its delta still changes an
159
- * element whose living text moved, or only a check of the diff and a re-stamp when nothing it cites
160
- * asks anything of its own text. One project expected a re-stamp and met rebase-required.
161
- */
162
- function staleReason(view , item , label , state ) {
163
- if (state.state === "edited") return `${label} changed since its approval`;
164
- const changed = state.changed.join(", ");
165
- const rebase = rebaseNeeded(view, item);
166
- const then = rebase.length > 0
167
- ? `its delta changes ${rebase.join(", ")}, so rebase it, as cruze diff ${item.ref} shows`
168
- : `read cruze diff ${item.ref}, and re-stamp it when the work still holds`;
169
- return `something ${label} cites changed since its approval (${changed}); ${then}`;
170
- }
171
-
172
- /** The first roadmap item that hasn't started and whose blockers have all landed. */
173
- function roadmapStep(view ) {
174
- const text = view.snapshot.get(PATHS.roadmap);
175
- if (text === undefined) return null;
176
- const status = new Map(readRoadmapStatus(text).map((row) => [row.slug, row.state]));
177
- const item = readRoadmapItems(text).find((i) => !status.has(i.slug) && i.blockedBy.every((b) => status.get(b) === "landed"));
178
- if (item === undefined) return null;
179
- if (item.kind === "feature") return { step: "envision", target: item.slug, reason: "the next roadmap feature; skip to architect when its requirements are settled" };
180
- return { step: "architect", target: item.slug, reason: "the next roadmap change; design it as a tweak" };
181
- }