@turri-dev/cli 0.1.11-next.056c7c7 → 0.1.11-next.3495500

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/docs/sandbox.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # The sandbox
2
2
 
3
- A rendering agent is building one module. It has no business reading the `.plain` specs it was not given, the rest of the project, or anything else on the machine - and [pi](https://pi.dev)'s `cwd` is only where relative paths start: an absolute path resolves to itself and `bash` can `cd` anywhere. So turri fences its agents: their file tools check every path, and every command they run on this machine passes a command check, runs in an environment of the module's own behind an OS sandbox where the OS has one, and is audited afterwards. The rules live in code that runs the same on every platform; the OS sandbox is an extra wall under them.
3
+ A regenerating agent is building one module. It has no business reading \*\*\*plain it was not given, the rest of the project, or anything else on the machine - and [pi](https://pi.dev)'s `cwd` is only where relative paths start: an absolute path resolves to itself and `bash` can `cd` anywhere. So turri fences its agents: their file tools check every path, and every command they run on this machine passes a command check, runs in an environment of the module's own behind an OS sandbox where the OS has one, and is audited afterwards. The rules live in code that runs the same on every platform; the OS sandbox is an extra wall under them.
4
4
 
5
5
  ## What an agent can reach
6
6
 
@@ -14,14 +14,14 @@ Each agent's file tools reach its own module and only the parts its role needs:
14
14
  | Explorers | its module's `code/` | nothing |
15
15
  | Test review | its module's `tests/` and `code/` | nothing |
16
16
 
17
- Nothing else exists for them: not another module, not turri's scratch area `plain_modules/.turri` (a test run's whole output reaches them through `read_output`, not by path), not the module's lineage `.plaingit`, and nothing above `plain_modules`. The specs it is building from reach it only through the prompt the harness writes. Nor does any context file: [pi](https://pi.dev) puts every `AGENTS.md` / `CLAUDE.md` from an agent's folder up into its system prompt, and turri turns that off for every agent it starts - so the project's own `AGENTS.md`, or yours in `~/.pi/agent`, never reaches a rendering agent, a judge or a skill's agent. Nor do [pi](https://pi.dev)'s skills, which [pi](https://pi.dev) would list for an agent to load: each agent turri starts has one job, and turri runs its own gated skills itself. The conversation you talk to still loads both, as [pi](https://pi.dev) does - but for the skills turri has already, its own and any about [\*\*\*plain](https://plainlang.org/docs/) (see [a project's own skills](skills-and-gates.md#a-projects-own-skills)).
17
+ Nothing else exists for them: not another module, not turri's scratch area `plain_modules/.turri` (a test run's whole output reaches them through `read_output`, not by path), not the module's lineage `.plaingit`, and nothing above `plain_modules`. The \*\*\*plain description it is building from reaches it only through the prompt the harness writes. Nor does any context file: [pi](https://pi.dev) puts every `AGENTS.md` / `CLAUDE.md` from an agent's folder up into its system prompt, and turri turns that off for every agent it starts - so the project's own `AGENTS.md`, or yours in `~/.pi/agent`, never reaches a regenerating agent, a judge or a skill's agent. Nor do [pi](https://pi.dev)'s skills, which [pi](https://pi.dev) would list for an agent to load: each agent turri starts has one job, and turri runs its own gated skills itself. The conversation you talk to still loads both, as [pi](https://pi.dev) does - but for the skills turri has already, its own and any about [\*\*\*plain](https://plainlang.org/docs/) (see [a project's own skills](skills-and-gates.md#a-projects-own-skills)).
18
18
 
19
19
  ## Every agent knows its walls
20
20
 
21
21
  Each agent's system prompt says where its walls are, in words generated from its fence, so they say exactly what is enforced and are the same on every platform:
22
22
 
23
23
  - its own folder or folders, by absolute path, and what its file tools read and write there - a role that only reads is told it writes nothing, "not with your file tools, not with your shell"; the conformance agent that it reads the code under test but writes tests, not code; the code fixer that it reads the conformance tests but fixes the code;
24
- - that its shell runs on this machine, in its folder, with this machine's toolchains, a home folder and a temporary folder of its own (`$HOME`, `$TMPDIR`), the package caches and an open network, and that nothing else exists for it - not the specs, not the project's other folders or modules, not your home;
24
+ - that its shell runs on this machine, in its folder, with this machine's toolchains, a home folder and a temporary folder of its own (`$HOME`, `$TMPDIR`), the package caches and an open network, and that nothing else exists for it - not \*\*\*plain, not the project's other folders or modules, not your home;
25
25
  - what happens on a reach outside, matter-of-fact: a command that names a path outside is refused before it runs, and anything a command changes outside is put back, and it is told;
26
26
  - that the project's dependencies are its own, through the build manifest, and installing anything else is not: what the machine lacks it reports with `report_environment_error`, naming what is missing, and goes no further with what depends on it (see [when the environment lacks something](regenerating.md#when-the-environment-lacks-something));
27
27
  - that it may stop only what it started, and that a port something else holds means another port; that `ps` may show nothing but what it started, so a server is checked with `kill -0 $!`, its log or a request;
@@ -82,11 +82,11 @@ Under the check, where the OS offers one, the command runs behind an OS sandbox
82
82
 
83
83
  ### The audit
84
84
 
85
- After every agent command, on every platform - a sandbox up or not - turri puts back what the command's role may not change inside the project (`src/regenerate/audit.ts`): the specs, `config*.yaml`, the testing scripts, every credential file (the agent's own folder included), the code record, the module's lineage `.plaingit` (its refs and settings), the other modules, the folders the role only reads, and the part of its own module it does not write - the coding agent's conformance tests. A changed file is put back, a new one removed, a deleted one brought back: from a copy kept before the command, or - another module - as its last commit has it, unless a pulse is at work in that module, whose writes are not this command's to undo (left, and logged). Nor is a change the conversation made while the command ran: the chat's `edit` and `write`, a spec diff it applied, or anything while a command of its own ran - the chat editing a spec beside a pulse is the person's change, not the agent's, so it is left and logged (`src/regenerate/conversation-writes.ts`). The agents of a pulse start without extensions, so nothing of theirs is marked so. The agent is told in its command's result what was put back; the job's log gets a line; you are not told, and the pulse goes on. It compares sizes and modification times, skipping dependencies and build output, and contents only where those differ - about 35 ms on a project of 3000 files. Outside the project nothing can be put back: that is the OS sandbox's, and on Windows an accepted gap.
85
+ After every agent command, on every platform - a sandbox up or not - turri puts back what the command's role may not change inside the project (`src/regenerate/audit.ts`): \*\*\*plain, `config*.yaml`, the testing scripts, every credential file (the agent's own folder included), the code record, the module's lineage `.plaingit` (its refs and settings), the other modules, the folders the role only reads, and the part of its own module it does not write - the coding agent's conformance tests. A changed file is put back, a new one removed, a deleted one brought back: from a copy kept before the command, or - another module - as its last commit has it, unless a pulse is at work in that module, whose writes are not this command's to undo (left, and logged). Nor is a change the conversation made while the command ran: the chat's `edit` and `write`, a \*\*\*plain diff it applied, or anything while a command of its own ran - the chat editing \*\*\*plain beside a pulse is the person's change, not the agent's, so it is left and logged (`src/regenerate/conversation-writes.ts`). The agents of a pulse start without extensions, so nothing of theirs is marked so. The agent is told in its command's result what was put back; the job's log gets a line; you are not told, and the pulse goes on. It compares sizes and modification times, skipping dependencies and build output, and contents only where those differ - about 35 ms on a project of 3000 files. Outside the project nothing can be put back: that is the OS sandbox's, and on Windows an accepted gap.
86
86
 
