@turri-dev/cli 0.1.11-next.fec1e14 → 0.1.11
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 +5 -5
- package/dist/{spec-diff-cli.js → change-plain-cli.js} +3 -3
- package/dist/cli.js +1 -1
- package/dist/extension.js +4 -4
- package/dist/genome.js +5 -5
- package/dist/identity.js +8 -6
- package/dist/loop-cli.d.ts +1 -1
- package/dist/loop-cli.js +1 -1
- package/dist/regenerate/activity.js +2 -2
- package/dist/self-invocation.js +6 -6
- package/dist/skill.d.ts +3 -3
- package/dist/skill.js +2 -2
- package/dist/spec-tools.js +2 -2
- package/dist/stacks.js +1 -1
- package/dist/tools.js +1 -1
- package/docs/README.md +13 -13
- package/docs/authoring-plain.md +33 -33
- package/docs/config.md +25 -25
- package/docs/deploy.md +5 -5
- package/docs/development.md +11 -11
- package/docs/headless.md +18 -18
- package/docs/hosting.md +14 -14
- package/docs/images/how-turri-works.svg +1 -1
- package/docs/installation.md +13 -13
- package/docs/parser.md +5 -5
- package/docs/regenerating.md +78 -78
- package/docs/sandbox.md +7 -7
- package/docs/skills-and-gates.md +19 -19
- package/docs/theming.md +11 -11
- package/docs/using.md +50 -50
- package/genome/skills/brood/SKILL.md +11 -11
- package/genome/skills/brood/references/product.md +4 -4
- package/genome/skills/brood/references/testing.md +1 -1
- package/genome/skills/bud/SKILL.md +6 -6
- package/genome/skills/mature/SKILL.md +4 -4
- package/genome/skills/spawn/SKILL.md +4 -4
- package/package.json +1 -1
- /package/dist/{spec-diff-cli.d.ts → change-plain-cli.d.ts} +0 -0
package/docs/using.md
CHANGED
|
@@ -7,32 +7,32 @@ cd ~/my-project
|
|
|
7
7
|
turri
|
|
8
8
|
```
|
|
9
9
|
|
|
10
|
-
That is pi, with turri's tools, skills and themes loaded - `turri` alone, or with pi's options (`turri -c`, `turri --harness-model <m>`). It opens with turri's own banner - the jellyfish it is named after, regenerating until the conversation fills the screen: a pixel polyp on the sea floor grows into the jellyfish - a bell from pink down to indigo, an orange gut, neon tentacles - which swims a while, sinks back to the floor, and the polyp grows again, round and round: a picture in a terminal that shows images (Kitty, Ghostty, WezTerm, Warp, iTerm2), and drawn at a third of the size in half blocks in any other, tmux included (`src/jellyfish.ts` draws it, ported from the design system's pixel kit, `scripts/jellyfish-frames.mjs` writes the pictures) - turri's version (and, run from a clone, the commit it is on, and whether the clone has changes) and where its settings are changed - and only when something is wrong, skills that failed to load or agents' commands less fenced than they should be - rather than pi's startup header and resource list; `turri --verbose` shows those too. pi's "Update Available" notice is left out - turri's pi is a pinned dependency `pi update` never touches - and the terminal's tab reads `🪼 turri - <folder>`, not pi's `π`. Talk to it: ask it to add a feature to
|
|
10
|
+
That is [pi](https://pi.dev), with turri's tools, skills and themes loaded - `turri` alone, or with [pi](https://pi.dev)'s options (`turri -c`, `turri --harness-model <m>`). It opens with turri's own banner - the jellyfish it is named after, regenerating until the conversation fills the screen: a pixel polyp on the sea floor grows into the jellyfish - a bell from pink down to indigo, an orange gut, neon tentacles - which swims a while, sinks back to the floor, and the polyp grows again, round and round: a picture in a terminal that shows images (Kitty, Ghostty, WezTerm, Warp, iTerm2), and drawn at a third of the size in half blocks in any other, tmux included (`src/jellyfish.ts` draws it, ported from the design system's pixel kit, `scripts/jellyfish-frames.mjs` writes the pictures) - turri's version (and, run from a clone, the commit it is on, and whether the clone has changes) and where its settings are changed - and only when something is wrong, skills that failed to load or agents' commands less fenced than they should be - rather than [pi](https://pi.dev)'s startup header and resource list; `turri --verbose` shows those too. [pi](https://pi.dev)'s "Update Available" notice is left out - turri's [pi](https://pi.dev) is a pinned dependency `pi update` never touches - and the terminal's tab reads `🪼 turri - <folder>`, not [pi](https://pi.dev)'s `π`. Talk to it: ask it to add a feature to \*\*\*plain, regenerate a module, fix a module's \*\*\*plain. It decides which skill or tool applies - but not *how* a skill runs: `cultivate` hands the work to the gated runner, which owns the steps, the gates and the judges (see [skills and gates](skills-and-gates.md)).
|
|
11
11
|
|
|
12
12
|
Three skills you start yourself, by their commands - turri never starts them on its own, and says which to type when one fits: `/brood`, `/spawn` and `/bud`, each with what you want after it - `/spawn a URL shortener in Go`. The skills that build from scratch:
|
|
13
13
|
|
|
14
14
|
| skill | how it works |
|
|
15
15
|
| --- | --- |
|
|
16
|
-
| `brood` | first, how many questions the stack and product phases may ask - light (3), medium (5), thorough (10) or best judgement, broader with fewer, turri deciding what they leave open; then intent, stack, then testing - conformance tests on or off (into `config.turri.yaml`), their `***test reqs***` and the conformance testing script, which build nothing - then the product feature by feature, where every answer becomes one
|
|
17
|
-
| `spawn` | its quick twin, for something running in minutes: asks what you want to build only when your message does not say, and chooses the stack itself unless you named one - conformance tests off, unless you ask for them - then writes the smallest thing that runs, which starts the first build at once, and the main slice of what you described and one more on their happy path, each a
|
|
18
|
-
| `mature` | for
|
|
16
|
+
| `brood` | first, how many questions the stack and product phases may ask - light (3), medium (5), thorough (10) or best judgement, broader with fewer, turri deciding what they leave open; then intent, stack, then testing - conformance tests on or off (into `config.turri.yaml`), their `***test reqs***` and the conformance testing script, which build nothing - then the product feature by feature, where every answer becomes one \*\*\*plain change - a git diff of the `.plain` files, written with `change_plain` and regenerated as a \*\*\*plain diff pulse the moment it is written, with its conformance tests when they are on |
|
|
17
|
+
| `spawn` | its quick twin, for something running in minutes: asks what you want to build only when your message does not say, and chooses the stack itself unless you named one - conformance tests off, unless you ask for them - then writes the smallest thing that runs, which starts the first build at once, and the main slice of what you described and one more on their happy path, each a \*\*\*plain diff queued as it is written - nothing you did not describe: named only the kind of software, you get it running, and say what it does. You try each as it lands, and say `go on` for the next slice it proposed, or what to change instead - every change is a \*\*\*plain diff, and what is left to build is worked out again from \*\*\*plain |
|
|
18
|
+
| `mature` | for \*\*\*plain you already have, rough or hand-written: on its own until it is fully regenerated - regenerates what is not built, watches the agents building it, and when one struggles on something \*\*\*plain leaves open, settles it in \*\*\*plain as a \*\*\*plain diff, stops that build and regenerates it again; decides open choices itself and marks them in its report |
|
|
19
19
|
|
|
20
20
|
Peeking is safe while a question is open - a skill's or the chat's: `ctrl+h` opens the side panel over the question, which waits under it as it was - the panel says `your question is waiting`, and `ctrl+h` or `esc` takes you back to answer it. (The question has the keys, so turri catches `ctrl+h` itself while one is open.) The dialog holds the question alone - no hint and no invitation; the `ctrl+h` under the job panel breathes until it is first pressed.
|
|
21
21
|
|
|
22
22
|
While a skill runs, a line above the input shows its phases, every one by name - those done ticked, the one it is in marked, the rest to come - and where it is: the skill's name at the left edge, `phase 2 of 4` at the right, and its phases centred between them, `✓ intent ━━━━ ⠋ stack ──── ○ testing ──── ○ product`. Under it, dimmed, the phase it is in and what it does: `stack - the language, frameworks, storage and architecture`. The phases are the skill's own steps, read from its `SKILL.md`. A skill of one phase, or none, has nothing to be partway through: its line is the spinner and its name alone, `⠹ add-test-requirement`.
|
|
23
23
|
|
|
24
|
-
`genome/skills/` holds the rest: adding a feature, a concept, a requirement, a resource; splitting a spec that is too big; resolving conflicts; checking the environment - see [authoring \*\*\*plain](authoring-plain.md#the-skills). A skill run that is interrupted can be resumed where it stopped. A project can have skills of its own, in `.turri/skills/` or `.agents/skills/` - see [skills and gates](skills-and-gates.md#a-projects-own-skills).
|
|
24
|
+
`genome/skills/` holds the rest: adding a feature, a concept, a requirement, a resource; splitting a functional spec that is too big; resolving conflicts; checking the environment - see [authoring \*\*\*plain](authoring-plain.md#the-skills). A skill run that is interrupted can be resumed where it stopped. A project can have skills of its own, in `.turri/skills/` or `.agents/skills/` - see [skills and gates](skills-and-gates.md#a-projects-own-skills).
|
|
25
25
|
|
|
26
26
|
## The tools
|
|
27
27
|
|
|
28
28
|
| tool | |
|
|
29
29
|
| --- | --- |
|
|
30
|
-
| `regenerate` | regenerate a module and its chain from
|
|
31
|
-
| `drift` | what is left to regenerate: how far the code and its conformance tests have drifted from
|
|
30
|
+
| `regenerate` | regenerate a module and its chain from \*\*\*plain as it is now - after any change to it, by any means: what is left to regenerate is planned into pulses of one to three functional specs, each with the definitions and reqs it uses, and a pulse regenerating what \*\*\*plain no longer wants is stopped. With functionalities named, those are regenerated though their \*\*\*plain did not change - rewritten or started over, planned into pulses as any change is; with `from_scratch`, the whole module, its code deleted first - `"chain"` for every module of its requires chain too, built again from the bottom - only when you ask, or agree |
|
|
31
|
+
| `drift` | what is left to regenerate: how far the code and its conformance tests have drifted from \*\*\*plain, starting nothing |
|
|
32
32
|
| `glimpse` | the pulse a module's codebases would start next, and the whole prompt its coding agent would get - starting nothing |
|
|
33
33
|
| `steer` | say how the work is planned - which functional specs go together, what first - for the project or one module, for the session; the next plan follows it, beside what AGENTS.md says under a heading naming the queue |
|
|
34
34
|
| `conform` | write and run conformance tests for functional specs - now, or ahead of their code: every one tested in one conformance pulse, as far as a size guardrail lets (or one a functional spec, or a pulse each of groups you name - 1-3 and 4-6 - when you ask for that), started once every one named is regenerated; whatever the config says |
|
|
35
|
-
| `validate` | validate
|
|
35
|
+
| `validate` | validate \*\*\*plain, regenerating nothing |
|
|
36
36
|
| `engine` | show or choose the model the code is regenerated on - see [below](#choosing-a-model) |
|
|
37
37
|
| `pulses` | the pulses - running, planned and finished - or one of them |
|
|
38
38
|
| `halt` | halt a pulse, a module's planned pulses, or what `activate` started |
|
|
@@ -45,24 +45,24 @@ While a skill runs, a line above the input shows its phases, every one by name -
|
|
|
45
45
|
| `ask_user` | put a question to you as a dialog to pick from - several at once, each a box you tick and untick, "Something else…" to type, or "Chat about this" to talk it over first; Esc in either goes back to the options |
|
|
46
46
|
| `web_search`, `web_fetch` | search the web and read a page as text - documentation, an API, an error; `curl` and the like run in its `bash` |
|
|
47
47
|
|
|
48
|
-
What each
|
|
48
|
+
What each regeneration does, in order, is in [regenerating the code](regenerating.md).
|
|
49
49
|
|
|
50
|
-
The conversation knows the
|
|
50
|
+
The conversation knows the \*\*\*plain standard library from its first turn: the stdlib rule in its system prompt lists every stack and where the templates are, so "build this as a Flask backend" becomes an import of `stdlib/backend/python-flask`, not a stack written out by hand. The banner at start says how many stacks there are, and **`/stdlib`** lists them, each with what it is (see [the standard library](authoring-plain.md#the-standard-library)).
|
|
51
51
|
|
|
52
|
-
The agent does this work with these tools, never by running turri's command line - or another pi - through its shell: that would be a separate, headless run outside regen and `/halt`. The harness refuses such a command unless your message asks for the command line ("run it headless", "use the CLI", or the command itself). pi configuring itself - `pi install`, `update`, `list`, `config`, `auth`, its settings - is not affected.
|
|
52
|
+
The agent does this work with these tools, never by running turri's command line - or another [pi](https://pi.dev) - through its shell: that would be a separate, headless run outside regen and `/halt`. The harness refuses such a command unless your message asks for the command line ("run it headless", "use the CLI", or the command itself). [pi](https://pi.dev) configuring itself - `pi install`, `update`, `list`, `config`, `auth`, its settings - is not affected.
|
|
53
53
|
|
|
54
54
|
## Pulses
|
|
55
55
|
|
|
56
56
|
A pulse is one unit of work regenerating the code. It was called a job, and still is where a name is kept - `job_id`, `parallel-jobs`, `allow-to-stop-jobs`, `.turri/jobs/` - and in a project's own files written before: the chat is told the two are the same, so an `AGENTS.md` or a skill that says "job" needs no change.
|
|
57
57
|
|
|
58
|
-
A
|
|
58
|
+
A regeneration takes minutes, so in the TUI every tool that regenerates starts the work as a pulse and returns straight away. The conversation carries on; pulses on the same module - or on modules of one `requires` chain - queue behind each other, in the order they were asked for, from the first pulse on: one queued while the module's \*\*\*plain is only a scaffold, with no functional specs yet, or while it does not parse, still holds its module, so nothing on the module runs beside it. Pulses on modules that share nothing run side by side, each with its own progress panel - two modules or ten. How many run at once is capped by what the machine has room for: past `parallel-jobs` (see [the project config](config.md#how-many-jobs-at-once)), a pulse whose turn has come waits for a free slot, and says so.
|
|
59
59
|
|
|
60
|
-
- **The panel** above the input, while something runs, shows on top what finished - a line a module, its pulses oldest first, each with its mark (green, red or amber, as in the side panel), number and kind - what it regenerated is in the side panel - `cognio … 2 earlier · ✓ 3 regenerate · ✓ 4 conformance`, the newest that fit the width - then a rule, and under it each running pulse - `cognio · regenerate pulse 5`, `cognio · conformance pulse 6` - its time and cost, and what its agents are doing. A module whose work is being planned into pulses shows at once, before any pulse runs - `shorty · planning the pulses · 12s` - the planner is an agent, and takes a while; a `/regenerate` says it began the moment it is sent. Pulses are numbered within their module, as regen numbers them. `ctrl+o` expands the agents' trace alone; what finished stays a line a module. The line of keys under it - `ctrl+o to expand the trace · ctrl+h to peek at regen` - has each key breathing, fully there to a little faint and back, until it is first pressed: once `ctrl+o` has opened the trace, or the side panel has been opened, that key is still for the rest of the session and the other breathes on. What waits is not in it, and it shows no
|
|
60
|
+
- **The panel** above the input, while something runs, shows on top what finished - a line a module, its pulses oldest first, each with its mark (green, red or amber, as in the side panel), number and kind - what it regenerated is in the side panel - `cognio … 2 earlier · ✓ 3 regenerate · ✓ 4 conformance`, the newest that fit the width - then a rule, and under it each running pulse - `cognio · regenerate pulse 5`, `cognio · conformance pulse 6` - its time and cost, and what its agents are doing. A module whose work is being planned into pulses shows at once, before any pulse runs - `shorty · planning the pulses · 12s` - the planner is an agent, and takes a while; a `/regenerate` says it began the moment it is sent. Pulses are numbered within their module, as regen numbers them. `ctrl+o` expands the agents' trace alone; what finished stays a line a module. The line of keys under it - `ctrl+o to expand the trace · ctrl+h to peek at regen` - has each key breathing, fully there to a little faint and back, until it is first pressed: once `ctrl+o` has opened the trace, or the side panel has been opened, that key is still for the rest of the session and the other breathes on. What waits is not in it, and it shows no \*\*\*plain diff - the side panel has both. With nothing running it is gone.
|
|
61
61
|
- **A pulse's change** is what its agent works from, not a text diff of the module's file: the items `<spec change>` gives the agent - only what the pulse's functional specs use, an import's items as well as the module's own, a code pulse's without the test reqs - each in the file it is written in, under its section, in the file's order (`change-view.ts`).
|
|
62
|
-
- **A
|
|
63
|
-
- **The side panel** (`ctrl+h`, as it was left - but on the pulse that last failed, stopped or did not pass, when one has since it was last opened: the live panel that showed it is gone with the last pulse, and its announcement in the chat says `ctrl+h shows this pulse`; **`/pulses`** on the pulses; not `ctrl+j`, which a terminal without the Kitty keyboard protocol sends as a line feed, the same as `shift+enter`, and which a dialog takes for `enter`; both stay pi's new-line keys. `ctrl+h` is the byte old terminals sent for backspace; a terminal set to send `^H` for its Backspace key would open the panel on it) takes the whole screen - the panel or the chat, one at a time - or, `s`, sits beside the chat: the chat on the left, the panel in the right half of the screen, and `s` again over the whole screen. It opens as it was last left, for the conversation; a terminal under 104 columns - 100 inside the screen's padding, a blank row above and below everything and two columns either side - has no room for both and shows the panel over the whole screen. It opens on **regen**: the
|
|
62
|
+
- **A \*\*\*plain diff**, in the side panel, reads as an editor shows a diff: its [\*\*\*plain](https://plainlang.org/docs/) highlighted as [plyn](https://github.com/plainlang/plyn) highlights it - section headers, concepts, names in backticks, text in quotes, links, the front matter - each added line filled green and each removed one red, the text on top, the fill the width of the panel ([colours](theming.md#plain-diffs)). It is shown file by file, each file's lines together under its name, with what it adds and takes out - the module being regenerated first, the rest by name, and none the diff does not change. Each line has its number in that file: a \*\*\*plain diff is of \*\*\*plain as read, with what it imports laid in, so a line is placed in the file it is written in - its links read as the parser renders them - and a removed line, in no file now, goes with the change it is part of and has no number. A line is never put in a file on a guess: one that cannot be placed is listed last, under "not found in the spec's files". Where lines of a file are not in the diff, `↕ 28 lines` says how many, as GitHub does - `↑` above the first - and names the section the lines under it are in when it is another than the one above, `↕ 28 lines · ***functional specs***`, as GitHub names a hunk's function: each section is named once.
|
|
63
|
+
- **The side panel** (`ctrl+h`, as it was left - but on the pulse that last failed, stopped or did not pass, when one has since it was last opened: the live panel that showed it is gone with the last pulse, and its announcement in the chat says `ctrl+h shows this pulse`; **`/pulses`** on the pulses; not `ctrl+j`, which a terminal without the Kitty keyboard protocol sends as a line feed, the same as `shift+enter`, and which a dialog takes for `enter`; both stay [pi](https://pi.dev)'s new-line keys. `ctrl+h` is the byte old terminals sent for backspace; a terminal set to send `^H` for its Backspace key would open the panel on it) takes the whole screen - the panel or the chat, one at a time - or, `s`, sits beside the chat: the chat on the left, the panel in the right half of the screen, and `s` again over the whole screen. It opens as it was last left, for the conversation; a terminal under 104 columns - 100 inside the screen's padding, a blank row above and below everything and two columns either side - has no room for both and shows the panel over the whole screen. It opens on **regen**: the module's \*\*\*plain itself, in full, with how much of it the code has been regenerated from drawn onto it.
|
|
64
64
|
|
|
65
|
-
Every item of the
|
|
65
|
+
Every item of the module's \*\*\*plain - a definition, a req, a functional spec with its sub-bullets and acceptance tests, what turri plans and diffs by - is a row in the file's order, its \*\*\*plain highlighted as plyn highlights it (as a \*\*\*plain diff is) and wrapped to the width, never cut short. The map has two columns, each headed on the line over it with a line of space under it: **status**, a mark a row - its rung on a ladder every item climbs - and the module's file, its \*\*\*plain. The top of the panel is where it is seen first: the top line, the module tabs, and the keys - each view's, `d talk it through in the chat` first when the module needs you - wrapped, never cut short, a line of space between the top line and the keys; then straight under them a rule, a line of space, and the columns' headings. The pulses view has its keys at the top too, and a line of space under its rule. The legend is a box of its own in the upper right corner, over what is under it, a line a mark, up the ladder. It names every rung, whatever the page shows - to take out only where something is - and a panel under 60 columns wide has none.
|
|
66
66
|
|
|
67
67
|
| Mark | Legend | Means |
|
|
68
68
|
| --- | --- | --- |
|
|
@@ -70,39 +70,39 @@ A render takes minutes, so in the TUI every render tool starts the work as a pul
|
|
|
70
70
|
| `⠋` | working | being regenerated now, or being taken out - the spinner in the accent colour |
|
|
71
71
|
| `◇` | regenerated | the code is built from it as it reads now |
|
|
72
72
|
| `⠋` | validating | its conformance tests being written and run - the spinner green, as `◇` and `◆` are |
|
|
73
|
-
| `◆` | conform | regenerated, and its conformance tests pass - its code is shown, end to end, to do what
|
|
73
|
+
| `◆` | conform | regenerated, and its conformance tests pass - its code is shown, end to end, to do what \*\*\*plain says |
|
|
74
74
|
| `!` | needs you | in the pulse that did not land and halted the module, or the planner found no plan |
|
|
75
|
-
| `−` | to take out | taken out of
|
|
75
|
+
| `−` | to take out | taken out of \*\*\*plain, still in the code - a struck-through red ghost, with the code record's text, until the pulse that takes it out lands |
|
|
76
76
|
|
|
77
77
|
`◇` and `◆` are both green, and mean the same whatever `conformance-tests:` says: a functional spec regenerated and not conformed is `◇` with its conformance tests on, off or not chosen, so changing the setting changes no mark. Tests written for an earlier text leave it at `◇`. Definitions and reqs are never conformed: `◇` is as far as they go. Test reqs are shown only where conformance tests are on - `conformance-tests:` is not `false`, or `conform` turned them on this session. A test req is the conformance tests', never built into the code: `◆` once they follow it as it reads now, the green `⠋` while a conformance pulse writes them for it, `○` until then - and no mark where no conformance test is written at all. No pulse numbers are drawn in the gutter - they move whenever the plan is made again; `p` on an item goes to the pulse that brings it.
|
|
78
78
|
|
|
79
|
-
**Needs you.** The top line ends `! needs you`, in red, and under the first `!` of the module is what for, and how: `needs you - pulse 3 didn't land - its unit tests never passed · d to talk it through in the chat`. `d` closes the panel and asks the chat, in your words, what went wrong and what to do about it, with turri's reason for the halt; the chat works it out with you - a diagnosis, a change to
|
|
79
|
+
**Needs you.** The top line ends `! needs you`, in red, and under the first `!` of the module is what for, and how: `needs you - pulse 3 didn't land - its unit tests never passed · d to talk it through in the chat`. `d` closes the panel and asks the chat, in your words, what went wrong and what to do about it, with turri's reason for the halt; the chat works it out with you - a diagnosis, a change to \*\*\*plain, the machine set up - and regenerates. Pulses are numbered as they start in a module, from 1 each session.
|
|
80
80
|
|
|
81
|
-
A changed item shows what changed in it: its first line word by word - what is gone struck through in red, what is new in green, what is kept highlighted as it reads now - and, under it, only the sub-bullets that changed.
|
|
81
|
+
A changed item shows what changed in it: its first line word by word - what is gone struck through in red, what is new in green, what is kept highlighted as it reads now - and, under it, only the sub-bullets that changed. \*\*\*plain never built that is taken out is simply gone. The top line has a strip of the module's functional specs - built, regenerating, planned, waiting - and how many are built, then what is happening: `pulse 3 of 8 regenerating`, or why nothing is - `not asked to build - regenerate plans it` (turri was just opened, or nothing has asked for the module: the plan lives in memory and is made again on the next regeneration), `planning`, `waits for backend`, or `! needs you`. `enter` opens an item: all of its text against the code's, and its acceptance tests. Where it stands is beside its mark, and is not said again under it.
|
|
82
82
|
|
|
83
|
-
One module is shown at a time, with a tab for each module of the chain on the line under the top line, in the chain's order - the module it is built on to the left: `d365_version_api [d365_http_client] d365_utils 12 ›`. A tab regenerating spins, one stuck says `!`; what does not fit is counted at either end. `←` `→` go from one tab to the next. The module shown is the one in focus - the one regenerating now, else the one asked for, else the first - until you pick another tab; it goes back to following once another module is in focus, or the one in focus starts regenerating. Under the module's own text is what it imports, under **imported**, a folded block a file, `▸ from stdlib/backend/python-fastapi · 7 reqs · all built`, closed until opened. The modules it requires are tabs of their own: a chain of many modules is never drawn nested, one in the next. A map longer than the screen has a minimap down its right edge, a cell a slice in the colour of its most pressing mark, the slice shown solid. The map is read from the files - the code record, the `.plain` files, the tests record - so it is right the moment turri opens a project, before anything runs; the work loop's plan adds the pulses on top. It maps the files asked to build this conversation, else `module:` in `config.turri.yaml`, else the `.plain` files of the folder that no other one there imports or requires - the folder's files looked at again only once one of them, or its config, changes. `↑` `↓` move row by row, `enter` or `space` open and close an item or an import block - a functional spec opens to its sub-bullets and its `***acceptance tests***`, nested under it as
|
|
83
|
+
One module is shown at a time, with a tab for each module of the chain on the line under the top line, in the chain's order - the module it is built on to the left: `d365_version_api [d365_http_client] d365_utils 12 ›`. A tab regenerating spins, one stuck says `!`; what does not fit is counted at either end. `←` `→` go from one tab to the next. The module shown is the one in focus - the one regenerating now, else the one asked for, else the first - until you pick another tab; it goes back to following once another module is in focus, or the one in focus starts regenerating. Under the module's own text is what it imports, under **imported**, a folded block a file, `▸ from stdlib/backend/python-fastapi · 7 reqs · all built`, closed until opened. The modules it requires are tabs of their own: a chain of many modules is never drawn nested, one in the next. A map longer than the screen has a minimap down its right edge, a cell a slice in the colour of its most pressing mark, the slice shown solid. The map is read from the files - the code record, the `.plain` files, the tests record - so it is right the moment turri opens a project, before anything runs; the work loop's plan adds the pulses on top. It maps the files asked to build this conversation, else `module:` in `config.turri.yaml`, else the `.plain` files of the folder that no other one there imports or requires - the folder's files looked at again only once one of them, or its config, changes. `↑` `↓` move row by row, `enter` or `space` open and close an item or an import block - a functional spec opens to its sub-bullets and its `***acceptance tests***`, nested under it as \*\*\*plain has them - `←` `→` go to the next module's tab, `o` and `c` open or close everything shown, `pgup` `pgdn` and the wheel scroll, `g`/`G` go to the top and the bottom.
|
|
84
84
|
|
|
85
|
-
`p` goes to the **pulses** - and back - onto the pulse that regenerates the item under the cursor, and back onto the first item the pulse under the cursor regenerates. The pulses are every pulse as three sections - running, queued, finished - each opened and closed on its own (running and queued start open) - and a fourth, **earlier sessions**, closed, when the [ledger](#the-ledger) has pulses of sessions before this one: its last three runs', each pulse read only - opened, what the ledger says of it (how it ended, its rounds and test runs, the healers, what turri refused and committed, its last stages and its log) - and a line for how many more runs it has. A pulse is one line: its module, kind and number (`hubspot · regenerate pulse 1`, `hubspot · conformance pulse 2`) - so it is named wherever a pulse is named in a line (the job panel, the `pulses` tool, `/halt`, the command line), never by what it regenerates, which is in it, opened - its time (none while it is queued: it has not started) and its cost - a finished one's mark green when it passed, red when it failed or did not pass, amber when it was stopped. Opened, a running one shows what it is doing; a queued one, what it waits for; a finished one, its agents and what each spent, and whether it did not pass. Under it is a row for each file its
|
|
85
|
+
`p` goes to the **pulses** - and back - onto the pulse that regenerates the item under the cursor, and back onto the first item the pulse under the cursor regenerates. The pulses are every pulse as three sections - running, queued, finished - each opened and closed on its own (running and queued start open) - and a fourth, **earlier sessions**, closed, when the [ledger](#the-ledger) has pulses of sessions before this one: its last three runs', each pulse read only - opened, what the ledger says of it (how it ended, its rounds and test runs, the healers, what turri refused and committed, its last stages and its log) - and a line for how many more runs it has. A pulse is one line: its module, kind and number (`hubspot · regenerate pulse 1`, `hubspot · conformance pulse 2`) - so it is named wherever a pulse is named in a line (the job panel, the `pulses` tool, `/halt`, the command line), never by what it regenerates, which is in it, opened - its time (none while it is queued: it has not started) and its cost - a finished one's mark green when it passed, red when it failed or did not pass, amber when it was stopped. Opened, a running one shows what it is doing; a queued one, what it waits for; a finished one, its agents and what each spent, and whether it did not pass. Under it is a row for each file its \*\*\*plain diff changes - the file of the module being regenerated first and open, the rest by name and closed - each opening onto that file's lines. A file with nothing added or taken out is not listed. `↑` `↓` move, `enter` or `space` open and close what is under the cursor, `→` `←` open and close (`←` on a closed pulse goes to its section, on a closed file to its pulse), `o` and `c` open or close every pulse of the section - or, on a file, every file of its pulse - `pgup` `pgdn` and the wheel scroll, `g`/`G` go to the top and the bottom. The cursor stays on its row when the pulse under it moves on; a pulse opened closes when it moves to another section. What is open is kept while the panel is closed. What the agents said and did is not shown - it is in each pulse's log (see [Agent logs](#agent-logs)).
|
|
86
86
|
|
|
87
|
-
`ctrl+h` again, or `esc`, closes it and goes back to the chat. Beside the chat, one side has the keys and the other is drawn faint: `ctrl+h` switches sides - from the chat to the panel, from the panel to the chat, the panel staying and following the pulses - `esc` gives the chat the keys too, and a click gives them to the side clicked. `ctrl+c` closes the panel; so do `s`, over the whole screen, then `esc`. (pi's regular screen, `--tui-mode regular`, reads no mouse: there it is the keys alone.) While the panel has the keys, every key is the panel's, whatever else happens: a question the chat model asks in the background (`ask_user`) waits in the chat, unanswered - an `enter` in the panel never answers it - and the panel's top line says "the chat is waiting for your answer". Back in the chat, the question is there, with the keys.
|
|
88
|
-
- `turri` runs pi in fullscreen mode, so the conversation stays where you scroll it while the pulses redraw (PgUp/PgDn, the wheel); `--tui-mode regular` goes back to the terminal's own scrollback.
|
|
87
|
+
`ctrl+h` again, or `esc`, closes it and goes back to the chat. Beside the chat, one side has the keys and the other is drawn faint: `ctrl+h` switches sides - from the chat to the panel, from the panel to the chat, the panel staying and following the pulses - `esc` gives the chat the keys too, and a click gives them to the side clicked. `ctrl+c` closes the panel; so do `s`, over the whole screen, then `esc`. ([pi](https://pi.dev)'s regular screen, `--tui-mode regular`, reads no mouse: there it is the keys alone.) While the panel has the keys, every key is the panel's, whatever else happens: a question the chat model asks in the background (`ask_user`) waits in the chat, unanswered - an `enter` in the panel never answers it - and the panel's top line says "the chat is waiting for your answer". Back in the chat, the question is there, with the keys.
|
|
88
|
+
- `turri` runs [pi](https://pi.dev) in fullscreen mode, so the conversation stays where you scroll it while the pulses redraw (PgUp/PgDn, the wheel); `--tui-mode regular` goes back to the terminal's own scrollback.
|
|
89
89
|
- **`/turri-config`** changes a setting of the project's `config.turri.yaml` - the models, review, conformance tests, any key - and says when it takes effect: now, at your next message, or at the next pulse; never only after a restart. See [changing it from the chat](config.md#changing-it-from-the-chat).
|
|
90
90
|
- **`/stack`** chooses the stack everything is built with from then on, in a project whose parts are on different stacks: a list of the project's stacks, or `/stack backend`, `/stack none`. The stack's `config.turri.<stack>.yaml` is laid over `config.turri.yaml` - its testing scripts, toolchains, models - for the session; a module already asked for keeps the stack it was asked for with. The chat model switches it too when you ask - and, to build one part, passes that part's stack to `regenerate` instead, leaving the session's as it is. See [stacks](config.md#stacks).
|
|
91
|
-
- **`/regenerate [module | file.plain] [functionality ...] [from scratch [whole chain]] [how to cut the work]`** regenerates a module from its
|
|
92
|
-
- **`/halt <id>`** halts a pulse - a queued one never starts - **`/halt <module>`** (or a planned row's id) halts a module's planned pulses until you ask for it again, a
|
|
93
|
-
- **What these commands show you, the chat knows.** What `/regenerate` and `/halt activated` tell you -
|
|
94
|
-
- **Nothing is queued.** What is left to build is worked out from what each codebase is built from and
|
|
95
|
-
- **A change to a module below** is made in the codebase at the tip of the chain, where its code lives; the codebase below is left as it was, and is brought up to its own
|
|
91
|
+
- **`/regenerate [module | file.plain] [functionality ...] [from scratch [whole chain]] [how to cut the work]`** regenerates a module from its \*\*\*plain as it is now, as the `regenerate` tool does: what is left to regenerate is planned into pulses - one functional spec at a time, a few together or the whole module in one, as the planner judges - and the first starts. Words after the module are guidance for that plan (`/regenerate app models first`). A module's name typed with spaces for its underscores is that module - `/regenerate d365_function executor` is `d365_function_executor`. With no module named: the one `module:` in `config.turri.yaml` names, else the folder's top modules, to pick from. Asked for from scratch - `from scratch`, `start over`, `delete the module` - it asks first, then deletes the module's code and conformance tests in every codebase where they live, committed so `/lineage` keeps them, and regenerates the whole module from its \*\*\*plain, built though nothing in it changed: `/regenerate shorty from scratch`. The modules below it keep their code, and the module starts again from a copy of the code below it. Asked for its chain too - `/regenerate shorty from scratch, the whole chain` - the code of every module of its requires chain is deleted, the base's included, and the whole chain is regenerated from the bottom: what to do after modules were split, merged or moved, when the code below was laid out for modules that are no more. The chat's `regenerate` does the same with `from_scratch: "module"` or `"chain"`, when you ask for it; `turri regenerate --from-scratch` or `--from-scratch-chain` on the command line, not with `--dry-run`. Functionalities named after the module - `/regenerate shorty 3 5` - are regenerated though their \*\*\*plain did not change, on the model chosen for functionalities: as `turri regenerate shorty.plain 3 5` does, and the chat's `regenerate` with `functionalities`.
|
|
92
|
+
- **`/halt <id>`** halts a pulse - a queued one never starts - **`/halt <module>`** (or a planned row's id) halts a module's planned pulses until you ask for it again, a \*\*\*plain change not starting it, and **`/halt activated <name>`** halts a server `activate` started; it lists all three when no id is given.
|
|
93
|
+
- **What these commands show you, the chat knows.** What `/regenerate` and `/halt activated` tell you - \*\*\*plain that does not parse, the pulses planned, a server halted - stays in the chat under the command, and since a slash command runs outside the conversation, it is put in the conversation as well: say "fix it" next and turri knows what you are looking at. A halted pulse reaches it as every pulse's end does.
|
|
94
|
+
- **Nothing is queued.** What is left to build is worked out from what each codebase is built from and \*\*\*plain as it is now - again whenever a pulse ends or \*\*\*plain changes - and the first pulse of it runs; see [a whole module](regenerating.md#a-whole-module). Changes that overlap never pile up: the next pulse builds \*\*\*plain as it is. The pulses planned after the one running are listed behind it, as queued, and so is each module waiting for the one it is built on - "waits for base" - until it can be planned. A change inside what the running pulse regenerates - a functional spec it regenerates reworded or given a sub-bullet, a definition or req it is given, a file they link - steers it: its agent is told, and it lands \*\*\*plain as it is now there, twice a pulse at most. Otherwise a pulse building something \*\*\*plain no longer wants is stopped and rolled back - unless `allow-to-stop-jobs: false` in `config.turri.yaml`, which has it land first and the change regenerated on top. To drop something, take it out of \*\*\*plain - one \*\*\*plain diff. Stopping a pulse leaves its change in \*\*\*plain: the codebase waits until its \*\*\*plain changes or it is asked for again.
|
|
95
|
+
- **A change to a module below** is made in the codebase at the tip of the chain, where its code lives; the codebase below is left as it was, and is brought up to its own \*\*\*plain only when a new module is to start from it. \*\*\*plain moved into a module the code was not built on stops the codebase: no change to its code brings that module in, so it has to be started over - its code and conformance tests taken out, kept in its history, and built again from the bottom of the chain. That is never done unasked: the chat asks you, saying why, and regenerates it from scratch once you agree; on the command line `turri regenerate` asks at the terminal, and `--start-over` agrees in advance.
|
|
96
96
|
- **Conformance tests are a track of their own.** A code pulse lands once its unit tests pass and are reviewed. Then, before the next one builds on it, a conformance pulse writes and runs the tests of what it built - from the test reqs and acceptance tests - with the whole suite the module inherits, and, where one fails, fixes the test or the code, whichever is wrong; what they are written for is recorded in `tests/.turri.json`. With `conformance-tests: false` none is written until you ask - `conform`, for a module or some of its functional specs. A conformance pulse that does not pass is undone whole and halts the conformance tests alone: the code goes on being built.
|
|
97
|
-
- **A pulse that fails** - its work errors, or it does not pass - is run once more. Failing again, it stops, and so does every pulse queued behind it on the same module, without running - the other modules' pulses go on. The harness then works out why: an agent that can read the project's
|
|
97
|
+
- **A pulse that fails** - its work errors, or it does not pass - is run once more. Failing again, it stops, and so does every pulse queued behind it on the same module, without running - the other modules' pulses go on. The harness then works out why: an agent that can read the project's \*\*\*plain, code, tests, testing scripts and logs, and turri's own source, skills and rules, says what happened, why, whether the fix belongs in \*\*\*plain, the environment, the code or turri itself, and exactly what to do.
|
|
98
98
|
- **When a pulse finishes** the transcript shows its line - `✓`, `✗` or `■`, its module, kind and number, how long it took and what it cost, never what it regenerated - `✓ shorty · conformance pulse 4 finished after 3m 47s · 488.6k tok · $0.02` - and the chat, which gets its whole report, reacts then and there, whatever else is running or queued, without waiting for you to speak: at once when it is idle, else as soon as the reply it is in ends. It does what the project's guidance says to do when a pulse finishes - release what was built, say - or tells you what finished and what it means. Ask to try it ("let me try it") and the chat releases what is built to `dist/<module>` and starts it on your machine, even while other pulses run. See [deploy](deploy.md). The chat reacts to it then and there, whatever else is running or queued, without waiting for you to speak - at once when it is idle, else as soon as the reply it is in ends: it does what the project's guidance says to do when a pulse finishes (release what was built, say), or tells you in a line or two what finished.
|
|
99
|
-
- **When a pulse stops** - a gate still failing after 20 attempts - it says where it stopped, why in plain words, and what to change in
|
|
99
|
+
- **When a pulse stops** - a gate still failing after 20 attempts - it says where it stopped, why in plain words, and what to change in \*\*\*plain - which section, and how. Running the same build again picks up from there.
|
|
100
100
|
|
|
101
101
|
In `print`, `json` and `rpc` modes the process ends when the answer is written, so the tools wait for the work there instead. Pulses live in memory for the session; the work itself is on disk.
|
|
102
102
|
|
|
103
103
|
## The project's config.turri.yaml
|
|
104
104
|
|
|
105
|
-
The chat reads the project's `config.turri.yaml` afresh every turn and follows it: its agent is told what it sets, and the
|
|
105
|
+
The chat reads the project's `config.turri.yaml` afresh every turn and follows it: its agent is told what it sets, and the tools that regenerate default to it - `module` when you ask to build without naming one, `conformance-tests: false` to regenerate without conformance tests (none written, none of the existing ones run) unless you ask for them, `coding-model` for what regenerations run on, and the testing scripts. `harness-model` is what `turri` starts on. What you ask for in the conversation wins. Every key, what happens without it, and how the models inherit: [the project config](config.md).
|
|
106
106
|
|
|
107
107
|
A project whose parts are on different stacks gives each stack its own `config.turri.<stack>.yaml` beside `config.turri.yaml` - `config.turri.backend.yaml`, `config.turri.frontend.yaml` - with its testing scripts, models and conformance over the project's. A stack named after its module builds it - `config.turri.backend.yaml` builds `backend.plain` - with nothing to choose, so a backend and a frontend are each built with their own, side by side, a brood's changes too; for a stack named otherwise the chat's model passes it as `stack`, or choose one for the session with `/stack backend`. The chat is told each stack's and which is chosen, and the status bar shows it. See [stacks](config.md#stacks).
|
|
108
108
|
|
|
@@ -110,7 +110,7 @@ A project with only a `config.yaml` - turri's file before it had its own name, o
|
|
|
110
110
|
|
|
111
111
|
## Choosing a model
|
|
112
112
|
|
|
113
|
-
There are three: the **harness model**, the one you chat with; the **coding model**, the one every
|
|
113
|
+
There are three: the **harness model**, the one you chat with; the **coding model**, the one every regeneration runs on - functionalities, conformance tests, repairs, \*\*\*plain changes; and the **judge model**, which gives the verdicts in a conformance run - the judge, which says whether the code, the tests or the \*\*\*plain description is wrong, and the test review. The project's `config.turri.yaml` can give each a model and a thinking level (off, minimal, low, medium, high, xhigh, max):
|
|
114
114
|
|
|
115
115
|
```yaml
|
|
116
116
|
harness-model:
|
|
@@ -125,35 +125,35 @@ judge-model:
|
|
|
125
125
|
|
|
126
126
|
Each may leave out either part, and a bare string (`coding-model: provider/id`, or `provider/id:high`) is a model alone. What one leaves out it takes from the one it inherits from: the judge model from the coding model, the coding model from the harness model - so with only a harness model, everything runs on it.
|
|
127
127
|
|
|
128
|
-
On the command line, `turri --harness-model <m> --coding-model <m>` wins over them (pi's own `--model` works for the harness model too), and in the conversation pi's `/model` changes the harness model and `engine`, below, the coding model. `model:`, from before there were two, is read as `coding-model`. With neither,
|
|
128
|
+
On the command line, `turri --harness-model <m> --coding-model <m>` wins over them ([pi](https://pi.dev)'s own `--model` works for the harness model too), and in the conversation [pi](https://pi.dev)'s `/model` changes the harness model and `engine`, below, the coding model. `model:`, from before there were two, is read as `coding-model`. With neither, regenerations run on the conversation's model.
|
|
129
129
|
|
|
130
|
-
Every
|
|
130
|
+
Every regeneration runs on the coding model unless you say otherwise. Say it in words - "regenerate \*\*\*plain changes with gpt 5.5", "build the functionalities on Gemini 3.6 Flash", "use Claude for everything" - and the agent calls `engine`, which holds for the rest of the session (a resumed one too):
|
|
131
131
|
|
|
132
132
|
| kind of work | covers |
|
|
133
133
|
| --- | --- |
|
|
134
|
-
|
|
|
135
|
-
| functionalities |
|
|
134
|
+
| \*\*\*plain changes | \*\*\*plain diff pulses, including the ones `brood` queues as you answer |
|
|
135
|
+
| functionalities | a functionality or a whole module regenerated, functionalities regenerated again, conformance |
|
|
136
136
|
| everything | both |
|
|
137
137
|
|
|
138
|
-
It works mid-interview as well: the skill's agent has the same tool. Pulses already queued keep the model they were queued with; `"chat"` drops the session's choice, back to the default coding model - `--coding-model`, `config.turri.yaml`, or the conversation's. `/engine` shows what each kind of work runs on. A
|
|
138
|
+
It works mid-interview as well: the skill's agent has the same tool. Pulses already queued keep the model they were queued with; `"chat"` drops the session's choice, back to the default coding model - `--coding-model`, `config.turri.yaml`, or the conversation's. `/engine` shows what each kind of work runs on. A tool that regenerates can also be given a model for one call.
|
|
139
139
|
|
|
140
|
-
Names are resolved strictly, because pi's own matching guesses and says nothing ("gpt-5" can land on a Bedrock model you have no key for). An exact `provider/model-id` is taken as it is. A looser name is taken when it fits exactly one model you have credentials for, or is exactly one model's id in other words ("gpt 5.5" is `openai/gpt-5.5`, not `gpt-5.5-pro`). Anything else comes back as a short list and the agent asks you which one; a model whose provider has no credentials is never picked.
|
|
140
|
+
Names are resolved strictly, because [pi](https://pi.dev)'s own matching guesses and says nothing ("gpt-5" can land on a Bedrock model you have no key for). An exact `provider/model-id` is taken as it is. A looser name is taken when it fits exactly one model you have credentials for, or is exactly one model's id in other words ("gpt 5.5" is `openai/gpt-5.5`, not `gpt-5.5-pro`). Anything else comes back as a short list and the agent asks you which one; a model whose provider has no credentials is never picked.
|
|
141
141
|
|
|
142
|
-
A provider a pi extension registers - your company's own LLM gateway, say - works for every agent the conversation starts, not only for the conversation. Those agents load no extensions (they would load a second copy of turri), so each is given the providers the conversation's extensions registered, as they are when it starts. An unattended run - `turri regenerate`, `render` and the rest, outside the chat - has no conversation to take them from: there, only the providers in pi's own configuration (`~/.pi/agent/models.json` and `auth.json`) are there, so a gateway those runs use has to be set up in `models.json`.
|
|
142
|
+
A provider a [pi](https://pi.dev) extension registers - your company's own LLM gateway, say - works for every agent the conversation starts, not only for the conversation. Those agents load no extensions (they would load a second copy of turri), so each is given the providers the conversation's extensions registered, as they are when it starts. An unattended run - `turri regenerate`, `render` and the rest, outside the chat - has no conversation to take them from: there, only the providers in [pi](https://pi.dev)'s own configuration (`~/.pi/agent/models.json` and `auth.json`) are there, so a gateway those runs use has to be set up in `models.json`.
|
|
143
143
|
|
|
144
|
-
To change pi's own default, in `~/.pi/agent/settings.json`:
|
|
144
|
+
To change [pi](https://pi.dev)'s own default, in `~/.pi/agent/settings.json`:
|
|
145
145
|
|
|
146
146
|
```json
|
|
147
147
|
{ "defaultProvider": "google", "defaultModel": "gemini-3.1-pro-preview" }
|
|
148
148
|
```
|
|
149
149
|
|
|
150
|
-
A provider failure - no credits, bad key, rate limit - is raised with the provider's own message, rather than surfacing as an agent that said nothing. One that passes - overloaded, rate limited, unreachable, timed out - is waited out first, after pi's own retries: six more tries, the wait doubled each time from 15 seconds up to five minutes (15s, 30s, 1m, 2m, 4m, 5m, a tenth either way so parallel jobs do not all come back at once), the agent carrying on where it was. Each wait - pi's own retries too - is shown as it happens - a notification in the chat and the job panel's status, an orange line in a headless run - and listed in the run report's `turn_incidents`. So is a provider stalling: a request to the model that nothing has come back on for two minutes (`TURRI_PROVIDER_STALL`, in seconds; 0 never), said once for each silence while the turn goes on waiting. A turn turri stops itself - its time up, the agent going round in a circle - is the work's normal course and is not said, only listed.
|
|
150
|
+
A provider failure - no credits, bad key, rate limit - is raised with the provider's own message, rather than surfacing as an agent that said nothing. One that passes - overloaded, rate limited, unreachable, timed out - is waited out first, after [pi](https://pi.dev)'s own retries: six more tries, the wait doubled each time from 15 seconds up to five minutes (15s, 30s, 1m, 2m, 4m, 5m, a tenth either way so parallel jobs do not all come back at once), the agent carrying on where it was. Each wait - [pi](https://pi.dev)'s own retries too - is shown as it happens - a notification in the chat and the job panel's status, an orange line in a headless run - and listed in the run report's `turn_incidents`. So is a provider stalling: a request to the model that nothing has come back on for two minutes (`TURRI_PROVIDER_STALL`, in seconds; 0 never), said once for each silence while the turn goes on waiting. A turn turri stops itself - its time up, the agent going round in a circle - is the work's normal course and is not said, only listed.
|
|
151
151
|
|
|
152
152
|
## Module history
|
|
153
153
|
|
|
154
|
-
Each `plain_modules/<module>` is a git repository whose git directory is `.plaingit` rather than `.git`, so it stays plain files to your own repository. A commit is made after every step that changes the module - a
|
|
154
|
+
Each `plain_modules/<module>` is a git repository whose git directory is `.plaingit` rather than `.git`, so it stays plain files to your own repository. A commit is made after every step that changes the module - a regeneration, a functionality regenerated, a conformance run, a regression repair, a \*\*\*plain change. `/lineage [module]` and `lineage` show it.
|
|
155
155
|
|
|
156
|
-
The last commit is also the last known good state. Every
|
|
156
|
+
The last commit is also the last known good state. Every regeneration, functionality regenerated, \*\*\*plain change and conformance run starts by putting the codebase it works in back to it, so if turri was stopped or restarted mid-pulse, whatever that pulse left half-written is undone first - and one that does not pass is undone the same way when it ends, commits and all. Only work that passed is committed. Installed dependencies and caches are not in the history and stay.
|
|
157
157
|
|
|
158
158
|
The history is turri's own: the hooks your git config names (`core.hooksPath` - gitleaks, husky) do not run on its commits. When work that passed cannot be committed all the same - a `.plaingit/index.lock` a git stopped mid-commit left, a full disk - the pulse does not land: the next start would undo it while turri took it as built. It is rolled back, and the stop says what git said and how to fix it. Any other step's commit failing leaves only a gap in the history. With git directly:
|
|
159
159
|
|
|
@@ -163,16 +163,16 @@ git --git-dir=plain_modules/<module>/.plaingit --work-tree=plain_modules/<module
|
|
|
163
163
|
|
|
164
164
|
## Agent logs
|
|
165
165
|
|
|
166
|
-
When an agent does something you cannot explain, its logs are where to look: the chat shows what its pulse is doing, not what its agents said. Every agent a
|
|
166
|
+
When an agent does something you cannot explain, its logs are where to look: the chat shows what its pulse is doing, not what its agents said. Every agent a regeneration starts - the coding agent, the conformance agent, the repair agent, the \*\*\*plain-steps planner - also streams its whole conversation to a file of its own.
|
|
167
167
|
|
|
168
|
-
The logs are in `.turri/debug/` in the project, beside `plain_modules`, one per agent, named by when it started and what it is: `2026-09-26T13-45-02-123Z-app-3-coding-agent.log`. Each starts with the pulse it runs for, its model and thinking level, its folder, its saved pi session, its tools and its system prompt, and then has, in order and stamped with the time: every prompt it is sent, whole; its thinking and replies as they stream; every tool call with its arguments, numbered
|
|
168
|
+
The logs are in `.turri/debug/` in the project, beside `plain_modules`, one per agent, named by when it started and what it is: `2026-09-26T13-45-02-123Z-app-3-coding-agent.log`. Each starts with the pulse it runs for, its model and thinking level, its folder, its saved [pi](https://pi.dev) session, its tools and its system prompt, and then has, in order and stamped with the time: every prompt it is sent, whole; its thinking and replies as they stream; every tool call with its arguments, numbered
|
|
169
169
|
- `→ #3 bash` - and its whole result under the same number, `← #3 bash done`, since calls made together run at once and finish in any order; a tool call a response was cut off in, marked as never run; each response's stop reason, tokens and cost; the provider's retries and errors; and how each turn ended. Every line is on disk as it happens, so `tail -f` follows an agent live and a run that crashed leaves its log up to that moment. The pulse's own log names each agent's log as it starts.
|
|
170
170
|
|
|
171
171
|
Beside them, each pulse has a log of its own in `.turri/jobs/` - `<time>-<pulse id>-<kind>.log`: its whole transcript, written as it happens. The harness's stages, the functional spec or the diff it builds, and every one of its agents in turn - each prompt, its thinking and replies, every tool call and its result, whole where the screen clips them - and how the pulse ended. A finished pulse, opened in the side panel, names it. It is the one to read for a pulse as a whole; an agent's own log has its system prompt, tokens and retries besides.
|
|
172
172
|
|
|
173
173
|
They are kept only in [debug mode](config.md#debug-mode) - `debug: true` in `config.turri.yaml`, or `TURRI_DEBUG=1`, or `turri --debug` for one run. Otherwise a pulse's logs are written while it runs - the diagnosis of a pulse that fails twice reads them, and quotes what it needs, never sending you to a file that is about to go - and removed as it ends, and an agent outside a pulse writes none; the [ledger](#the-ledger) still says what each pulse did, how it ended and what it cost. To read why an agent did something, turn debug mode on and run it again.
|
|
174
174
|
|
|
175
|
-
The coding and conformance agents cannot read the logs: they are outside `plain_modules` - or, for a `--build-dir` outside it, beside that folder rather than in it. The diagnosis of a failed pulse reads them. In debug mode nothing prunes them - delete the folder when you are done with it. A log that cannot be written is given up on without failing the
|
|
175
|
+
The coding and conformance agents cannot read the logs: they are outside `plain_modules` - or, for a `--build-dir` outside it, beside that folder rather than in it. The diagnosis of a failed pulse reads them. In debug mode nothing prunes them - delete the folder when you are done with it. A log that cannot be written is given up on without failing the regeneration.
|
|
176
176
|
|
|
177
177
|
## The ledger
|
|
178
178
|
|
|
@@ -184,7 +184,7 @@ It is in `.turri/ledger/` in the project, a JSON line an event, one file a run -
|
|
|
184
184
|
| --- | --- |
|
|
185
185
|
| runs | `run.started` (how it was started, turri's version and commit, the machine), `run.ended` |
|
|
186
186
|
| pulses | `job.queued`, `job.started`, every `job.stage`, `job.retried`, `job.finished` - its status, whether it landed, how long it ran, what it cost, how it ended |
|
|
187
|
-
| the work loop | `loop.halted`, `loop.stopped` (a pulse
|
|
187
|
+
| the work loop | `loop.halted`, `loop.stopped` (a pulse \*\*\*plain moved on from), `planner.failed`, `planner.fellBack` |
|
|
188
188
|
| the model | `model.call` (time and tokens), `model.turn`, `model.compaction`, `provider.retry`, `provider.stall`, `provider.error`, `turn.stopped` (a turn turri ended itself, and why) |
|
|
189
189
|
| tools and tests | `tool.call` (the tool, how long, whether it failed - never its arguments or result), `script.run` (the testing script, its exit, how long, whether the time limit stopped it, the failure's fingerprint, and `mutated` for a `can_it_fail` run on code broken on purpose) |
|
|
190
190
|
| the gates | `gate.round`, `review.verdict`, `arbiter.ruling`, `judge.verdict` |
|
|
@@ -201,7 +201,7 @@ It is for looking up, never for deciding: what to build comes from the records a
|
|
|
201
201
|
|
|
202
202
|
## From the command line
|
|
203
203
|
|
|
204
|
-
The same work, without a conversation. Run it from the directory that holds the
|
|
204
|
+
The same work, without a conversation. Run it from the directory that holds the module's \*\*\*plain, so its linked resources resolve the way the parser expects. For a long run - a whole module's \*\*\*plain overnight - see [running unattended](headless.md).
|
|
205
205
|
|
|
206
206
|
```bash
|
|
207
207
|
turri validate <file.plain> # validate a spec - no model, no API key
|
|
@@ -211,7 +211,7 @@ turri glimpse <file.plain> # plan it, and print the first job's prom
|
|
|
211
211
|
turri regenerate <file.plain> 2 # regenerate functionality 2 again, changed or not
|
|
212
212
|
turri regenerate <file.plain> --from-scratch # delete the module's code and tests (its lineage keeps them), regenerate all of it
|
|
213
213
|
turri regenerate <file.plain> --from-scratch-chain # the same for every module of its requires chain, from the bottom
|
|
214
|
-
turri
|
|
214
|
+
turri change-plain <file.plain> <diff> # apply a ***plain diff, and regenerate it
|
|
215
215
|
turri conform <file.plain> 1 # black-box tests for functionality 1
|
|
216
216
|
turri lineage <module> # what each regeneration changed in a module, and when
|
|
217
217
|
turri ledger [pulse] # what turri did here: the last runs and their pulses, or one pulse
|
|
@@ -13,7 +13,7 @@ description: >-
|
|
|
13
13
|
what they want alone, use its quick twin, `spawn`. Not for adding a
|
|
14
14
|
feature to an existing project (use `bud`) or for editing generated code (the .plain
|
|
15
15
|
specs are the source of truth).
|
|
16
|
-
allowed-tools: [read, write, edit, bash, ls, grep, find,
|
|
16
|
+
allowed-tools: [read, write, edit, bash, ls, grep, find, change_plain]
|
|
17
17
|
metadata:
|
|
18
18
|
# Started only by the person, with its command - /brood - never by the model on its own.
|
|
19
19
|
command-only: true
|
|
@@ -27,8 +27,8 @@ metadata:
|
|
|
27
27
|
# medium 5, thorough 10, best judgement as many as turri judges the phase needs.
|
|
28
28
|
questioning: [stack, product]
|
|
29
29
|
# Every answer in the product phase is a spec change: the next question waits until it is
|
|
30
|
-
# written with
|
|
31
|
-
|
|
30
|
+
# written with change_plain. Changes turri decides itself need no answer before them.
|
|
31
|
+
change-plain-per-answer: [product]
|
|
32
32
|
---
|
|
33
33
|
|
|
34
34
|
# Brood
|
|
@@ -39,9 +39,9 @@ Load the `***plain` syntax rules before writing anything — they are in the sys
|
|
|
39
39
|
|
|
40
40
|
Act as a `***plain` spec writer. The only output is `.plain` specification files — never code. Code is generated from the specs and lives in `plain_modules/` as a read-only artifact; never write or edit it. Frame every message to the user in terms of specs: "I'll add this to the spec," "Let me change the spec to fix that." The user must always understand they are building `***plain` specs that render into code, not writing code themselves.
|
|
41
41
|
|
|
42
|
-
In the product phase, every change you make to the specs is a **spec diff**: a git diff of the `.plain` files, written with `
|
|
42
|
+
In the product phase, every change you make to the specs is a **spec diff**: a git diff of the `.plain` files, written with `change_plain`, which applies it and starts building it. The spec diffs are the output of the interview; the builds follow from them.
|
|
43
43
|
|
|
44
|
-
The testing phase is the exception: it changes only `***test reqs***`, and comes before anything is built, so it writes them with the ordinary file tools and never with `
|
|
44
|
+
The testing phase is the exception: it changes only `***test reqs***`, and comes before anything is built, so it writes them with the ordinary file tools and never with `change_plain`. It comes before the product phase so that every spec change the product phase writes is conformance tested as it is built: with conformance on, each build writes and runs the change's conformance tests, and keeps every earlier one passing.
|
|
45
45
|
|
|
46
46
|
## Phase discipline
|
|
47
47
|
|
|
@@ -64,7 +64,7 @@ The first question of the intent phase is the harness's own, not yours: how many
|
|
|
64
64
|
|
|
65
65
|
At every level the questions reach all of the phase's topics; fewer questions only go less deep. The intent and testing phases are not capped. Follow-ups count; the closing confirmation of a phase - asked with `confirms_phase: true` - does not. Past the cap `ask_user` refuses the question. To stop sooner, or under best judgement once the phase is settled enough to build, call `end_questions`.
|
|
66
66
|
|
|
67
|
-
The level limits the questions, never the spec changes. In the product phase **every answer is a spec change**, whatever the level: written with `
|
|
67
|
+
The level limits the questions, never the spec changes. In the product phase **every answer is a spec change**, whatever the level: written with `change_plain` and built at once, before the next question - `ask_user` refuses the next one until it is. A broad answer that covers several features is several changes, one a feature, and you write them all. What the questions leave open - the details of a light answer, what is left once they are over - is yours to decide, as a careful engineer would, in the light of the intent brief: each decision a spec change of its own, written with `change_plain` with no answer needed before it, and said in one line. Write as many as the product needs. The phase's summary names every decision you made yourself, so the confirmation is where the person can change it.
|
|
68
68
|
|
|
69
69
|
## Question style
|
|
70
70
|
|
|
@@ -100,11 +100,11 @@ Read `references/stack.md` and walk its topics in order, within the questioning
|
|
|
100
100
|
|
|
101
101
|
Offer the standard library's stacks first, those in a language this machine can build now (the stdlib rule, in `<plain-rules>`, `<toolchains on this machine>`, and the first section of `references/stack.md`): a stack from it is one import, and only what it leaves open - storage, external services, other constraints - is asked and written.
|
|
102
102
|
|
|
103
|
-
Create the module structure and frontmatter so there is a `.plain` file to build into, and author every requirement into `***implementation reqs***` at the right scope — shared requirements into the template, module-specific ones into the module. Write these with the ordinary file tools; **nothing is built in this phase**, and `
|
|
103
|
+
Create the module structure and frontmatter so there is a `.plain` file to build into, and author every requirement into `***implementation reqs***` at the right scope — shared requirements into the template, module-specific ones into the module. Write these with the ordinary file tools; **nothing is built in this phase**, and `change_plain` is not used here.
|
|
104
104
|
|
|
105
105
|
Write no definitions, no functional specs, no `***test reqs***` and no `***acceptance tests***`.
|
|
106
106
|
|
|
107
|
-
End the phase with the unit tests: their framework, as a `:UnitTests:` requirement, and the unit testing script, which every build needs from the first one on. If turri already finds a script - named by `unittests-script:` in `config.turri.yaml` at the project root, or the one `run_unittests*.sh` in the root or in `test_scripts/` - keep it. Otherwise write `test_scripts/run_unittests_<language>.sh` with the `implement-unit-testing-script` skill, for the language and framework just settled. Do not link it from the specs. Without it, every build stops before it starts. When this module sits in a project beside another on a different stack - a frontend beside a backend, sharing only an imported module - its script is its stack's: name it in that stack's `config.turri.<stack>.yaml` beside `config.turri.yaml` (`unittests-script:`) - `config.turri.frontend.yaml` for the stack `frontend` - not in `config.turri.yaml`, which is the project's and holds what the stacks share. Name the stack after this module - `config.turri.frontend.yaml` for `frontend.plain` - and it is built with it by itself, with nothing to choose. A stack the project already has under another name is passed as `stack` to `
|
|
107
|
+
End the phase with the unit tests: their framework, as a `:UnitTests:` requirement, and the unit testing script, which every build needs from the first one on. If turri already finds a script - named by `unittests-script:` in `config.turri.yaml` at the project root, or the one `run_unittests*.sh` in the root or in `test_scripts/` - keep it. Otherwise write `test_scripts/run_unittests_<language>.sh` with the `implement-unit-testing-script` skill, for the language and framework just settled. Do not link it from the specs. Without it, every build stops before it starts. When this module sits in a project beside another on a different stack - a frontend beside a backend, sharing only an imported module - its script is its stack's: name it in that stack's `config.turri.<stack>.yaml` beside `config.turri.yaml` (`unittests-script:`) - `config.turri.frontend.yaml` for the stack `frontend` - not in `config.turri.yaml`, which is the project's and holds what the stacks share. Name the stack after this module - `config.turri.frontend.yaml` for `frontend.plain` - and it is built with it by itself, with nothing to choose. A stack the project already has under another name is passed as `stack` to `change_plain`.
|
|
108
108
|
|
|
109
109
|
The unit testing script runs the static checks too - the formatter, linter and type checker the stack names, which count as the unit tests do; `implement-unit-testing-script` says which for each language. There is no separate static checks script.
|
|
110
110
|
|
|
@@ -118,7 +118,7 @@ Read `references/testing.md`. Carry the intent brief and the stack in; ask about
|
|
|
118
118
|
|
|
119
119
|
First the decision: are there conformance tests at all? Ask it explicitly - even when `config.turri.yaml` already says, ask, offering what it says first - and write the answer to `config.turri.yaml` as `conformance-tests:` - the chat and every build follow it. A project with only a `config.yaml` has it copied to `config.turri.yaml` first; never write `config.yaml`. Off, and the phase is done once the user confirms: the product phase's builds then write and run no conformance tests.
|
|
120
120
|
|
|
121
|
-
On: when the stack came from the standard library, import its conformance template (`stdlib/<kind>/conformance/<stack>`) and ask only about what it does not settle. Each answer about how the tests are written and run becomes a `:ConformanceTests:` requirement in `***test reqs***`, written with the ordinary file tools. A change to the test reqs is not a spec diff: never use `
|
|
121
|
+
On: when the stack came from the standard library, import its conformance template (`stdlib/<kind>/conformance/<stack>`) and ask only about what it does not settle. Each answer about how the tests are written and run becomes a `:ConformanceTests:` requirement in `***test reqs***`, written with the ordinary file tools. A change to the test reqs is not a spec diff: never use `change_plain` in this phase. Then make sure the project has its conformance testing script - `implement-conformance-testing-script` writes it into `test_scripts/` when turri finds none - and run `validate` on what you changed. Without both, the first build of the product phase stops before it starts.
|
|
122
122
|
|
|
123
123
|
Finish by summarizing the decision, the test reqs and the script, and saying that from the first spec change on, every build writes and runs its conformance tests. Get explicit confirmation.
|
|
124
124
|
|
|
@@ -131,10 +131,10 @@ Walk its topics in order, and run this loop while the questioning level allows (
|
|
|
131
131
|
1. **Ask** one question with `ask_user` - about a feature, a step of a flow, a rule; never about a spec section.
|
|
132
132
|
2. The user **answers**.
|
|
133
133
|
3. **Make the spec change** the answer calls for, as one git diff of the `.plain` files - across every section it needs: the concepts it introduces, a policy it states, the functional spec it adds or changes.
|
|
134
|
-
4. **Write it with `
|
|
134
|
+
4. **Write it with `change_plain`**, which applies it and starts a render spec diff job for it, whatever it touches. With conformance on, that build also writes and runs the change's conformance tests. An answer that covers several features is a change for each: write them all. Then the changes for what you decide yourself around it, if any.
|
|
135
135
|
5. Move straight on to the next question; never wait for a build.
|
|
136
136
|
|
|
137
|
-
Every answer, at least one spec change and one render spec diff job, written before the next question - at every level. Once the questions are over, write what is left of the product the same way - one decision of yours, one spec change - with no answer between. Correct something already written with a diff and `fixes_earlier`, before the next question. Never change a `.plain` file in this phase by any other means than `
|
|
137
|
+
Every answer, at least one spec change and one render spec diff job, written before the next question - at every level. Once the questions are over, write what is left of the product the same way - one decision of yours, one spec change - with no answer between. Correct something already written with a diff and `fixes_earlier`, before the next question. Never change a `.plain` file in this phase by any other means than `change_plain`.
|
|
138
138
|
|
|
139
139
|
Report each build result between questions, in one sentence. Use `drift` when the user asks how the builds are going.
|
|
140
140
|
|
|
@@ -8,8 +8,8 @@ Carry the confirmed intent brief and the settled stack in. Do not restart either
|
|
|
8
8
|
|
|
9
9
|
The questioning level caps this phase's questions (light 3, medium 5, thorough 10, or best judgement - `SKILL.md`, "How many questions"), and so sets how broad each is. Every level walks all the topics below: light in a few broad questions - the features and their order in one, what they do end to end in another, with what fits the brief proposed for the person to confirm or correct; thorough in narrow ones, a feature, rule or edge case each. The level limits the questions, never the spec changes:
|
|
10
10
|
|
|
11
|
-
- **Every answer is a spec change**, at every level: written with `
|
|
12
|
-
- **What the questions leave open is yours** - the details under a broad answer, and everything still open once the questions are over (the cap reached, or `end_questions` called). Each feature, edge case and rule you decide is its own spec change, written with `
|
|
11
|
+
- **Every answer is a spec change**, at every level: written with `change_plain` and built at once, before the next question - `ask_user` refuses the next question until it is. A broad answer covering several features is a change for each.
|
|
12
|
+
- **What the questions leave open is yours** - the details under a broad answer, and everything still open once the questions are over (the cap reached, or `end_questions` called). Each feature, edge case and rule you decide is its own spec change, written with `change_plain` with no answer needed before it, and said in one line. Write as many as the product needs. Decide from the intent brief and the answers so far; leave out what they do not call for rather than invent scope.
|
|
13
13
|
|
|
14
14
|
## One question, one answer, a spec change, a render
|
|
15
15
|
|
|
@@ -20,13 +20,13 @@ Every iteration is exactly this, at every questioning level:
|
|
|
20
20
|
- a concept the feature introduces, into `***definitions***`, following `add-concept`, defined in the same change as the functional spec that first uses it;
|
|
21
21
|
- a technology-free policy the answer states, into `***implementation reqs***`;
|
|
22
22
|
- the functional spec for the feature, or the change to one already written, following `add-functional-spec`. Write it as a git diff of the `.plain` file(s): `diff --git a/<file> b/<file>`, `--- a/<file>`, `+++ b/<file>`, then the hunks with a few lines of unchanged context. Paths are relative to the project root.
|
|
23
|
-
3. **Write it with `
|
|
23
|
+
3. **Write it with `change_plain`.** It applies the diff and starts a render spec diff job for it in the same call - whatever the change touches, a definition alone included. Write every change the answer calls for before the next question - `ask_user` refuses it until then - and any you decide yourself beside them. Move straight on to the next question; never wait for a build.
|
|
24
24
|
|
|
25
25
|
**One answer, at least one change.** A follow-up about the same feature - an edge case, a validation rule - is its own answer and its own change to that feature's spec. An answer that describes two features is two changes, both written before the next question. A change never carries both an answer and something you decided: what you decide is a change of its own.
|
|
26
26
|
|
|
27
27
|
**Fix contradictions in place.** When a later answer refines or contradicts something already written, write the correction as its own diff with `fixes_earlier` set, before the next question. It is rendered like any other change.
|
|
28
28
|
|
|
29
|
-
**Never edit the `.plain` files with `write` or `edit` in this phase.** A change that does not go through `
|
|
29
|
+
**Never edit the `.plain` files with `write` or `edit` in this phase.** A change that does not go through `change_plain` is a change nothing renders.
|
|
30
30
|
|
|
31
31
|
## What a change may contain
|
|
32
32
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# the testing phase — How is what was built checked from the outside?
|
|
2
2
|
|
|
3
|
-
Decide whether the project has conformance tests, and if it does, say in `***test reqs***` how they are written and run, and give it the script that runs them. This comes before the product phase, and nothing here is built: a change to the test reqs is not a spec diff, so it is written with the ordinary file tools, never `
|
|
3
|
+
Decide whether the project has conformance tests, and if it does, say in `***test reqs***` how they are written and run, and give it the script that runs them. This comes before the product phase, and nothing here is built: a change to the test reqs is not a spec diff, so it is written with the ordinary file tools, never `change_plain`. What is settled here is what every build of the product phase follows - with conformance on, each spec change is built with its conformance tests. Run the core loop for each topic below, in order: ask, then write. When the user has no preference, propose a default that fits the stack and the intent brief and ask them to confirm.
|
|
4
4
|
|
|
5
5
|
Conformance tests are black-box tests written from the spec: for each functional spec, tests that run the built software from the outside - as a command, over HTTP, through its public entry point - and check it does what the spec says, kept apart from the spec's acceptance tests. The harness runs the whole suite after every spec change, so they catch one change breaking another. They cost time and tokens on every build.
|
|
6
6
|
|
|
@@ -11,7 +11,7 @@ description: >-
|
|
|
11
11
|
is left to build is worked out again from the spec. Use when the user wants to add a
|
|
12
12
|
feature to an existing project. Not for starting a new project (use `brood`, or `spawn`
|
|
13
13
|
to start fast) or for editing generated code (the .plain specs are the source of truth).
|
|
14
|
-
allowed-tools: [read, write, edit, bash, ls, grep, find,
|
|
14
|
+
allowed-tools: [read, write, edit, bash, ls, grep, find, change_plain]
|
|
15
15
|
metadata:
|
|
16
16
|
# Started only by the person, with its command - /bud - never by the model on its own.
|
|
17
17
|
command-only: true
|
|
@@ -24,8 +24,8 @@ metadata:
|
|
|
24
24
|
# medium 5, thorough 10, best judgement as many as turri judges the phase needs.
|
|
25
25
|
questioning: [product]
|
|
26
26
|
# Every answer in the product phase is a spec change: the next question waits until it is
|
|
27
|
-
# written with
|
|
28
|
-
|
|
27
|
+
# written with change_plain. Changes turri decides itself need no answer before them.
|
|
28
|
+
change-plain-per-answer: [product]
|
|
29
29
|
---
|
|
30
30
|
|
|
31
31
|
# Bud
|
|
@@ -34,7 +34,7 @@ Load the `***plain` syntax rules before writing anything - they are in the syste
|
|
|
34
34
|
|
|
35
35
|
## Role
|
|
36
36
|
|
|
37
|
-
Act as a `***plain` spec writer adding one feature to specs that already exist - and, usually, to code already built from them, or being built. The only output is changes to `.plain` files, and every one of them is a **spec diff**: a git diff of the `.plain` files, written with `
|
|
37
|
+
Act as a `***plain` spec writer adding one feature to specs that already exist - and, usually, to code already built from them, or being built. The only output is changes to `.plain` files, and every one of them is a **spec diff**: a git diff of the `.plain` files, written with `change_plain`, which applies it and queues its render spec diff job. Never write code, and never change a `.plain` file by any other means. Frame every message in terms of specs: "I'll add this to the spec."
|
|
38
38
|
|
|
39
39
|
This is `brood`'s product phase, scoped to one feature, on top of what is there: the stack, the testing setup and every earlier feature are settled - read them, build on them, restart none.
|
|
40
40
|
|
|
@@ -51,7 +51,7 @@ The first question of the intent phase is the harness's own, not yours: how many
|
|
|
51
51
|
|
|
52
52
|
The intent phase is not capped. Follow-ups count; the closing confirmation - asked with `confirms_phase: true` - does not. Past the cap `ask_user` refuses the question. To stop sooner, or under best judgement once the feature is settled enough to build, call `end_questions`.
|
|
53
53
|
|
|
54
|
-
The level limits the questions, never the spec changes. **Every answer is a spec change**, whatever the level: written with `
|
|
54
|
+
The level limits the questions, never the spec changes. **Every answer is a spec change**, whatever the level: written with `change_plain` and built at once, before the next question - `ask_user` refuses the next one until it is. A broad answer that covers several behaviours is several changes, and you write them all. What the questions leave open - the details of a light answer, what is left once they are over - is yours to decide, as a careful engineer would, in the light of the brief: each decision a spec change of its own, written with `change_plain` with no answer needed before it, and said in one line. The closing summary names every decision you made yourself, so the confirmation is where the person can change it.
|
|
55
55
|
|
|
56
56
|
## Question style
|
|
57
57
|
|
|
@@ -91,7 +91,7 @@ The feature, walked question by question. Run this loop while the questioning le
|
|
|
91
91
|
- a policy it states - error handling, a retry, a data format, a library not already in the module or its imports - into `***implementation reqs***`, the way `add-implementation-requirement` says;
|
|
92
92
|
- the functional spec it adds, or the change to one already written, the way `add-functional-spec` says: its wording rules, its complexity check (`analyze-if-func-spec-too-complex` - at most 200 lines of code; a spec too big is split *with the user*, as the next question) and its conflict check (`analyze-func-specs`, over the new spec and every existing one in the chain). Follow those skills for what the text is; the diff is the only thing that writes it.
|
|
93
93
|
- with conformance tests on - test reqs of the module's own or imported, and a conformance testing script turri finds - an acceptance test under its functional spec when the answer states one concrete verification (`add-acceptance-test`), and a test req only when the answer changes how conformance tests run (`add-test-requirement`). With conformance off, none. Write it as a git diff: `diff --git a/<file> b/<file>`, `--- a/<file>`, `+++ b/<file>`, then the hunks with a few lines of unchanged context, paths from the project root. A new functional spec goes where it belongs in build order - after what it builds on, usually at the end.
|
|
94
|
-
4. **Write it with `
|
|
94
|
+
4. **Write it with `change_plain` at once**, before anything else. It applies the diff and starts a render spec diff job for it; with conformance on, its conformance tests follow it. An answer that covers several behaviours is a change for each: write them all. Then the changes for what you decide yourself around it, if any. Say in one line what went into the spec - "added `:Note:` and functional spec 7" - and what became of the build, as the tool says, then ask the next question. Never wait for a build, and never ask the user to approve a change.
|
|
95
95
|
|
|
96
96
|
**One answer, at least one change.** A follow-up about the same behaviour - an edge case, a validation rule - is its own answer and its own change. An answer that describes two behaviours is two changes, both written before the next question. A change never carries both an answer and something you decided: what you decide is a change of its own.
|
|
97
97
|
|
|
@@ -11,7 +11,7 @@ description: >-
|
|
|
11
11
|
is stuck on something the spec cannot settle. Use when the user has a rough or hand-written spec
|
|
12
12
|
and wants it made to build without steering it. Not for starting from nothing (`brood`,
|
|
13
13
|
`spawn`) or adding a feature (`bud`).
|
|
14
|
-
allowed-tools: [read, write, edit, bash, ls, grep, find,
|
|
14
|
+
allowed-tools: [read, write, edit, bash, ls, grep, find, change_plain, regenerate, drift, halt, watch_jobs, spec_findings]
|
|
15
15
|
metadata:
|
|
16
16
|
# What each phase does, in a line - shown under the skill's progress while it runs.
|
|
17
17
|
phases:
|
|
@@ -37,14 +37,14 @@ Act as a `***plain` spec writer who hardens a spec by building it, on your own,
|
|
|
37
37
|
Where a spec is silent, the agent that renders it has to decide - a format, a default, an error, a rule - and when the decision is hard, or the spec reads two ways, it struggles: its tests keep failing, the judge keeps finding the tests or the code wrong, it runs far too long. Much of that is the spec's to settle, and once the spec says it, the next build does not struggle. Some of it is not
|
|
38
38
|
- a bug the agent will fix, an environment problem, turri itself - and the spec stays out of that.
|
|
39
39
|
|
|
40
|
-
You never ask the user: this runs on its own. Where the spec leaves a real choice, take the reading that best fits the rest of the spec, the intent it states and what the builds already did, write it, and record the decision for the report. You never change what the spec plainly means, never write the spec to match code that does something else, never add features, never touch generated code, and never edit a `.plain` file with the file tools once rendering has started - every change is a spec diff, with `
|
|
40
|
+
You never ask the user: this runs on its own. Where the spec leaves a real choice, take the reading that best fits the rest of the spec, the intent it states and what the builds already did, write it, and record the decision for the report. You never change what the spec plainly means, never write the spec to match code that does something else, never add features, never touch generated code, and never edit a `.plain` file with the file tools once rendering has started - every change is a spec diff, with `change_plain`.
|
|
41
41
|
|
|
42
42
|
## Steps
|
|
43
43
|
|
|
44
44
|
### intake
|
|
45
45
|
|
|
46
46
|
1. Find the spec: the `.plain` file(s) the user named, or the project's modules. Read them whole, with their `import` and `requires` chains, and the project's `config.turri.yaml` (or the old `config.yaml`, when there is no `config.turri.yaml`) and each stack's `config.turri.<stack>.yaml`.
|
|
47
|
-
2. Run `validate` on each, and fix only what stops it parsing - with the file tools, while nothing is built from it yet, otherwise with `
|
|
47
|
+
2. Run `validate` on each, and fix only what stops it parsing - with the file tools, while nothing is built from it yet, otherwise with `change_plain`.
|
|
48
48
|
3. Make sure a build can start: the unit testing script (and, with conformance tests on, the conformance testing script) turri finds. When one is missing, write it by following the `implement-unit-testing-script` (or `implement-conformance-testing-script`) skill, for the stack the spec's implementation reqs name. The static checks - formatter, linter, type checker - are part of the unit testing script, as that skill writes it.
|
|
49
49
|
|
|
50
50
|
Say in two lines what will be rendered, and go on.
|
|
@@ -61,7 +61,7 @@ Run cycles until the goal is met - or you are stuck - and at most twenty. Each c
|
|
|
61
61
|
- **Not the spec's** - a bug the agent is working through, a missing tool or package, a flaky service, turri itself. Leave the build to it, note it for the report, and keep watching. Stopping it would throw its work away for nothing.
|
|
62
62
|
5. **When the builds finish**, read `spec_findings`, and write into the spec every assumption about what the software does that a user, a caller or a test can see - as the build assumed it, unless that is not what the spec meant - so the next build does not have to guess. An assumption about how it is built inside - a library, a file layout, a name - stays out. A diagnosis that says the fix is in the spec is written; one that says code, environment or turri is noted. Then the next cycle: what failed, and what the new diffs changed, is rendered again.
|
|
63
63
|
|
|
64
|
-
**Writing a fix.** A git diff of the `.plain` file(s), paths from the project root, a few lines of context, with `
|
|
64
|
+
**Writing a fix.** A git diff of the `.plain` file(s), paths from the project root, a few lines of context, with `change_plain`. One diff per concern - a functionality's missing rules together, a concept and what it means together - never one diff for everything. Put each where it belongs: a rule of one functionality into its functional spec, a policy for many into `***implementation reqs***`, a term into `***definitions***`, following `add-concept`, `add-functional-spec` and `add-implementation-requirement` for how it is written. Each diff is built as it is written: what is left to build is worked out again from the spec - only what it changes is rendered again.
|
|
65
65
|
|
|
66
66
|
**When you are stuck, stop.** The same functionality struggling three times after you clarified the spec for it is not something the spec can settle: stop maturing it, and report it. Two cycles in a row with nothing newly built and nothing newly settled: stop, and report what is open. Twenty cycles: stop, and report.
|
|
67
67
|
|