@turri-dev/cli 0.1.4-next.afc43a8 → 0.1.4-next.d6e59a8

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 (84) hide show
  1. package/.pi/themes/turri-dark.json +67 -66
  2. package/.pi/themes/turri-light.json +71 -73
  3. package/README.md +41 -127
  4. package/assets/jellyfish/dark-00.png +0 -0
  5. package/assets/jellyfish/dark-01.png +0 -0
  6. package/assets/jellyfish/dark-02.png +0 -0
  7. package/assets/jellyfish/dark-03.png +0 -0
  8. package/assets/jellyfish/dark-04.png +0 -0
  9. package/assets/jellyfish/dark-05.png +0 -0
  10. package/assets/jellyfish/dark-06.png +0 -0
  11. package/assets/jellyfish/dark-07.png +0 -0
  12. package/assets/jellyfish/dark-08.png +0 -0
  13. package/assets/jellyfish/dark-09.png +0 -0
  14. package/assets/jellyfish/dark-10.png +0 -0
  15. package/assets/jellyfish/dark-11.png +0 -0
  16. package/assets/jellyfish/light-00.png +0 -0
  17. package/assets/jellyfish/light-01.png +0 -0
  18. package/assets/jellyfish/light-02.png +0 -0
  19. package/assets/jellyfish/light-03.png +0 -0
  20. package/assets/jellyfish/light-04.png +0 -0
  21. package/assets/jellyfish/light-05.png +0 -0
  22. package/assets/jellyfish/light-06.png +0 -0
  23. package/assets/jellyfish/light-07.png +0 -0
  24. package/assets/jellyfish/light-08.png +0 -0
  25. package/assets/jellyfish/light-09.png +0 -0
  26. package/assets/jellyfish/light-10.png +0 -0
  27. package/assets/jellyfish/light-11.png +0 -0
  28. package/dist/brand.js +7 -7
  29. package/dist/cultivate-cli.js +16 -23
  30. package/dist/extension.js +33 -59
  31. package/dist/jellyfish.js +130 -0
  32. package/dist/loop-cli.js +8 -8
  33. package/dist/module-report.js +5 -4
  34. package/dist/regenerate/plain-palette.js +52 -37
  35. package/dist/regenerate/side-panel.js +1 -1
  36. package/dist/regenerate/spec-map-view.js +9 -5
  37. package/dist/release-cli.js +3 -8
  38. package/dist/validate-cli.js +3 -8
  39. package/docs/README.md +5 -12
  40. package/docs/authoring-plain.md +37 -121
  41. package/docs/config.md +50 -176
  42. package/docs/deploy.md +21 -80
  43. package/docs/development.md +13 -41
  44. package/docs/headless.md +30 -136
  45. package/docs/images/how-turri-works.svg +29 -31
  46. package/docs/images/turri-banner.png +0 -0
  47. package/docs/images/turri-screenshot.png +0 -0
  48. package/docs/installation.md +13 -40
  49. package/docs/parser.md +15 -43
  50. package/docs/regenerating.md +231 -964
  51. package/docs/sandbox.md +38 -152
  52. package/docs/skills-and-gates.md +50 -173
  53. package/docs/theming.md +45 -46
  54. package/docs/using.md +66 -356
  55. package/genome/rules/bullet-continuation.md +5 -13
  56. package/genome/rules/concise-specs-examples.md +2 -4
  57. package/genome/rules/concise-specs-integration-examples.md +2 -6
  58. package/genome/rules/concise-specs.md +1 -2
  59. package/genome/rules/integration-embedded-testing.md +1 -4
  60. package/genome/rules/integrations.md +1 -2
  61. package/genome/rules/project-model.md +14 -37
  62. package/genome/rules/stdlib.md +14 -49
  63. package/genome/rules/turri-regeneration.md +35 -23
  64. package/genome/skills/add-implementation-requirement/SKILL.md +1 -2
  65. package/genome/skills/analyze-if-func-spec-too-complex/SKILL.md +3 -8
  66. package/genome/skills/brood/SKILL.md +37 -127
  67. package/genome/skills/brood/references/intent.md +4 -12
  68. package/genome/skills/brood/references/product.md +24 -76
  69. package/genome/skills/brood/references/stack.md +7 -29
  70. package/genome/skills/brood/references/testing.md +11 -42
  71. package/genome/skills/bud/SKILL.md +32 -109
  72. package/genome/skills/bud/references/intent.md +4 -11
  73. package/genome/skills/implement-conformance-testing-script/SKILL.md +3 -6
  74. package/genome/skills/implement-unit-testing-script/SKILL.md +1 -2
  75. package/genome/skills/mature/SKILL.md +21 -69
  76. package/genome/skills/spawn/SKILL.md +28 -113
  77. package/package.json +1 -1
  78. package/assets/jellyfish/dark-12.png +0 -0
  79. package/assets/jellyfish/dark-13.png +0 -0
  80. package/assets/jellyfish/dark-14.png +0 -0
  81. package/assets/jellyfish/fallback.txt +0 -252
  82. package/assets/jellyfish/light-12.png +0 -0
  83. package/assets/jellyfish/light-13.png +0 -0
  84. package/assets/jellyfish/light-14.png +0 -0