87
87
  ## The web
88
88
 
89
- Every rendering agent - render, spec change, conformance, regression repair - can also search the web and read a page, for a library's documentation or an error it has not seen:
89
+ Every regenerating agent - regenerate, \*\*\*plain change, conformance, regression repair - can also search the web and read a page, for a library's documentation or an error it has not seen:
90
90
 
91
91
  | Tool | What it does |
92
92
  | --- | --- |
@@ -99,7 +99,7 @@ DuckDuckGo is used without a key through its Lite page, which is not an official
99
99
 
100
100
  ## Outside the sandbox: deploy
101
101
 
102
- What `deploy` starts runs on your machine unfenced: you asked to try the software, with your toolchains, in your browser. It runs from `dist/<module>`, a copy of the last commit outside `plain_modules`, so no rendering agent reaches it. Nothing is installed there without your say-so - every command that would install or download is put to you first, and the chat's shell refuses the rest. See [deploy](deploy.md).
102
+ What `deploy` starts runs on your machine unfenced: you asked to try the software, with your toolchains, in your browser. It runs from `dist/<module>`, a copy of the last commit outside `plain_modules`, so no regenerating agent reaches it. Nothing is installed there without your say-so - every command that would install or download is put to you first, and the chat's shell refuses the rest. See [deploy](deploy.md).
103
103
 
104
104
  ## What turri keeps beside the code
105
105
 
@@ -2,16 +2,16 @@
2
2
 
3
3
  A skill's review step is normally a paragraph asking the agent to check its own work. Agents talk their way past paragraphs. turri makes the review an edge in a graph instead: the harness decides when a gate runs, spawns the judges itself, and branches on a structured verdict. The model's only job is producing the artifact under review.
4
4
 
5
- [pi](https://pi.dev) supplies the agent runtime (sessions, tools, multi-provider models); LangGraph supplies the control flow. The gate syntax lives in `SKILL.md`, so a gated skill still loads in any spec-compliant harness - it just runs ungated there.
5
+ [pi](https://pi.dev) supplies the agent runtime (sessions, tools, multi-provider models); LangGraph supplies the control flow. The gate syntax lives in `SKILL.md`, so a gated skill still loads in any harness that follows the Agent Skills standard - it just runs ungated there.
6
6
 
7
7
  ## The syntax
8
8
 
9
- Under `metadata:`, which the Agent Skills spec reserves for arbitrary key-values:
9
+ Under `metadata:`, which the Agent Skills standard reserves for arbitrary key-values:
10
10
 
11
11
  ```yaml
12
12
  ---
13
- name: draft-spec
14
- description: Drafts a ***plain spec, judged before it is kept.
13
+ name: draft-plain
14
+ description: Drafts ***plain, judged before it is kept.
15
15
  allowed-tools: [read, write, edit, bash, ls, grep, find]
16
16
  metadata:
17
17
  gates:
@@ -39,21 +39,21 @@ A skill declares its steps with a `## Steps` section whose `###` headings are th
39
39
 
40
40
  `command-only: true` under `metadata:` keeps a skill from the model: it is started only by the person, with its slash command - `/brood`, `/spawn` and `/bud` are. turri registers the command for every such skill, the project's own too. Typed, the command puts what was typed in the chat as the person's message, and lets `cultivate` start the skill once; the run starting uses that up, so the model cannot start it again later on its own. Without the command, `cultivate` refuses it and the model is told to name the command to the person instead - the skill's line in `cultivate`'s list says so too.
41
41
 
42
- `paced: false` under `metadata:` lets a skill write spec changes one after another on its own: `write_spec_diff` otherwise refuses a new change until the user has answered a question since the last one - one answer, one change, as an interview writes them. `spawn` is unpaced: the tool tells it so, and after each change to write the next at once, not to carry on with an interview it does not have.
42
+ `paced: false` under `metadata:` lets a skill write \*\*\*plain changes one after another on its own: `change_plain` otherwise refuses a new change until the user has answered a question since the last one - one answer, one change, as an interview writes them. `spawn` is unpaced: the tool tells it so, and after each change to write the next at once, not to carry on with an interview it does not have.
43
43
 
44
44
  `questioning:` under `metadata:` names the steps the questioning level caps (`src/questioning.ts`) - `brood`'s `[stack, product]`, `bud`'s `[product]`. When there are any and someone to ask, the harness itself asks the first question of the first step: how many questions those steps may ask - light (3), medium (5), thorough (10) or best judgement - and tells the agent the answer beside each step's instructions, with how broad each question is to be: broad at light, a whole area at once, narrow at thorough, an edge case at a time. Every level covers the same ground, in less or more depth. In a capped step `ask_user` refuses a question past the cap; the step's closing confirmation, asked with `confirms_phase: true`, is never counted. `end_questions` ends the step's questions sooner - best judgement's only end. What the questions leave open, the agent decides itself and says what it chose. The level, and how many questions each step has asked, are kept in the run's `run.json`, so a resumed run keeps them. A step it does not name, or a run with nobody to ask, is not capped.
45
45
 
46
- `spec-diff-per-answer:` under `metadata:` names the steps where every answer is a spec change - `brood`'s and `bud`'s `[product]`, at every questioning level. There `ask_user` refuses the next question until the last answer is written with `write_spec_diff` (a confirmation, or a question dismissed, owes nothing), and `write_spec_diff` no longer waits for an answer before a change: the agent writes as many as the step needs, those it decides itself included. The level limits the questions, never the spec changes.
46
+ `change-plain-per-answer:` under `metadata:` names the steps where every answer is a \*\*\*plain change - `brood`'s and `bud`'s `[product]`, at every questioning level. There `ask_user` refuses the next question until the last answer is written with `change_plain` (a confirmation, or a question dismissed, owes nothing), and `change_plain` no longer waits for an answer before a change: the agent writes as many as the step needs, those it decides itself included. The level limits the questions, never the \*\*\*plain changes.
47
47
 
48
48
  A step ends when its agent's turn does. A turn that ends on a question in the reply would end the step there, half done, on a question no one reads - the person sees only what `ask_user` asks. So such a step is sent back, twice at most: to ask with `ask_user` if the question is truly the person's, or, in an unpaced skill, to decide it itself - then to carry on until the step is done.
49
49
 
50
50
  `phases:` under `metadata:` says what each step does, in a short line a step by its name - for the person, not the agent: while the skill runs, the line above the input shows its phases, and under them the one it is in with its line (`skeleton - sets up the project and writes the smallest spec that runs, so the first build can start`). A phase's name alone says little to someone who has not read the skill. Every skill with more than one step describes each; a line for a step the skill does not have is an error, as a gate after one is.
51
51
 
52
- A skill gets the tools its `allowed-tools` names, and nothing else: one that names none has none. So every skill here names its own - the file tools (`read`, `write`, `edit`, `bash`, `ls`, `grep`, `find`) for the ones that write specs, resources or scripts, only the reading ones for those that analyse and report, and turri's own (`write_spec_diff` and the ones below) where a skill uses them.
52
+ A skill gets the tools its `allowed-tools` names, and nothing else: one that names none has none. So every skill here names its own - the file tools (`read`, `write`, `edit`, `bash`, `ls`, `grep`, `find`) for the ones that write \*\*\*plain, resources or scripts, only the reading ones for those that analyse and report, and turri's own (`change_plain` and the ones below) where a skill uses them.
53
53
 
54
- What a run changed is part of its result: every file of the project it added, changed or removed - its specs, resources, scripts; not the built code - or that it changed none, which is said as such, so a run that wrote nothing is never reported as done. A skill without gates says it was not reviewed. A `.plain` file a skill edited with the file tools is not rendered by it: the result says so, for the conversation to render the change.
54
+ What a run changed is part of its result: every file of the project it added, changed or removed - its \*\*\*plain, resources, scripts; not the built code - or that it changed none, which is said as such, so a run that wrote nothing is never reported as done. A skill without gates says it was not reviewed. A `.plain` file a skill edited with the file tools is not regenerated by it: the result says so, for the conversation to regenerate the change.
55
55
 
56
- A skill acts on the work loop with the conversation's own tools, on the conversation's loop - never copies of them (`src/loop-tools.ts`). Every skill with the loop has `drift`: what is left to regenerate, what is regenerating, and "Fully regenerated" once nothing is. One that lists `regenerate` and `halt` in `allowed-tools` - as `mature` does - can regenerate what a module's spec holds and halt a pulse, with why. `mature` lists two tools of its own as well: `watch_jobs` waits for its pulses, coming back as soon as one struggles - sent back four times or more, five setbacks, a spec conflict, a failure run again, over half an hour - with what it went through; `spec_findings` reads back, numbered and each once, what the pulses recorded since the run started: the assumptions and pitfalls in every codebase's working notes, a failed pulse's diagnosis, a spec that contradicts itself.
56
+ A skill acts on the work loop with the conversation's own tools, on the conversation's loop - never copies of them (`src/loop-tools.ts`). Every skill with the loop has `drift`: what is left to regenerate, what is regenerating, and "Fully regenerated" once nothing is. One that lists `regenerate` and `halt` in `allowed-tools` - as `mature` does - can regenerate what a module's \*\*\*plain holds and halt a pulse, with why. `mature` lists two tools of its own as well: `watch_jobs` waits for its pulses, coming back as soon as one struggles - sent back four times or more, five setbacks, a \*\*\*plain conflict, a failure run again, over half an hour - with what it went through; `spec_findings` reads back, numbered and each once, what the pulses recorded since the run started: the assumptions and pitfalls in every codebase's working notes, a failed pulse's diagnosis, \*\*\*plain that contradicts itself.
57
57
 
58
58
  Each step is told where its skill's files are, so a path it names - `references/intent.md` - and a skill it says to follow - `implement-unit-testing-script` - are found without searching.
59
59
 
@@ -75,11 +75,11 @@ A gate can also have a `continuous` reviewer: a read-only agent that re-reads th
75
75
 
76
76
  - **Interview the user.** `ask_user` puts one question to the person. In the TUI it is a dialog; from the CLI the options are numbered. Offering choices is required, not optional - a call with neither `options` nor `free_form: true` is refused - and every list carries "Something else…". `multi_select: true` gives every option a box, ticked and unticked as it is chosen - a pick made by mistake is undone by choosing it again - until they choose Done; what they type is one more box, ticked. "Something else…", "Chat about this" and Done are no boxes. The skill's agent talks in a session of its own that the person never sees, so what the person must read to answer - the brief to confirm, the plan to approve - it passes as `ask_user`'s `context`, shown above the question in the dialog; `brood`, `spawn` and `bud` pass their briefs so. The agents are told to keep it to what can be taken in at a glance, about fifteen lines; one longer scrolls in a window of its own above the question (`src/question-dialog.ts`) - PgUp/PgDn, Shift+Up/Down or the mouse wheel - so the question and its options always show below it. [pi](https://pi.dev)'s own select, which turri used before, put it all in its title, and a long brief pushed the options off the screen. Nothing else it wrote is shown there: the last thing it said before asking - its account of a probe, a regeneration - is kept with the answer, in the run's trace and the transcript, never in the dialog.
77
77
 
78
- Beside "Something else…", every list an agent's question shows carries "Chat about this": the person says what they want to talk over - why it is asked, what an option means for later - and it goes back to the agent that asked, as no answer. A skill's agent answers in the `context` of the question asked again, since nothing else it writes reaches them; the conversation's agent answers in its reply and ends its turn, the person going on in the chat. It is not counted against the questioning level, and owes no spec change. The harness's own first question - how many the phases may ask - has no agent to answer, and does not offer it. Either choice is backed out of with Esc, or by typing nothing: the options come back, so one picked by accident costs nothing. Esc on the options themselves dismisses the question.
78
+ Beside "Something else…", every list an agent's question shows carries "Chat about this": the person says what they want to talk over - why it is asked, what an option means for later - and it goes back to the agent that asked, as no answer. A skill's agent answers in the `context` of the question asked again, since nothing else it writes reaches them; the conversation's agent answers in its reply and ends its turn, the person going on in the chat. It is not counted against the questioning level, and owes no \*\*\*plain change. The harness's own first question - how many the phases may ask - has no agent to answer, and does not offer it. Either choice is backed out of with Esc, or by typing nothing: the options come back, so one picked by accident costs nothing. Esc on the options themselves dismisses the question.
79
79
  - **Be asked something on the side.** A skill runs inside one `cultivate` call, so the conversation can answer nothing until it returns. `/btw <question>` (`btw.ts`) can: [pi](https://pi.dev) runs a command at once, even mid-turn, and it starts an agent of its own - fresh, on the chat's model, reading the project and turri and changing nothing - shown the conversation, the running skill's trace and the end of its agent's session, and the pulses. It is told it is turri, as the conversation's agent is (`identity.ts`), and answers as turri, in the first person - never as an onlooker who sends the person to turri with a message to pass on. Its answer goes in the transcript as an entry, in no model's context: the conversation and the skill go on as if nothing was asked. While a question of the skill's has the keys, `/btw <question>` typed as the answer (under "Something else…") is the same: answered, and the question asked again with the answer above it - the skill's agent never sees it.
80
- - **Build while it interviews.** `write_spec_diff` (`brood`, `spawn`) writes a change to the spec and queues its render in one call; the interview carries on while the builds run in the background, one after another. `drift` shows how they stand. The person sees its calls as `mutate`: a tool can be shown by a name of its own (`SHOWN_AS` in `regenerate/activity.ts`), while its agent calls it by the one that says what it takes.
80
+ - **Build while it interviews.** `change_plain` (`brood`, `spawn`) writes a change to \*\*\*plain and queues its regeneration in one call; the interview carries on while the builds run in the background, one after another. `drift` shows how they stand. The person sees its calls as `mutate`: a tool can be shown by a name of its own (`SHOWN_AS` in `regenerate/activity.ts`), while its agent calls it by the one that says what it takes.
81
81
  - **Regenerate, and conform.** A skill that names them in `allowed-tools` gets the conversation's own `regenerate`, `drift`, `halt` and `conform`, on its loop: `conform` schedules a conformance pulse for functional specs not built yet, as it does from the chat. The skill never waits for them.
82
- - **Choose the render model**, when the user says so - `engine`, the same tool the conversation has.
82
+ - **Choose the model that regenerates**, when the user says so - `engine`, the same tool the conversation has.
83
83
 
84
84
  The harness still owns the sequence. A step chooses what to ask; it does not choose whether the gate afterwards runs. A run is saved as it goes, so an interrupted one can be resumed where it stopped.
85
85
 
@@ -93,13 +93,13 @@ A name is one skill. turri's own come first, then `.turri/skills/`, then `.agent
93
93
 
94
94
  `.turri/` is turri's scratch folder and ignores itself, all but `skills/`: its `.gitignore` is `*`, `!skills/`, `!skills/**`, and one written before is brought up to date. [pi](https://pi.dev) reads `.agents/skills/` too, as skills of its own the chat could follow by hand - the chat is told to run any `cultivate` lists with `cultivate`, so its gates still run.
95
95
 
96
- The skills [pi](https://pi.dev) offers the chat leave out what turri has already (`src/chat-skills.ts`): a skill of a name turri runs itself - its own, or the project's - and any skill about [\*\*\*plain](https://plainlang.org/docs/), its specs or their testing scripts. [pi](https://pi.dev) gathers skills from `~/.pi/agent/skills`, `~/.agents/skills` and the `.agents/skills` of every folder from where turri started up to its git root, so a folder above the project can hold another tool's \*\*\*plain skills - a `load-plain-reference` the chat would load at the start of every session, though the whole \*\*\*plain reference, turri's [rules](#rules), is in its system prompt already. The person is told once which were left out. A skill about anything else is offered as [pi](https://pi.dev) offers it.
96
+ The skills [pi](https://pi.dev) offers the chat leave out what turri has already (`src/chat-skills.ts`): a skill of a name turri runs itself - its own, or the project's - and any skill about [\*\*\*plain](https://plainlang.org/docs/) or its testing scripts. [pi](https://pi.dev) gathers skills from `~/.pi/agent/skills`, `~/.agents/skills` and the `.agents/skills` of every folder from where turri started up to its git root, so a folder above the project can hold another tool's \*\*\*plain skills - a `load-plain-reference` the chat would load at the start of every session, though the whole \*\*\*plain reference, turri's [rules](#rules), is in its system prompt already. The person is told once which were left out. A skill about anything else is offered as [pi](https://pi.dev) offers it.
97
97
 
98
98
  A skill is found once, as turri starts: one added while it runs is there the next time. A project's skill agent runs as turri's do - [pi](https://pi.dev)'s file tools and `bash` on the machine, no fence - so a project's skills are as trusted as the project.
99
99
 
100
100
  ## Rules
101
101
 
102
- `genome/rules/` holds the \*\*\*plain authoring rules. Every rule, in full, is appended to the system prompt of the conversation and of the agents that write specs - never of the ones that write code. The block is byte-identical every time, so it sits inside the cached prefix - but it is ~40k tokens. The trade is deliberate: a rule the model has to decide to go and read is a rule it can skip. `src/rules.ts` is where to change it.
102
+ `genome/rules/` holds the \*\*\*plain authoring rules. Every rule, in full, is appended to the system prompt of the conversation and of the agents that write \*\*\*plain - never of the ones that write code. The block is byte-identical every time, so it sits inside the cached prefix - but it is ~40k tokens. The trade is deliberate: a rule the model has to decide to go and read is a rule it can skip. `src/rules.ts` is where to change it.
103
103
 
104
104
  ## Trying the gate semantics
105
105
 
package/docs/theming.md CHANGED
@@ -14,27 +14,27 @@ Bad input fails quietly - an unknown `--use-theme` name and a malformed theme fi
14
14
 
15
15
  ## The palette
16
16
 
17
- Both themes, the command line's colours, the spec diff's, the banner's jellyfish and the docs' diagram are drawn from turri's design system and nothing else: dark, quiet surfaces, one pink thing to press, neon for anything you can follow. These are all the colours turri has - none is mixed, tinted or added, but for the jellyfish's tentacles, every other pixel of which the design draws half seen.
17
+ Both themes, the command line's colours, the \*\*\*plain diff's, the banner's jellyfish and the docs' diagram are drawn from turri's design system and nothing else: dark, quiet surfaces, one pink thing to press, neon for anything you can follow. These are all the colours turri has - none is mixed, tinted or added, but for the jellyfish's tentacles, every other pixel of which the design draws half seen.
18
18
 
19
19
  | name | value | in `turri-dark` |
20
20
  |---|---|---|
21
21
  | abyss | `#0a0f1a` | the page - the HTML export's background, and the surface the contrast below is measured on |
22
22
  | tile | `#0d1420` | the HTML export's cards |
23
23
  | hēi sè | `#142030` | boxes, the HTML export's callouts |
24
- | blue whale | `#1e3442` | a highlighted line: `selectedBg`; an added line's fill in a spec diff |
24
+ | blue whale | `#1e3442` | a highlighted line: `selectedBg`; an added line's fill in a \*\*\*plain diff |
25
25
  | lines | `#22344a` | rules, dividers, frames: `borderMuted`, `mdHr` |
26
26
  | path | `#3e5a6b` | the editor's border, what is dim: `dim`, line numbers |
27
27
  | grauzone | `#85a3b2` | body text, and what can be skimmed: `text`, `muted` |
28
28
  | white | `#f4f7f9` | headings, tool titles, what you typed |
29
29
  | siesta tan | `#e9d8c8` | strings in code, [\*\*\*plain](https://plainlang.org/docs/)'s text in quotes or backticks |
30
- | eggplant | `#732553` | a removed line's fill in a spec diff, the jellyfish's rim |
31
- | neon | `#5ce1e6` | links, focus, the selection, section labels, \*\*\*plain's concepts in a spec diff: `accent`, `mdLink`, regen's `◆` |
32
- | neon hover | `#8eebee` | inline code - siesta tan beside white text was too close to tell apart, and tiring to read - types in code, an added line's fill in a light theme's spec diff: `mdCode` |
33
- | pink | `#ff5c8d` | the wordmark, the jellyfish's bell, and what is done - its `✓`, the spec map's `◇` and `◆`: `success` |
30
+ | eggplant | `#732553` | a removed line's fill in a \*\*\*plain diff, the jellyfish's rim |
31
+ | neon | `#5ce1e6` | links, focus, the selection, section labels, \*\*\*plain's concepts in a \*\*\*plain diff: `accent`, `mdLink`, regen's `◆` |
32
+ | neon hover | `#8eebee` | inline code - siesta tan beside white text was too close to tell apart, and tiring to read - types in code, an added line's fill in a light theme's \*\*\*plain diff: `mdCode` |
33
+ | pink | `#ff5c8d` | the wordmark, the jellyfish's bell, and what is done - its `✓`, the \*\*\*plain map's `◇` and `◆`: `success` |
34
34
  | pink hover | `#ff7fa6` | the glint on the jellyfish's bell, \*\*\*plain's section headers, a removed line's `-`, keywords in code |
35
35
  | orchid | `#a9479a` | the jellyfish's bell, between its pink and its indigo - nowhere else |
36
36
  | indigo | `#5b57d9` | the jellyfish's bell, at its rim - nowhere else |
37
- | orange | `#ff7a3d` | the jellyfish's gut, the `$` of bash mode, and everything that asks for you - what failed (`✗`), what works with limits, what needs you (the spec map's `!`): `error`, `warning` |
37
+ | orange | `#ff7a3d` | the jellyfish's gut, the `$` of bash mode, and everything that asks for you - what failed (`✗`), what works with limits, what needs you (the \*\*\*plain map's `!`): `error`, `warning` |
38
38
 
39
39
  The design system keeps pink for the one thing to press and orange for the `$` prompt alone. A terminal has no buttons, but it needs status colours, so pink carries what is done and orange everything that asks for you - what failed, what works with limits, what waits on you - the mark (`✗`, `!`, `■`) telling them apart. The palette has no red.
40
40
 
@@ -68,9 +68,9 @@ With tool rows carrying no background, their state is signalled by the title and
68
68
 
69
69
  **The window's background is your terminal's setting**, and the transcript is drawn on it. For the brand look, set it to abyss, `#0a0f1a`, in your terminal profile.
70
70
 
71
- ## Spec diffs
71
+ ## \*\*\*plain diffs
72
72
 
73
- A spec diff in the panels is not painted with the theme's colours for code: those are made for code, and the turri themes' backgrounds are all the surface colour, so a diff drawn with them would have no fills and hardly any highlighting. It has a palette of its own (`src/regenerate/plain-palette.ts`), one for a dark theme and one for a light, picked by the theme's appearance: a hue for each piece of \*\*\*plain - a section's header, a concept, a link, text in quotes or backticks, a front matter key, a module, a comment - at least 7:1 on the surface, and a fill behind each added and removed line: on a dark theme blue whale and eggplant, on a light one neon hover and siesta tan. Every colour reads at least 4.8:1 on blue whale and 5:1 on the light fills; eggplant, the design system's only warm dark fill, takes the pinks and grauzone down to 3.7:1.
73
+ A \*\*\*plain diff in the panels is not painted with the theme's colours for code: those are made for code, and the turri themes' backgrounds are all the surface colour, so a diff drawn with them would have no fills and hardly any highlighting. It has a palette of its own (`src/regenerate/plain-palette.ts`), one for a dark theme and one for a light, picked by the theme's appearance: a hue for each piece of \*\*\*plain - a section's header, a concept, a link, text in quotes or backticks, a front matter key, a module, a comment - at least 7:1 on the surface, and a fill behind each added and removed line: on a dark theme blue whale and eggplant, on a light one neon hover and siesta tan. Every colour reads at least 4.8:1 on blue whale and 5:1 on the light fills; eggplant, the design system's only warm dark fill, takes the pinks and grauzone down to 3.7:1.
74
74
 
75
75
  ## The font
76
76
 
package/docs/using.md CHANGED
@@ -7,32 +7,32 @@ cd ~/my-project
7
7
  turri
8
8
  ```
9
9
 
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 a spec, render a module, fix a spec. 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)).
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 spec change - a git diff of the `.plain` files, written with `write_spec_diff` and rendered as a render spec 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 spec 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 spec diff, and what is left to build is worked out again from the spec |
18
- | `mature` | for a spec you already have, rough or hand-written: on its own until it is fully rendered - renders what is not built, watches the agents building it, and when one struggles on something the spec leaves open, settles it in the spec as a spec diff, stops that build and renders it again; decides open choices itself and marks them in its report |
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 the spec 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 the spec no longer wants is stopped. With functionalities named, those are regenerated though their spec 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 the spec, starting nothing |
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 a spec, regenerating nothing |
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,9 +45,9 @@ 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 render does, in order, is in [regenerating the code](regenerating.md).
48
+ What each regeneration does, in order, is in [regenerating the code](regenerating.md).
49
49
 
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)).
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
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
 
@@ -55,14 +55,14 @@ The agent does this work with these tools, never by running turri's command line
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 render takes minutes, so in the TUI every render tool 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 spec 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.
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 spec diff - the side panel has both. With nothing running it is gone.
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 spec 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#spec-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 rendered first, the rest by name, and none the diff does not change. Each line has its number in that file: a spec diff is of the spec 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 spec itself, in full, with how much of it the code has been regenerated from drawn onto it.
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 spec - 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 spec 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 spec. 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.
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 the spec says |
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 the spec, still in the code - a struck-through red ghost, with the code record's text, until the pulse that takes it out lands |
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 the spec, the machine set up - and regenerates. Pulses are numbered as they start in a module, from 1 each session.
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. A spec 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.
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 the spec 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.
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 spec diff changes - the file of the module being rendered 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)).
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
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
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 spec 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 spec, 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 spec 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 spec 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 - a spec 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 the spec as it is now - again whenever a pulse ends or the spec 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 the spec 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 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 the spec as it is now there, twice a pulse at most. Otherwise a pulse building something the spec 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 the spec - one spec diff. Stopping a pulse leaves its change in the spec: the codebase waits until the spec 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 spec only when a new module is to start from it. Specs moved into a module the code was not built on stop 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.
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 specs, code, tests, testing scripts and logs, and turri's own source, skills and rules, says what happened, why, whether the fix belongs in the spec, the environment, the code or turri itself, and exactly what to do.
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 the spec - which section, and how. Running the same build again picks up from there.
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 render tools default to it - `module` when you ask to build without naming one, `conformance-tests: false` to render without conformance tests (none written, none of the existing ones run) unless you ask for them, `coding-model` for what renders 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).
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 render runs on - functionalities, conformance tests, repairs, spec changes; and the **judge model**, which gives the verdicts in a conformance run - the judge, which says whether the code, the tests or the spec 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):
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,17 +125,17 @@ 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](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, renders run on the conversation's model.
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 render runs on the coding model unless you say otherwise. Say it in words - "render spec 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):
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
- | spec changes | render spec diff pulses, including the ones `brood` queues as you answer |
135
- | functionalities | render functionality, render module, regenerate, conformance |
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 render tool can also be given a model for one call.
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
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
 
@@ -151,9 +151,9 @@ A provider failure - no credits, bad key, rate limit - is raised with the provid
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 render, a functionality regenerated, a conformance run, a regression repair, a spec change. `/lineage [module]` and `lineage` show it.
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 render, functionality regenerated, spec 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.
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,7 +163,7 @@ 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 render starts - the coding agent, the conformance agent, the repair agent, the spec-steps planner - also streams its whole conversation to a file of its own.
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
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.
@@ -172,7 +172,7 @@ Beside them, each pulse has a log of its own in `.turri/jobs/` - `<time>-<pulse
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 render.
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 the spec moved on from), `planner.failed`, `planner.fellBack` |
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 spec, so its linked resources resolve the way the parser expects. For a long run - a whole spec overnight - see [running unattended](headless.md).
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 spec-diff <file.plain> <diff> # apply a spec diff, and regenerate it
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