@@ -1,45 +1,53 @@
1
- /** On a dark surface - turri-dark's #0f0f0f, pi's dark ones. Every colour at least 7:1 on it, and 4.5:1 on the fills. */
1
+ /**
2
+ * On a dark surface - turri-dark's abyss, #0a0f1a, pi's dark ones - in the design system's colours alone: a section's
3
+ * header in pink, a concept in neon, text in quotes or backticks in siesta tan. Every colour at least 7:1 on abyss and 4.8:1 on the blue whale fill
4
+ * of an added line; on the eggplant of a removed one, the design system's only fill of that warmth, its pinks and
5
+ * grauzone drop to 3.7:1.
6
+ */
2
7
  const DARK = {
3
8
  roles: {
4
- section: { colour: "#fcd34d", bold: true },
5
- concept: { colour: "#7dd3fc" },
6
- link: { colour: "#c4b5fd", underline: true },
7
- literal: { colour: "#fdba74" },
8
- keyword: { colour: "#f9a8d4", bold: true },
9
- module: { colour: "#5eead4" },
10
- comment: { colour: "#b4bfcf", italic: true },
11
- bullet: { colour: "#b4bfcf" },
9
+ section: { colour: "#ff7fa6", bold: true },
10
+ concept: { colour: "#5ce1e6" },
11
+ link: { colour: "#f4f7f9", underline: true },
12
+ literal: { colour: "#e9d8c8" },
13
+ keyword: { colour: "#f4f7f9", bold: true },
14
+ module: { colour: "#f4f7f9" },
15
+ comment: { colour: "#85a3b2", italic: true },
16
+ bullet: { colour: "#85a3b2" },
12
17
  },
13
- added: "#86efac",
14
- removed: "#fca5a5",
15
- addedFill: "#1f4d2e",
16
- removedFill: "#5c2323",
17
- muted: "#808080",
18
+ added: "#5ce1e6",
19
+ removed: "#ff7fa6",
20
+ addedFill: "#1e3442",
21
+ removedFill: "#732553",
22
+ muted: "#85a3b2",
18
23
  };
19
- /** On a light surface - turri-light's white, pi's light ones. Every colour at least 7:1 on it, and 5:1 on the fills. */
24
+ /**
25
+ * On a light surface - turri-light's white, pi's light ones - in the design system's deep colours, the only ones it has
26
+ * that read on white. Every colour at least 7:1 on white, and 5:1 on the neon hover and siesta tan fills.
27
+ */
20
28
  const LIGHT = {
21
29
  roles: {
22
- section: { colour: "#0a1fd4", bold: true },
23
- concept: { colour: "#155e75" },
24
- link: { colour: "#6d28d9", underline: true },
25
- literal: { colour: "#92400e" },
26
- keyword: { colour: "#9d174d", bold: true },
27
- module: { colour: "#065f46" },
28
- comment: { colour: "#475569", italic: true },
29
- bullet: { colour: "#475569" },
30
+ section: { colour: "#3e5a6b", bold: true },
31
+ concept: { colour: "#732553" },
32
+ link: { colour: "#1e3442", underline: true },
33
+ literal: { colour: "#1e3442" },
34
+ keyword: { colour: "#0a0f1a", bold: true },
35
+ module: { colour: "#1e3442" },
36
+ comment: { colour: "#3e5a6b", italic: true },
37
+ bullet: { colour: "#3e5a6b" },
30
38
  },
31
- added: "#166534",
32
- removed: "#991b1b",
33
- addedFill: "#d3f5dc",
34
- removedFill: "#fcd9d9",
35
- muted: "#6b7280",
39
+ added: "#1e3442",
40
+ removed: "#732553",
41
+ addedFill: "#8eebee",
42
+ removedFill: "#e9d8c8",
43
+ muted: "#3e5a6b",
36
44
  };
37
45
  const rgb = (hex) => {
38
46
  const value = Number.parseInt(hex.slice(1), 16);
39
47
  return [(value >> 16) & 255, (value >> 8) & 255, value & 255];
40
48
  };
41
49
  /** A colour's SGR parameters: 24-bit, or the nearest of the 256 where the terminal has no more. */
42
- function sgr(hex, background, truecolor) {
50
+ export function sgr(hex, background, truecolor) {
43
51
  const [r, g, b] = rgb(hex);
44
52
  if (truecolor)
45
53
  return `${background ? 48 : 38};2;${r};${g};${b}`;
@@ -57,17 +65,24 @@ const fillWith = (hex, text, truecolor) => {
57
65
  return `${on}${text.replace(/\x1b\[(?:0|49)?m/g, (reset) => `${reset}${on}`)}\x1b[49m`;
58
66
  };
59
67
  /**
60
- * Brand blue, for what is shown to do what its spec says - regen's ◆. Brand blue itself on a light surface, 9.72:1
61
- * on white; on a dark one, where it is illegible (1.97:1 on brand black - see docs/theming.md), its tint 55% of the way
62
- * to white, 7.34:1 on brand black.
68
+ * Neon, for what is shown to do what its spec says - regen's ◆: the design system's colour for what can be followed,
69
+ * 12.2:1 on abyss. On a light surface, where neon is illegible (1.6:1 on white), blue whale, 12.9:1 on white.
63
70
  */
64
- export const BRAND_BLUE = { light: "#0a1fd4", dark: "#919aec" };
65
- /** Text in brand blue on a theme - its tint on a dark one - in as many colours as the terminal has. */
66
- export function brandBlue(theme) {
67
- const colour = theme.appearance === "light" ? BRAND_BLUE.light : BRAND_BLUE.dark;
71
+ export const BRAND_NEON = { light: "#1e3442", dark: "#5ce1e6" };
72
+ /**
73
+ * Pink, the one pink thing in a view - the banner's wordmark: 6.54:1 on abyss. On a light surface, where pink is
74
+ * too pale (2.9:1 on white), eggplant, 9.9:1 on white.
75
+ */
76
+ export const BRAND_PINK = { light: "#732553", dark: "#ff5c8d" };
77
+ const brandInk = (colours) => (theme) => {
78
+ const colour = theme.appearance === "light" ? colours.light : colours.dark;
68
79
  const truecolor = (theme.getColorMode?.() ?? "truecolor") === "truecolor";
69
80
  return (text) => paintWith({ colour, bold: true }, text, truecolor);
70
- }
81
+ };
82
+ /** Text in neon on a theme - blue whale on a light one - in as many colours as the terminal has. */
83
+ export const brandNeon = brandInk(BRAND_NEON);
84
+ /** Text in pink on a theme - eggplant on a light one - in as many colours as the terminal has. */
85
+ export const brandPink = brandInk(BRAND_PINK);
71
86
  /** How a spec diff is painted on a theme: its palette by whether the theme is dark or light, in as many colours as the terminal has. */
72
87
  export function plainPaint(theme) {
73
88
  const palette = theme.appearance === "light" ? LIGHT : DARK;
@@ -16,7 +16,7 @@ export function createSidePanel(options) {
16
16
  const backKeys = () => {
17
17
  if (options.dock === undefined)
18
18
  return [`esc or ${options.key} back to the chat`];
19
- return options.dock.docked() ? ["s full screen", "esc or ⌥⌘← to the chat", `${options.key} close`] : ["s beside the chat", `esc or ${options.key} back to the chat`];
19
+ return options.dock.docked() ? [`${options.key} or esc to the chat`, "ctrl+c close", "s full screen"] : ["s beside the chat", `esc or ${options.key} back to the chat`];
20
20
  };
21
21
  const waitingNote = () =>
22
22
  // A question asked while it showed waits in the chat, unanswered: the person is told it is there.
@@ -52,7 +52,7 @@ export function tabBar(pages, shown, width, style, now) {
52
52
  const text = `${mark}${tabName(page)}`;
53
53
  if (page === shown)
54
54
  return style.heading(`[${text}]`);
55
- return regenerating ? style.accent(text) : page.stuck ? style.error(text) : style.muted(text);
55
+ return regenerating ? style.accent(text) : page.stuck ? needs(style)(text) : style.muted(text);
56
56
  };
57
57
  const tabs = pages.map(label);
58
58
  const at = Math.max(0, shown === undefined ? 0 : pages.indexOf(shown));
@@ -69,6 +69,8 @@ export function tabBar(pages, shown, width, style, now) {
69
69
  }
70
70
  return truncateToWidth(lineOf(from, to), width, "…");
71
71
  }
72
+ /** What needs the person - a stuck page, its `!` - in the warning colour: it waits on them, nothing is broken. */
73
+ const needs = (style) => style.warning ?? style.error;
72
74
  /** Strike text through. */
73
75
  const strike = (text) => `\x1b[9m${text}\x1b[29m`;
74
76
  /** How far a group, and what is in it, is indented under its module. */
@@ -121,7 +123,7 @@ function rungGlyph(rung, style, now) {
121
123
  if (rung === "conform")
122
124
  return green(LADDER.conform);
123
125
  if (rung === "needsYou")
124
- return style.error(LADDER.needsYou);
126
+ return needs(style)(LADDER.needsYou);
125
127
  if (rung === "takenOut")
126
128
  return style.error(LADDER.takenOut);
127
129
  if (rung === "toRegenerate")
@@ -486,7 +488,7 @@ export function specMapLines(pages, state, width, style, _jobs = [], now = Date.
486
488
  // The first item that needs the person says what for, and how to go about it - opened or closed.
487
489
  if (page.stuck && !toldWhy && rungOf(item) === "needsYou") {
488
490
  toldWhy = true;
489
- for (const line of wrapped(needsLine(page), width, style.error))
491
+ for (const line of wrapped(needsLine(page), width, needs(style)))
490
492
  add(line, mark);
491
493
  }
492
494
  if (state.open.has(row.key)) {
@@ -515,7 +517,9 @@ function paintOf(mark, style) {
515
517
  return style.success ?? style.added;
516
518
  if (mark === "regenerating" || mark === "planning")
517
519
  return style.accent;
518
- if (mark === "stuck" || mark === "removed")
520
+ if (mark === "stuck")
521
+ return needs(style);
522
+ if (mark === "removed")
519
523
  return style.error;
520
524
  return style.muted;
521
525
  }
@@ -548,7 +552,7 @@ export function specMapHeader(page, width, style, waiting = "") {
548
552
  const strip = progressStrip(page, style);
549
553
  const count = total > 0 ? `${built} of ${total} ${total === 1 ? "functionality" : "functionalities"} built · ` : "";
550
554
  // Short, never cut off: what for, and how, is under the first `!`.
551
- const status = page.stuck ? style.error(`${LADDER.needsYou} needs you`) : page.status;
555
+ const status = page.stuck ? needs(style)(`${LADDER.needsYou} needs you`) : page.status;
552
556
  // The module's file heads its column, under this line.
553
557
  return truncateToWidth(`${style.heading("regen")}${strip !== "" ? ` ${strip}` : ""} ${count}${status}${waiting}`, width);
554
558
  }
@@ -4,6 +4,7 @@ import { configForSpec, modelSpec, projectModels, readProjectConfig } from "./co
4
4
  import { createDeployState } from "./release-tools.js";
5
5
  import { runReleaseAgent } from "./release-agent.js";
6
6
  import { listDeployed, runningDeployed, stopDeployed } from "./regenerate/release.js";
7
+ import { c } from "./module-report.js";
7
8
  const USAGE = `usage: turri release [file.plain] [options]
8
9
 
9
10
  Releases the module on top of the spec's requires chain, which holds the code of the ones below, and
@@ -20,12 +21,6 @@ harness-model (the agent's model).
20
21
  --in-module <module> the codebase to take, when modules built on this one branch
21
22
  --guidance <text> how this project releases it: the scripts to run, in order
22
23
  --model <provider/id> the agent's model (default: harness-model)`;
23
- const c = {
24
- dim: (s) => `\x1b[2m${s}\x1b[0m`,
25
- bold: (s) => `\x1b[1m${s}\x1b[0m`,
26
- green: (s) => `\x1b[32m${s}\x1b[0m`,
27
- cyan: (s) => `\x1b[36m${s}\x1b[0m`,
28
- };
29
24
  const PROBLEMS = "If you are having problems, use chat mode to deploy: run turri and ask to try it.";
30
25
  function parseArgs(argv) {
31
26
  const positional = [];
@@ -76,7 +71,7 @@ function followLogs() {
76
71
  closeSync(fd);
77
72
  offsets.set(item.log, size);
78
73
  for (const line of buffer.toString("utf8").split("\n").filter((l) => l !== ""))
79
- console.log(`${c.cyan(`[${item.name}]`)} ${line}`);
74
+ console.log(`${c.accent(`[${item.name}]`)} ${line}`);
80
75
  }
81
76
  if (running.length === 0) {
82
77
  clearInterval(timer);
@@ -119,7 +114,7 @@ async function main() {
119
114
  console.log(`\n\n${c.dim(PROBLEMS)}`);
120
115
  if (runningDeployed().length === 0)
121
116
  process.exit(0);
122
- console.log(c.green(c.bold("\nRunning - Ctrl-C stops it.")) + c.dim(" Its output follows.\n"));
117
+ console.log(c.success(c.bold("\nRunning - Ctrl-C stops it.")) + c.dim(" Its output follows.\n"));
123
118
  await followLogs();
124
119
  process.exit(0);
125
120
  }
@@ -1,4 +1,5 @@
1
1
  import { checkPlainTargets, formatCheckResults } from "./plain/check.js";
2
+ import { c } from "./module-report.js";
2
3
  const USAGE = `usage: turri validate <file.plain | directory> [options]
3
4
 
4
5
  Validates a ***plain specification statically: parses it, resolves its imports and
@@ -8,12 +9,6 @@ required modules, and reports every error without rendering anything.
8
9
  --json machine-readable output
9
10
 
10
11
  PLAIN_TEMPLATE_DIRS colon-separated template directories`;
11
- const c = {
12
- dim: (s) => `\x1b[2m${s}\x1b[0m`,
13
- bold: (s) => `\x1b[1m${s}\x1b[0m`,
14
- green: (s) => `\x1b[32m${s}\x1b[0m`,
15
- red: (s) => `\x1b[31m${s}\x1b[0m`,
16
- };
17
12
  const argv = process.argv.slice(2);
18
13
  const templateDirs = [];
19
14
  const positional = [];
@@ -43,10 +38,10 @@ if (json) {
43
38
  else {
44
39
  const output = formatCheckResults(results)
45
40
  .split("\n")
46
- .map((line) => line.startsWith("PASS") ? c.green(line) : line.startsWith("FAIL") ? c.red(line) : c.dim(line))
41
+ .map((line) => line.startsWith("PASS") ? c.success(line) : line.startsWith("FAIL") ? c.error(line) : c.dim(line))
47
42
  .join("\n");
48
43
  console.log(output);
49
44
  const failed = results.filter((r) => !r.ok).length;
50
- console.log(`\n${failed === 0 ? c.green(c.bold("all specs valid")) : c.red(c.bold(`${failed} of ${results.length} failed`))}`);
45
+ console.log(`\n${failed === 0 ? c.success(c.bold("all specs valid")) : c.error(c.bold(`${failed} of ${results.length} failed`))}`);
51
46
  }
52
47
  process.exit(results.every((r) => r.ok) ? 0 : 1);
package/docs/README.md CHANGED
@@ -1,20 +1,13 @@
1
1
  # turri documentation
2
2
 
3
- turri implements regenerative software: the \*\*\*plain specs are the durable definition of
4
- what a system does, and the code is generated from them, validated against them, and regenerated
5
- as they change. The [project README](../README.md) says why, and how to install and start; these
6
- pages go into each part.
3
+ turri implements regenerative software: the \*\*\*plain specs are the durable definition of what a system does, and the code is generated from them, validated against them, and regenerated as they change. The [project README](../README.md) says why, and how to install and start; these pages go into each part.
7
4
 
8
5
  ## Start here
9
6
 
10
- 1. [Installation](installation.md) - get a machine ready: `turri doctor`, and what turri needs of
11
- this machine.
12
- 2. [Using turri](using.md) - the conversation, the skills and tools, background jobs and
13
- their logs, choosing the model that renders, the command line.
14
- 3. [Authoring \*\*\*plain](authoring-plain.md) - writing the specs: what a spec is made of, the
15
- authoring rules, the skills that write and fix specs.
16
- 4. [Rendering](regenerating.md) - turning the specs into code: what each build does, in which order,
17
- and what has to pass before it moves on.
7
+ 1. [Installation](installation.md) - get a machine ready: `turri doctor`, and what turri needs of this machine.
8
+ 2. [Using turri](using.md) - the conversation, the skills and tools, background jobs and their logs, choosing the model that renders, the command line.
9
+ 3. [Authoring \*\*\*plain](authoring-plain.md) - writing the specs: what a spec is made of, the authoring rules, the skills that write and fix specs.
10
+ 4. [Rendering](regenerating.md) - turning the specs into code: what each build does, in which order, and what has to pass before it moves on.
18
11
 
19
12
  ## By question
20
13
 
@@ -1,14 +1,8 @@
1
1
  # Authoring \*\*\*plain
2
2
 
3
- Rendering turns a spec into code; authoring is the half that decides what gets built. The code is
4
- only ever as good as the spec - a fact in the wrong section is silently ignored, a functional spec
5
- that is too big comes out half-built, two specs that contradict each other break each other's
6
- tests. So this repo carries as much for writing specs as for rendering them: the authoring rules,
7
- a library of skills that apply them, and independent review of what they write.
3
+ Rendering turns a spec into code; authoring is the half that decides what gets built. The code is only ever as good as the spec - a fact in the wrong section is silently ignored, a functional spec that is too big comes out half-built, two specs that contradict each other break each other's tests. So this repo carries as much for writing specs as for rendering them: the authoring rules, a library of skills that apply them, and independent review of what they write.
8
4
 
9
- The rule underneath all of it: **the specs are the source of truth**. The generated code in
10
- `plain_modules/` is never edited by hand. A bug is fixed in the spec, and a change that is wanted
11
- goes into the spec - that is what persists and what the next render builds from.
5
+ The rule underneath all of it: **the specs are the source of truth**. The generated code in `plain_modules/` is never edited by hand. A bug is fixed in the spec, and a change that is wanted goes into the spec - that is what persists and what the next render builds from.
12
6
 
13
7
  ## A \*\*\*plain project
14
8
 
@@ -33,8 +27,7 @@ A `.plain` file is one module: optional YAML frontmatter, then up to four sectio
33
27
  | `***test reqs***` | everything about the black-box conformance tests: framework, how they run, mocking | `- :ConformanceTests: use pytest and call the CLI as a subprocess.` |
34
28
  | `***functional specs***` | WHAT it does - observable, language-agnostic behaviour, one functionality per item, in the order they are built | `- A :User: can add a :Task:.` |
35
29
 
36
- Under a functional spec, a nested `***acceptance tests***` list states how that one
37
- functionality is checked end to end.
30
+ Under a functional spec, a nested `***acceptance tests***` list states how that one functionality is checked end to end.
38
31
 
39
32
  The frontmatter connects modules:
40
33
 
@@ -46,20 +39,13 @@ The frontmatter connects modules:
46
39
  | `exported_concepts` | which concepts a module that `requires` this one may use |
47
40
  | `required_concepts` | concepts an import module expects the importing module to define |
48
41
 
49
- Beyond that: a spec links to files under `resources/` with markdown link syntax, and the file's
50
- contents go to the renderer with the spec; lines starting with `>` are comments for people; and
51
- files are processed with Liquid first, so a template can be included with parameters.
42
+ Beyond that: a spec links to files under `resources/` with markdown link syntax, and the file's contents go to the renderer with the spec; lines starting with `>` are comments for people; and files are processed with Liquid first, so a template can be included with parameters.
52
43
 
53
- **Section ownership is the rule that bites.** The renderer reads each kind of fact only from the
54
- section that owns it. A tech choice written into a functional spec, or a behaviour written into
55
- the implementation reqs, is not flagged - it is simply not read.
44
+ **Section ownership is the rule that bites.** The renderer reads each kind of fact only from the section that owns it. A tech choice written into a functional spec, or a behaviour written into the implementation reqs, is not flagged - it is simply not read.
56
45
 
57
46
  ## The standard library
58
47
 
59
- turri ships a standard library of import modules in `stdlib/` that settle everything about a
60
- generic piece of software on one stack except what it does: its language and framework, layout,
61
- configuration, error handling, logging, shutdown, and how it is unit and conformance tested. A spec
62
- imports one and writes only its own concepts and functional specs:
48
+ turri ships a standard library of import modules in `stdlib/` that settle everything about a generic piece of software on one stack except what it does: its language and framework, layout, configuration, error handling, logging, shutdown, and how it is unit and conformance tested. A spec imports one and writes only its own concepts and functional specs:
63
49
 
64
50
  ```plain
65
51
  ---
@@ -82,13 +68,7 @@ import:
82
68
 
83
69
  It is built in three layers, and a stack imports the other two:
84
70
 
85
- - **The language** - `stdlib/lang/python`, `typescript`, `java`, `go`, `rust`: the version, the
86
- dependency manager, formatting, linting and type checking (ruff and mypy for Python; Prettier, ESLint
87
- and `tsc --noEmit` for TypeScript), how a failure is reported (an exception, a returned error, a
88
- `Result`), the logging library, and the unit test framework and layout. A module on a stack the library does not have can import its language alone.
89
- The formatter, linter and type checker a language names are enforced by the testing scripts, which
90
- run them with the tests - the unit testing script on the code, the conformance testing script on the
91
- tests - and count them as the tests (see [the static checks](regenerating.md#the-static-checks)).
71
+ - **The language** - `stdlib/lang/python`, `typescript`, `java`, `go`, `rust`: the version, the dependency manager, formatting, linting and type checking (ruff and mypy for Python; Prettier, ESLint and `tsc --noEmit` for TypeScript), how a failure is reported (an exception, a returned error, a `Result`), the logging library, and the unit test framework and layout. A module on a stack the library does not have can import its language alone. The formatter, linter and type checker a language names are enforced by the testing scripts, which run them with the tests - the unit testing script on the code, the conformance testing script on the tests - and count them as the tests (see [the static checks](regenerating.md#the-static-checks)).
92
72
  - **The kind's `common`** - what every stack of a kind shares:
93
73
 
94
74
  | kind | settles |
@@ -98,52 +78,23 @@ It is built in three layers, and a stack imports the other two:
98
78
  | cli | settings from flags, then `<TOOL>_<SETTING>`, then defaults; the work apart from parsing; errors as `Error: <message>` on standard error, exit 0, 1, 2 for a usage error, 130 on Ctrl-C, a stack trace only with `--debug`; logs on standard error, warnings by default, `--verbose`, `--debug` |
99
79
  | library | one public entry point; one base error type, with a dependency's errors wrapped; logs through the language's logging with no output set up; no environment variables, output or global state |
100
80
 
101
- - **The stack** - the framework and how it does each of those: for `python-flask`, an application
102
- factory, one Blueprint per resource, Pydantic for bodies, `@app.errorhandler` for the error body,
103
- structlog JSON logs with the request id bound in a hook, Gunicorn. For `go-cobra`, `RunE`
104
- commands with Cobra's own error printing off, so `main` alone writes the error and picks the exit
105
- code, and slog on standard error.
106
-
107
- Each conformance template imports its kind's `conformance/common`: a backend's tests start the
108
- built service on a free port and reach it over HTTP only, checking a failure by status and
109
- `error.code`; a frontend's run the served build in Chromium through Playwright with the backend
110
- stood in; a command-line tool's run the built tool as a process; a library's go through its public
111
- entry point.
112
-
113
- - **Conformance tests apart.** A spec without the conformance template has no test reqs, so no
114
- conformance tests are written or run for it. Turning them on later is adding that one import.
115
- - **A reserved name.** An import that starts with `stdlib/` is always turri's template, never
116
- a file of the project's. A project with a module at such a path - `stdlib/backend/python-fastapi.plain`
117
- beside the spec, in its `template/` folder or in `PLAIN_TEMPLATE_DIRS` - gets an error saying the
118
- name is reserved, so which one a spec builds on never depends on what is on disk beside it.
119
- - **No concepts.** Its requirements are on the predefined `:Implementation:`, `:UnitTests:` and
120
- `:ConformanceTests:` only, so it never collides with a spec's own definitions.
121
- - **Pinned to a toolchain.** It names its stack's toolchain - Python 3.12, Node.js 24, Java 21, Go 1.25, Rust -
122
- which the agents use from this machine; `turri doctor` says when the machine lacks it.
123
- - **Not storage, not the testing scripts.** A module with a database says which on top of its
124
- stack. It still needs its unit testing script, and a conformance testing script when it has
125
- conformance tests.
126
- - **Known to the agents.** The `stdlib` rule, in every spec-writing agent's system prompt, says when
127
- to import which, and lists every template with what it is and the folder it is in, read from
128
- `stdlib/` when the rules load. `spawn` and `brood` import a stack when the one chosen
129
- is in it, and its conformance template when conformance tests are on.
130
- - **Only what this machine can build.** The agents use this machine's toolchains and install none, so
131
- a Go stack on a laptop without Go stops at its first build. Every spec-writing agent and the
132
- conversation are told which of the standard library's languages the machine can build now - Python
133
- needs `uv`, TypeScript and every frontend `node` and `npm`, Go `go`, Rust `cargo`, Java a JDK
134
- (`src/stack.ts`, `toolchainsBlock`) - and `spawn` and `brood` offer, and choose, only stacks in those.
135
- A stack the person names in a language the machine lacks is not refused: they are told what to install
136
- first, and that `turri doctor` checks it.
137
-
138
- A requirement beyond the stack - storage, an outside service - is added on top of it. A module that
139
- differs from its stack on something the stack settles - another framework, another layout - writes
140
- its own instead: an import cannot be taken back in part.
81
+ - **The stack** - the framework and how it does each of those: for `python-flask`, an application factory, one Blueprint per resource, Pydantic for bodies, `@app.errorhandler` for the error body, structlog JSON logs with the request id bound in a hook, Gunicorn. For `go-cobra`, `RunE` commands with Cobra's own error printing off, so `main` alone writes the error and picks the exit code, and slog on standard error.
82
+
83
+ Each conformance template imports its kind's `conformance/common`: a backend's tests start the built service on a free port and reach it over HTTP only, checking a failure by status and `error.code`; a frontend's run the served build in Chromium through Playwright with the backend stood in; a command-line tool's run the built tool as a process; a library's go through its public entry point.
84
+
85
+ - **Conformance tests apart.** A spec without the conformance template has no test reqs, so no conformance tests are written or run for it. Turning them on later is adding that one import.
86
+ - **A reserved name.** An import that starts with `stdlib/` is always turri's template, never a file of the project's. A project with a module at such a path - `stdlib/backend/python-fastapi.plain` beside the spec, in its `template/` folder or in `PLAIN_TEMPLATE_DIRS` - gets an error saying the name is reserved, so which one a spec builds on never depends on what is on disk beside it.
87
+ - **No concepts.** Its requirements are on the predefined `:Implementation:`, `:UnitTests:` and `:ConformanceTests:` only, so it never collides with a spec's own definitions.
88
+ - **Pinned to a toolchain.** It names its stack's toolchain - Python 3.12, Node.js 24, Java 21, Go 1.25, Rust - which the agents use from this machine; `turri doctor` says when the machine lacks it.
89
+ - **Not storage, not the testing scripts.** A module with a database says which on top of its stack. It still needs its unit testing script, and a conformance testing script when it has conformance tests.
90
+ - **Known to the agents.** The `stdlib` rule, in every spec-writing agent's system prompt, says when to import which, and lists every template with what it is and the folder it is in, read from `stdlib/` when the rules load. `spawn` and `brood` import a stack when the one chosen is in it, and its conformance template when conformance tests are on.
91
+ - **Only what this machine can build.** The agents use this machine's toolchains and install none, so a Go stack on a laptop without Go stops at its first build. Every spec-writing agent and the conversation are told which of the standard library's languages the machine can build now - Python needs `uv`, TypeScript and every frontend `node` and `npm`, Go `go`, Rust `cargo`, Java a JDK (`src/stack.ts`, `toolchainsBlock`) - and `spawn` and `brood` offer, and choose, only stacks in those. A stack the person names in a language the machine lacks is not refused: they are told what to install first, and that `turri doctor` checks it.
92
+
93
+ A requirement beyond the stack - storage, an outside service - is added on top of it. A module that differs from its stack on something the stack settles - another framework, another layout - writes its own instead: an import cannot be taken back in part.
141
94
 
142
95
  ## The rules
143
96
 
144
- `genome/rules/` holds the authoring rules. They are in the system prompt of every agent that
145
- writes specs - the conversation, the interview skills, the reviewers - so they are not something
146
- an agent has to decide to go and read.
97
+ `genome/rules/` holds the authoring rules. They are in the system prompt of every agent that writes specs - the conversation, the interview skills, the reviewers - so they are not something an agent has to decide to go and read.
147
98
 
148
99
  | rule | covers |
149
100
  | --- | --- |
@@ -158,68 +109,39 @@ an agent has to decide to go and read.
158
109
  | `import-modules.md`, `requires-modules.md` | the two ways modules build on each other |
159
110
  | `exported-concepts.md`, `required-concepts.md` | which concepts cross module boundaries |
160
111
  | `integrations.md` (+ standalone, embedded, embedded testing) | specs for REST API integrations, deployed standalone or embedded in an existing codebase |
161
- | `turri-regeneration.md` | how turri regenerates a spec change - as a spec diff, wherever it sits, in the chain's tip - so a spec is placed by what it means, never to spare regeneration |
112
+ | `turri-regeneration.md` | turri's model of itself: a spec change regenerated as a spec diff, wherever it sits, in the chain's tip; what each kind of change sets off; what a coding agent sees; how bad code is fixed; where the cost is - so a spec is placed by what it means, never to spare regeneration |
162
113
  | `stdlib.md` | the standard library: when to import which template, and every template with what it is and where it is, read from `stdlib/` |
163
114
 
164
115
  The ones that shape a spec most:
165
116
 
166
- - **One functionality is at most ~200 lines of code.** A bigger one is split before it is written
167
- (`analyze-if-func-spec-too-complex`, `break-down-func-spec`).
168
- - **Functional specs are in build order.** Each is rendered on top of the ones before it - which is
169
- what a spec may assume, not what a change costs: a spec inserted anywhere is regenerated alone.
170
- - **They state facts, with a concrete subject.** "A :User: can add a :Task:" - not "Implement
171
- adding tasks", not "The system should…".
172
- - **Nothing a test cannot check.** No `robust`, `seamless`, `as appropriate`; state the checkable
173
- fact or cut the word.
174
- - **No two specs contradict each other.** A conflict is found and resolved when a spec is added,
175
- not when its tests fail.
117
+ - **One functionality is at most ~200 lines of code.** A bigger one is split before it is written (`analyze-if-func-spec-too-complex`, `break-down-func-spec`).
118
+ - **Functional specs are in build order.** Each is rendered on top of the ones before it - which is what a spec may assume, not what a change costs: a spec inserted anywhere is regenerated alone.
119
+ - **They state facts, with a concrete subject.** "A :User: can add a :Task:" - not "Implement adding tasks", not "The system should…".
120
+ - **Nothing a test cannot check.** No `robust`, `seamless`, `as appropriate`; state the checkable fact or cut the word.
121
+ - **No two specs contradict each other.** A conflict is found and resolved when a spec is added, not when its tests fail.
176
122
 
177
123
  ## Writing specs with turri
178
124
 
179
- In a conversation (`turri`), say what you want and the matching skill runs. Two build a
180
- project from scratch, and write each spec change as a git diff that is built in the background the
181
- moment it is written. `brood` interviews you one question at a time and writes the specs as
182
- you answer; `spawn`, its quick twin, asks only what you want to build and which stack - or leaves the stack
183
- to it - and writes the rest on its own:
125
+ In a conversation (`turri`), say what you want and the matching skill runs. Two build a project from scratch, and write each spec change as a git diff that is built in the background the moment it is written. `brood` interviews you one question at a time and writes the specs as you answer; `spawn`, its quick twin, asks only what you want to build and which stack - or leaves the stack to it - and writes the rest on its own:
184
126
 
185
127
  | skill | phases | how a spec change is written |
186
128
  | --- | --- | --- |
187
129
  | `brood` | intent → stack → testing → product, feature by feature | first, how many questions the stack and product phases may ask - light 3, medium 5, thorough 10 or best judgement, broader with fewer, covering the same ground - and what they leave open turri decides itself, each decision a spec change of its own; every product answer is still a spec change, built before the next question; in testing, whether there are conformance tests, their `***test reqs***` and script - written as files, building nothing; then one spec change per answer, across whatever sections it needs, as a git diff with `write_spec_diff`, rendered as a render spec diff job and conformance tested as it is built |
188
130
  | `spawn` | intent → groundwork → product | only the intent and the stack are asked - the stack one question, `You choose` first, skipped when the user named one; the stack and the unit tests are written as files, with conformance tests off unless the user asks for them; then one spec change per feature, in build order, as a git diff with `write_spec_diff`, one after another with no answer between them. Changes after it are asked for in the conversation, as spec diffs, and what is left to build is worked out again from the spec |
189
131
 
190
- `mature` starts from a spec you already have, rough or hand-written, and runs on its own until
191
- it is fully rendered - every functionality built from the spec as it stands, its tests passing. It
192
- renders what is not built and watches the agents building it; when one struggles on something the
193
- spec leaves open or unclear - sent back again and again, the judge going back and forth, a spec
194
- conflict - it writes what the spec should say as a spec diff, stops that build, and renders it again.
195
- What the builds had to decide themselves - their assumptions, pitfalls, a failed build's diagnosis -
196
- goes into the spec the same way, when a user, a caller or a test can see it. It never asks: it takes
197
- the reading that fits the rest of the spec, and marks each such decision in its report. It leaves
198
- code, environment and turri problems to the builds, and stops when a functionality keeps
199
- struggling after the spec was clarified, when two cycles change nothing, or after twenty.
200
-
201
- For an existing project, `bud` works the same way, scoped to one feature: intent → scope → product.
202
- It first asks how many questions the product phase may ask - light, medium, thorough or best
203
- judgement, as `brood` does - then a short feature-intent interview, then product questions - a
204
- trigger and its outcome, an edge case, a rule - and every answer is a spec change, a git diff
205
- written with `write_spec_diff` and built the moment it is written, before the next question. What
206
- the questions leave open it decides itself, a spec change each. A change to something already
207
- specified, built or still to build, is put to the user first and is a spec diff too: what is left to
208
- build is worked out again from the spec. It ends on the person's confirmation of a short summary of
209
- the feature's changes.
132
+ `mature` starts from a spec you already have, rough or hand-written, and runs on its own until it is fully rendered - every functionality built from the spec as it stands, its tests passing. It renders what is not built and watches the agents building it; when one struggles on something the spec leaves open or unclear - sent back again and again, the judge going back and forth, a spec conflict - it writes what the spec should say as a spec diff, stops that build, and renders it again. What the builds had to decide themselves - their assumptions, pitfalls, a failed build's diagnosis - goes into the spec the same way, when a user, a caller or a test can see it. It never asks: it takes the reading that fits the rest of the spec, and marks each such decision in its report. It leaves code, environment and turri problems to the builds, and stops when a functionality keeps struggling after the spec was clarified, when two cycles change nothing, or after twenty.
133
+
134
+ For an existing project, `bud` works the same way, scoped to one feature: intent → scope → product. It first asks how many questions the product phase may ask - light, medium, thorough or best judgement, as `brood` does - then a short feature-intent interview, then product questions - a trigger and its outcome, an edge case, a rule - and every answer is a spec change, a git diff written with `write_spec_diff` and built the moment it is written, before the next question. What the questions leave open it decides itself, a spec change each. A change to something already specified, built or still to build, is put to the user first and is a spec diff too: what is left to build is worked out again from the spec. It ends on the person's confirmation of a short summary of the feature's changes.
210
135
 
211
136
  What keeps the interview honest is the harness, not the interviewer:
212
137
 
213
- - **Every question offers choices** (`ask_user` refuses a question without them), and each
214
- answer maps to one spec change.
215
- - **Every answer is written before the next question.** In the product phase of `brood` and `bud`, `ask_user` refuses
216
- a new question until the last answer is a spec change - see [skills and gates](skills-and-gates.md).
138
+ - **Every question offers choices** (`ask_user` refuses a question without them), and each answer maps to one spec change.
139
+ - **Every answer is written before the next question.** In the product phase of `brood` and `bud`, `ask_user` refuses a new question until the last answer is a spec change - see [skills and gates](skills-and-gates.md).
217
140
  - **Each phase ends on the person's confirmation** of a short summary, not on the interviewer's word.
218
141
 
219
142
  ## The skills
220
143
 
221
- Every skill in `genome/skills/` can be run from a conversation; most are also used by the bigger ones. A project's own,
222
- in `.turri/skills/` or `.agents/skills/`, can be too (see [skills and gates](skills-and-gates.md#a-projects-own-skills)).
144
+ Every skill in `genome/skills/` can be run from a conversation; most are also used by the bigger ones. A project's own, in `.turri/skills/` or `.agents/skills/`, can be too (see [skills and gates](skills-and-gates.md#a-projects-own-skills)).
223
145
 
224
146
  | to… | skills |
225
147
  | --- | --- |
@@ -231,8 +153,7 @@ in `.turri/skills/` or `.agents/skills/`, can be too (see [skills and gates](ski
231
153
  | restructure modules | `create-import-module`, `create-requires-module`, `refactor-module`, `consolidate-concepts` |
232
154
  | set up the project to build | `implement-unit-testing-script`, `implement-conformance-testing-script`, `check-plain-env` |
233
155
 
234
- `debug-specs` is the one for a running application that misbehaves: it reads the generated code
235
- to find the cause, traces it back to the spec, and fixes only the `.plain` files.
156
+ `debug-specs` is the one for a running application that misbehaves: it reads the generated code to find the cause, traces it back to the spec, and fixes only the `.plain` files.
236
157
 
237
158
  ## Checking a spec
238
159
 
@@ -240,11 +161,6 @@ to find the cause, traces it back to the spec, and fixes only the `.plain` files
240
161
  turri validate app.plain # or validate in a conversation
241
162
  ```
242
163
 
243
- It parses the spec with this repo's own parser, resolves its imports and required modules, and
244
- reports every syntax, concept and structure error with its line - no model, no API key. The
245
- interview skills run it before a phase ends, and `write_spec_diff`
246
- refuses a change that leaves a file that does not parse. Pass the project directory to check every
247
- module at once.
164
+ It parses the spec with this repo's own parser, resolves its imports and required modules, and reports every syntax, concept and structure error with its line - no model, no API key. The interview skills run it before a phase ends, and `write_spec_diff` refuses a change that leaves a file that does not parse. Pass the project directory to check every module at once.
248
165
 
249
- What `check` cannot see - a fact in the wrong section, a spec that is too big, two that conflict,
250
- wording no test can check - is what the rules, the skills and the reviewers are for.
166
+ What `check` cannot see - a fact in the wrong section, a spec that is too big, two that conflict, wording no test can check - is what the rules, the skills and the reviewers are for.