@turri-dev/cli 0.1.27 → 0.1.28-next.d5f2680
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +6 -6
- package/dist/advisor-chat.d.ts +63 -0
- package/dist/advisor-chat.js +290 -0
- package/dist/background-skills.d.ts +53 -0
- package/dist/background-skills.js +121 -0
- package/dist/bedrock-region.d.ts +66 -0
- package/dist/bedrock-region.js +144 -0
- package/dist/btw.d.ts +1 -1
- package/dist/btw.js +1 -1
- package/dist/change-plain-cli.js +20 -13
- package/dist/chat-idle.d.ts +7 -0
- package/dist/chat-idle.js +20 -0
- package/dist/chats.d.ts +10 -0
- package/dist/chats.js +23 -0
- package/dist/cli-options.d.ts +1 -2
- package/dist/cli-options.js +2 -7
- package/dist/cli.js +11 -3
- package/dist/config-command.d.ts +14 -0
- package/dist/config-command.js +35 -15
- package/dist/config-edit.d.ts +3 -3
- package/dist/config-edit.js +16 -12
- package/dist/config.d.ts +54 -34
- package/dist/config.js +40 -44
- package/dist/conform-cli.js +12 -10
- package/dist/conformance-switch.d.ts +34 -0
- package/dist/conformance-switch.js +108 -0
- package/dist/cultivate-cli.js +15 -5
- package/dist/drift-cli.js +2 -0
- package/dist/extension.d.ts +15 -0
- package/dist/extension.js +260 -83
- package/dist/host-session.d.ts +26 -0
- package/dist/host-session.js +30 -2
- package/dist/host.d.ts +5 -2
- package/dist/host.js +7 -1
- package/dist/init-config.d.ts +1 -1
- package/dist/init-config.js +20 -11
- package/dist/loop-cli.d.ts +3 -3
- package/dist/loop-cli.js +13 -16
- package/dist/loop-tools.js +18 -10
- package/dist/model-check.d.ts +8 -3
- package/dist/model-check.js +13 -8
- package/dist/new-project.js +12 -8
- package/dist/{validate-cli.js → parse-cli.js} +11 -5
- package/dist/parse-tool.d.ts +9 -0
- package/dist/parse-tool.js +33 -0
- package/dist/pi-launch.js +1 -1
- package/dist/plain/check.d.ts +53 -6
- package/dist/plain/check.js +217 -24
- package/dist/plain/concepts.d.ts +8 -0
- package/dist/plain/concepts.js +66 -23
- package/dist/plain/cycles.d.ts +21 -0
- package/dist/plain/cycles.js +190 -0
- package/dist/plain/errors.d.ts +4 -0
- package/dist/plain/errors.js +7 -0
- package/dist/plain/files.d.ts +18 -1
- package/dist/plain/files.js +39 -2
- package/dist/plain/graph.d.ts +33 -4
- package/dist/plain/graph.js +60 -12
- package/dist/plain/highlight.d.ts +3 -2
- package/dist/plain/highlight.js +4 -3
- package/dist/plain/index.d.ts +8 -5
- package/dist/plain/index.js +8 -5
- package/dist/plain/liquid.js +1 -6
- package/dist/plain/markdown.d.ts +32 -3
- package/dist/plain/markdown.js +146 -24
- package/dist/plain/module-concepts.js +2 -2
- package/dist/plain/parser.d.ts +83 -28
- package/dist/plain/parser.js +381 -260
- package/dist/plain/plain.tmLanguage.json +29 -3
- package/dist/plain/project.d.ts +48 -0
- package/dist/plain/project.js +155 -0
- package/dist/plain/spec.d.ts +90 -28
- package/dist/plain/spec.js +107 -133
- package/dist/plain/test-plain-file.d.ts +59 -0
- package/dist/plain/test-plain-file.js +232 -0
- package/dist/regenerate/activity.d.ts +20 -9
- package/dist/regenerate/activity.js +11 -4
- package/dist/regenerate/advisor-records.d.ts +93 -0
- package/dist/regenerate/advisor-records.js +110 -0
- package/dist/regenerate/advisor-runs.d.ts +87 -0
- package/dist/regenerate/advisor-runs.js +327 -0
- package/dist/regenerate/base-folder.d.ts +68 -0
- package/dist/regenerate/base-folder.js +168 -0
- package/dist/regenerate/cases.d.ts +9 -6
- package/dist/regenerate/cases.js +70 -14
- package/dist/regenerate/change-view.d.ts +8 -3
- package/dist/regenerate/change-view.js +71 -22
- package/dist/regenerate/chunk-plan.d.ts +36 -15
- package/dist/regenerate/chunk-plan.js +165 -59
- package/dist/regenerate/conform.d.ts +3 -0
- package/dist/regenerate/conform.js +6 -5
- package/dist/regenerate/conformance-agents.d.ts +18 -39
- package/dist/regenerate/conformance-agents.js +60 -88
- package/dist/regenerate/conformance.d.ts +23 -13
- package/dist/regenerate/conformance.js +119 -82
- package/dist/regenerate/diff-view.d.ts +1 -1
- package/dist/regenerate/diff-view.js +20 -4
- package/dist/regenerate/drift.d.ts +47 -15
- package/dist/regenerate/drift.js +101 -30
- package/dist/regenerate/jobs.d.ts +2 -2
- package/dist/regenerate/jobs.js +1 -1
- package/dist/regenerate/ledger.d.ts +19 -0
- package/dist/regenerate/loop.d.ts +62 -43
- package/dist/regenerate/loop.js +472 -341
- package/dist/regenerate/paths.d.ts +4 -0
- package/dist/regenerate/paths.js +7 -1
- package/dist/regenerate/plain-palette.js +2 -0
- package/dist/regenerate/prompt.d.ts +57 -19
- package/dist/regenerate/prompt.js +157 -122
- package/dist/regenerate/records.d.ts +43 -12
- package/dist/regenerate/records.js +102 -88
- package/dist/regenerate/regenerate.js +29 -11
- package/dist/regenerate/regression.d.ts +4 -4
- package/dist/regenerate/regression.js +14 -10
- package/dist/regenerate/renames.d.ts +42 -0
- package/dist/regenerate/renames.js +185 -0
- package/dist/regenerate/requires.d.ts +1 -1
- package/dist/regenerate/requires.js +10 -3
- package/dist/regenerate/resources.d.ts +5 -3
- package/dist/regenerate/resources.js +4 -4
- package/dist/regenerate/scripts-being-written.d.ts +19 -0
- package/dist/regenerate/scripts-being-written.js +30 -0
- package/dist/regenerate/spec-map-view.d.ts +2 -2
- package/dist/regenerate/spec-map-view.js +46 -15
- package/dist/regenerate/spec-map.d.ts +27 -5
- package/dist/regenerate/spec-map.js +92 -60
- package/dist/regenerate/spec-steps.d.ts +48 -5
- package/dist/regenerate/spec-steps.js +213 -28
- package/dist/regenerate/state.d.ts +4 -3
- package/dist/regenerate/stop-report.d.ts +1 -1
- package/dist/regenerate/stop-report.js +10 -10
- package/dist/regenerate/test-advisor.d.ts +101 -0
- package/dist/regenerate/test-advisor.js +379 -0
- package/dist/regenerate/test-kinds.d.ts +71 -28
- package/dist/regenerate/test-kinds.js +110 -63
- package/dist/regenerate/test-scripts.d.ts +2 -0
- package/dist/regenerate/test-scripts.js +4 -0
- package/dist/regenerate/testing-steps.d.ts +41 -0
- package/dist/regenerate/testing-steps.js +110 -0
- package/dist/regenerate/testing.d.ts +1 -1
- package/dist/regenerate/testing.js +5 -4
- package/dist/regenerate/unit-review.d.ts +1 -1
- package/dist/regenerate/unit-review.js +3 -3
- package/dist/regenerate-cli.js +11 -12
- package/dist/release-agent.js +4 -0
- package/dist/release-cli.js +2 -0
- package/dist/release-tools.js +2 -0
- package/dist/run-report.d.ts +2 -2
- package/dist/run-report.js +4 -3
- package/dist/self-invocation.d.ts +1 -1
- package/dist/self-invocation.js +1 -1
- package/dist/sentinel.js +1 -1
- package/dist/setup.d.ts +10 -0
- package/dist/setup.js +52 -4
- package/dist/skill.d.ts +10 -0
- package/dist/skill.js +6 -0
- package/dist/skills.js +3 -1
- package/dist/spec-tools.js +21 -31
- package/dist/test-plain-files.d.ts +41 -0
- package/dist/test-plain-files.js +177 -0
- package/docs/README.md +3 -1
- package/docs/authoring-plain.md +46 -13
- package/docs/chats.md +3 -1
- package/docs/config.md +20 -15
- package/docs/deploy.md +1 -1
- package/docs/development.md +2 -1
- package/docs/headless.md +15 -18
- package/docs/hosting.md +28 -12
- package/docs/installation.md +1 -0
- package/docs/parser.md +88 -27
- package/docs/regenerating.md +87 -52
- package/docs/sentinel.md +1 -1
- package/docs/skills-and-gates.md +3 -1
- package/docs/test-advisor.md +110 -0
- package/docs/theming.md +1 -1
- package/docs/using.md +17 -15
- package/genome/rules/bullet-continuation.md +8 -8
- package/genome/rules/concise-specs-examples.md +111 -80
- package/genome/rules/concise-specs-integration-examples.md +56 -41
- package/genome/rules/concise-specs.md +17 -16
- package/genome/rules/definitions.md +5 -5
- package/genome/rules/examples/integration-embedded-testing/run_conformance_tests_java.sh +1 -1
- package/genome/rules/func-specs.md +69 -74
- package/genome/rules/impl-reqs.md +4 -4
- package/genome/rules/integration-embedded-testing.md +20 -20
- package/genome/rules/integration-embedded.md +31 -31
- package/genome/rules/integration-standalone.md +9 -9
- package/genome/rules/integrations.md +18 -18
- package/genome/rules/linked-resources.md +19 -16
- package/genome/rules/module-structure.md +21 -14
- package/genome/rules/project-model.md +18 -17
- package/genome/rules/required-concepts.md +1 -1
- package/genome/rules/requires-modules.md +6 -5
- package/genome/rules/semantic-ids.md +49 -0
- package/genome/rules/stdlib.md +4 -5
- package/genome/rules/test-plain-files.md +130 -0
- package/genome/rules/test-reqs.md +8 -8
- package/genome/rules/turri-regeneration.md +23 -8
- package/genome/skills/analyze-2-func-specs/SKILL.md +36 -21
- package/genome/skills/analyze-func-specs/SKILL.md +46 -32
- package/genome/skills/break-down-func-spec/SKILL.md +91 -67
- package/genome/skills/consolidate-concepts/SKILL.md +10 -10
- package/genome/skills/convert-old-syntax/SKILL.md +68 -0
- package/genome/skills/convert-old-syntax/references/rubric.md +11 -0
- package/genome/skills/debug-specs/SKILL.md +62 -56
- package/genome/skills/implement-conformance-testing-script/SKILL.md +19 -15
- package/genome/skills/implement-conformance-testing-script/assets/run_conformance_tests_java.sh +2 -2
- package/genome/skills/implement-conformance-testing-script/assets/run_conformance_tests_python.sh +2 -2
- package/genome/skills/implement-unit-testing-script/SKILL.md +14 -10
- package/genome/skills/refactor-module/SKILL.md +35 -35
- package/genome/skills/resolve-section-ownership/SKILL.md +9 -9
- package/genome/skills/resolve-spec-conflict/SKILL.md +35 -29
- package/package.json +7 -6
- package/dist/regenerate/choose-conformance.d.ts +0 -32
- package/dist/regenerate/choose-conformance.js +0 -117
- /package/dist/{validate-cli.d.ts → parse-cli.d.ts} +0 -0
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
# The test advisor
|
|
2
|
+
|
|
3
|
+
Which functional specs need explicit testing scenarios is not obvious, and not every one should be tested - only the functionality that matters. The test advisor decides for you: when a functional spec is new or its text changed and it has no scenarios - or when the chat asks - turri checks in the background whether it needs testing. When it does, the chat you are looking at tells you so and writes what testing it takes - the \*\*\*test reqs\*\*\*, the conformance testing script and its scenarios - so the module's conformance pulse runs once its code is built. The advisor decides; the chat is the only writer of the \*\*\*plain. You are never asked anything.
|
|
4
|
+
|
|
5
|
+
## When it runs
|
|
6
|
+
|
|
7
|
+
**Only with conformance tests on.** The advisor is there to say which functional specs need scenarios, and scenarios are only tested with `conformance-tests: true` ([config](config.md#conformance-tests)): off - as it is unset - the advisor is off too, by itself and when asked: the chat has no `evaluate_tests`, and a host's `evaluateTests` throws, saying how they are turned on.
|
|
8
|
+
|
|
9
|
+
**Two ways in.** By itself, for every new or changed functional spec without scenarios, as below - what `test-advisor: false` turns off. And when the chat asks, with `evaluate_tests`, whatever the setting - see [asking for an evaluation](#asking-for-an-evaluation).
|
|
10
|
+
|
|
11
|
+
**Only in the chat and a host.** Never in a headless command - `turri regenerate`, `conform`, `change-plain` and the rest - and never in [pi](https://pi.dev)'s print, json and rpc modes (`turri -p`), which end when the answer is printed. That is decided where the session starts: the terminal's chat and a host's session have one, nothing else does.
|
|
12
|
+
|
|
13
|
+
**What it looks at by itself:** a functional spec, in any module, that is new or whose text changed since its code was built - measured against the code record, what the code was built from: text that differs from the record, or a functional spec not built yet. Then against the advisor's file: a text already looked at, under its hash, is not looked at again. So a fresh project's first functional specs are looked at, and a project already built that has no advisor file gets no backlog: only what changes from now on is.
|
|
14
|
+
|
|
15
|
+
**What it leaves alone:**
|
|
16
|
+
- a functional spec with scenarios of any kind - [today's review](regenerating.md#conformance-tests) of its scenarios covers it when its text changes
|
|
17
|
+
- the standard library's templates, which have no functional specs
|
|
18
|
+
|
|
19
|
+
**When:** by itself, as soon as the work loop sees a \*\*\*plain change - a `change_plain`, a `regenerate`, a pulse ending - beside the code pulse it plans. The functional spec is marked `pending` in the advisor's file there and then, so quitting loses nothing: the next start in the chat or a host picks up every `pending` entry.
|
|
20
|
+
|
|
21
|
+
**Two at a time at most,** outside `parallel-jobs` and the lanes: it never takes a pulse's slot, no pulse waits for it, and it never steers or stops one.
|
|
22
|
+
|
|
23
|
+
**A text that changes while it runs throws the work away,** whoever changed it - you, the chat's `change_plain`, a skill: what it had judged for the old text is dropped (`advisor.dropped`), and the new text starts again.
|
|
24
|
+
|
|
25
|
+
A failure - a model error, a call that never answered - is dropped quietly: the entry says `failed` and why, `advisor.failed` goes into the ledger, and the same text is not tried again. Nothing is shown.
|
|
26
|
+
|
|
27
|
+
## What you see
|
|
28
|
+
|
|
29
|
+
**Being evaluated.** A change's result - `regenerate`'s, `change_plain`'s - names the functional specs the advisor has just taken up, as `evaluate_tests` names the ones it was asked for, and the chat says in a line that testing scenarios are being evaluated for them, and goes on: there is nothing to wait for.
|
|
30
|
+
|
|
31
|
+
**A yes** comes back to the chat you are looking at as a message that starts a turn. It holds the report - the questions that were a yes with their evidence, and the inputs to cover - and the chat, asking you nothing, tells you in a line:
|
|
32
|
+
|
|
33
|
+
> core:clear-tasks needs testing scenarios - a wrong implementation could look right. Writing them now.
|
|
34
|
+
|
|
35
|
+
Then it writes, with `change_plain`, whatever the [step-7 guard](regenerating.md#the-steps-of-adding-tests) would refuse the scenarios for, in turn and only what is missing: the \*\*\*test reqs\*\*\* of the module it is tested in - the top of its chain, where its conformance tests live - the stack's conformance template, `stdlib/<kind>/conformance/<stack>`, imported, or the module's own; the conformance testing script for its stack, with `implement-conformance-testing-script` - in the background, the chat going straight on; then the scenarios, in `tests/<module>/<func-id>.plain`. They cover the report's inputs that are this functional spec's - an agreed one with the readers' output, never a placeholder - and a split one is settled by the reading most consistent with the functional spec and its module, the chat saying which it took. The change is visible as any is, and the conformance pulse follows once the code is built.
|
|
36
|
+
|
|
37
|
+
**A no** is one line turri draws itself, starting no turn and reaching no model:
|
|
38
|
+
|
|
39
|
+
> core:list-tasks doesn't need testing scenarios - nothing silent to get wrong.
|
|
40
|
+
|
|
41
|
+
**Never an interruption.** A verdict that arrives while the chat is busy - its agent working, or you typing - waits until it is idle; verdicts that arrive meanwhile come together, as one turn for every yes and a line for each no. One whose functional spec changed meanwhile, or was given scenarios, is let go. A yes is kept in the advisor's file until a chat has it: one that arrives with no chat open, or whose session ends first, is delivered at the next start, as `pending` work is picked up.
|
|
42
|
+
|
|
43
|
+
Asked for scenarios - "write conformance scenarios for core:add-task" - the chat decides and writes them itself, as a \*\*\*plain change you see, whatever `test-advisor` says.
|
|
44
|
+
|
|
45
|
+
A host gets the verdicts in its chats the same way - see [hosting](hosting.md).
|
|
46
|
+
|
|
47
|
+
## Asking for an evaluation
|
|
48
|
+
|
|
49
|
+
The chat has a tool of its own, `evaluate_tests`: the functional specs as `module:func-id`s - or func-ids, with `plain_file` - and an optional `reason`. It returns at once - "Testing scenarios for core:add-task are being evaluated in the background; the verdict comes back to this chat." - and the verdict comes back as any verdict does. The chat calls it:
|
|
50
|
+
|
|
51
|
+
- when you ask whether something needs tests - "does add-task need tests?";
|
|
52
|
+
- on its own, as another way of saying what a functional spec means: when pulses keep failing on a complicated one, say, scenarios may pin down what it should do.
|
|
53
|
+
|
|
54
|
+
It evaluates even a text the advisor already judged. A functional spec being evaluated already is not started twice, and one that has scenarios is answered at once as such and not evaluated. It works whatever `test-advisor` says. The `reason` - "the coding agent failed three times to implement it" - reaches the classifier as context, never as evidence.
|
|
55
|
+
|
|
56
|
+
## How it decides
|
|
57
|
+
|
|
58
|
+
Every step is a plain model call on the advisor's model, answered through one tool and checked by turri - none of them is a skill, and none can touch the project. Each is given the functional spec, its module whole - and for a module below, the tip its scenarios are tested in, whose own functional specs show in the same commands - and the project's own words on what matters: its `AGENTS.md`, and what you said in the conversation about how the work is to be planned. There is no keyword filter and no confidence score.
|
|
59
|
+
|
|
60
|
+
**Classifying** is one call with four questions, each yes backed by evidence:
|
|
61
|
+
|
|
62
|
+
| question | a yes needs |
|
|
63
|
+
| --- | --- |
|
|
64
|
+
| silent failure: could a wrong implementation give output that looks right? | one such wrong output |
|
|
65
|
+
| divergence: is there an input two reasonable readings answer differently? | the input, and both outputs |
|
|
66
|
+
| revisited: is it likely to be changed or built on later? | what would change |
|
|
67
|
+
| cost: would a bug cost money, expose something, lose data or harm users' trust? | the consequence |
|
|
68
|
+
|
|
69
|
+
A yes whose evidence is empty or malformed - two readings that are the same - counts as no. The verdict is **yes** when silent failure, divergence or cost is a yes; revisited alone, or all four no, is **no**: the coding agent's unit tests are the regression protection there. A no costs one call, and ends there.
|
|
70
|
+
|
|
71
|
+
**On a yes, the inputs** the scenarios are to cover are picked by readers that disagree on purpose:
|
|
72
|
+
|
|
73
|
+
1. **Probes:** one call writes concrete boundary inputs, as many as the functional spec has real boundaries - one to ten, never padded to a number - limits, empty and missing values, the cases the wording leaves open - and the divergence's input joins them. Only inputs the functional spec bears on: never a command, flag or alias it does not name.
|
|
74
|
+
2. **Readers:** each predicts every probe's output. There are three - a literal reader, an edge-case hunter, and a reader from another domain - the same call on the same model, differing only by their system prompt. Two run first; when they already disagree on a probe, the third is spared. A reader answers `unspecified` for an input the functional spec says nothing about.
|
|
75
|
+
3. **Comparing,** in code: the outputs are compared with whitespace and case left aside, and any disagreement makes the input a candidate - the union of the readers, never a majority.
|
|
76
|
+
4. **Verifying:** one last call keeps, of each candidate, the readings a reasonable person could hold. A candidate left with one is an agreed input; one left with none is dropped. An input whose `unspecified` holds - every reader's, or one the verifier keeps - is left out: a scenario on it would pin a guess, testing more than the functional spec says.
|
|
77
|
+
|
|
78
|
+
The **report** is what the advisor's file keeps: the questions that were a yes with their evidence, and each input, either agreed - with the readers' output - or split, with its readings.
|
|
79
|
+
|
|
80
|
+
## The setting and its model
|
|
81
|
+
|
|
82
|
+
`test-advisor: true | false` in `config.turri.yaml`, `true` when it is left out - `/turri-config` sets it. `false` turns off only the advisor's own start: `evaluate_tests` still works, and asked for scenarios, the chat writes them as it always has, whatever the setting says.
|
|
83
|
+
|
|
84
|
+
Every call the advisor makes runs on `models.advisor` - see [models per role](config.md#models-per-role). Without one it runs on `default`'s model, else the coding model alone, thinking low.
|
|
85
|
+
|
|
86
|
+
## What it keeps
|
|
87
|
+
|
|
88
|
+
What the advisor made of each functional spec is in `.turri/advisor/<module>.json`, in the project - local to this machine, as all of `.turri/` is: another machine starts with none, and looks only at what changes there. One entry a functional spec, by its func-id:
|
|
89
|
+
|
|
90
|
+
| | |
|
|
91
|
+
| --- | --- |
|
|
92
|
+
| `hash` | a hash of the functional spec's text: the entry is about that text, and stale once it changes |
|
|
93
|
+
| `state` | `pending` - seen, not finished; `yes`; `no`; or `failed` |
|
|
94
|
+
| `report` | the verdict, the questions that were a yes with their evidence, and on a yes the inputs to cover |
|
|
95
|
+
| `model`, `at` | what it ran on, and when |
|
|
96
|
+
| `tip` | the module at the top of its chain, where its scenarios are tested |
|
|
97
|
+
| `undelivered` | a yes no chat has had yet: delivered at the next start |
|
|
98
|
+
| `asked` | pending at the chat's asking, with its reason: picked up at the next start whatever `test-advisor` says |
|
|
99
|
+
|
|
100
|
+
Deleting the folder loses only what the advisor knows: an entry that is not there is a functional spec it has not looked at.
|
|
101
|
+
|
|
102
|
+
## In the ledger
|
|
103
|
+
|
|
104
|
+
Three facts go into [the ledger](using.md#the-ledger), the report staying in the advisor's file:
|
|
105
|
+
|
|
106
|
+
| event | |
|
|
107
|
+
| --- | --- |
|
|
108
|
+
| `advisor.classified` | the functional spec, `module:func-id`, its verdict, the questions that were a yes, and whether the chat asked for it (`asked: true`) or a change started it |
|
|
109
|
+
| `advisor.dropped` | the work on a functional spec thrown away, its text changed while it ran |
|
|
110
|
+
| `advisor.failed` | why it failed |
|
package/docs/theming.md
CHANGED
|
@@ -70,7 +70,7 @@ With tool rows carrying no background, their state is signalled by the title and
|
|
|
70
70
|
|
|
71
71
|
## \*\*\*plain diffs
|
|
72
72
|
|
|
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.
|
|
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, the `# <id>` heading of a functional spec or a scenario (neon, bold; eggplant, bold, on a light theme), 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
|
@@ -9,7 +9,7 @@ turri
|
|
|
9
9
|
|
|
10
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 holds several chats side by side, each working while another is shown: `/chat new` or `alt+n` opens one, `alt+1` … `alt+9` switch, and `turri -c` opens them all again - see [chats](chats.md). 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 - and once the conversation fills the screen, comes to rest swimming, whole: 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
|
-
To start a project, just say what you want - `create a small server in Java Spring Boot` - and turri starts it in the conversation, without asking how: it takes the stack you named, or chooses one this machine can build, and says it in a line; writes the module importing the stack from the standard library, `config.turri.yaml` (conformance tests off unless you ask for them) and the unit testing script (with `implement-unit-testing-script`, unless there is one); then the smallest thing that runs, regenerated at once, and while it regenerates the main thing you described and at most one more, on their happy path - nothing you did not describe. You say `go on`, or what to change, and every change after is \*\*\*plain it writes and regenerates (`src/new-project.ts`).
|
|
12
|
+
To start a project, just say what you want - `create a small server in Java Spring Boot` - and turri starts it in the conversation, without asking how: it takes the stack you named, or chooses one this machine can build, and says it in a line; writes the module importing the stack from the standard library, `config.turri.yaml` (conformance tests off unless you ask for them) and the unit testing script (with `implement-unit-testing-script`, unless there is one - in the background, on the coding model, while the first pulse waits for it); then the smallest thing that runs, regenerated at once, and while it regenerates the main thing you described and at most one more, on their happy path - nothing you did not describe. You say `go on`, or what to change, and every change after is \*\*\*plain it writes and regenerates (`src/new-project.ts`).
|
|
13
13
|
|
|
14
14
|
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.
|
|
15
15
|
|
|
@@ -26,7 +26,9 @@ While a skill runs, a line above the input shows its phases, every one by name -
|
|
|
26
26
|
| `glimpse` | the pulse a module's codebases would start next, and the whole prompt its coding agent would get - starting nothing |
|
|
27
27
|
| `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 |
|
|
28
28
|
| `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 |
|
|
29
|
-
| `
|
|
29
|
+
| `evaluate_tests` | have the [test advisor](test-advisor.md) judge whether functional specs need explicit testing scenarios, in the background - returning at once, the verdict coming back to the chat; whatever `test-advisor` says |
|
|
30
|
+
| `turn_on_conformance_tests` | turn conformance tests on for the project - `conformance-tests: true`, for this session and the next - once you asked for them, or agreed when the chat offered them. The chat has it while they are off, and `conform` and `evaluate_tests` while they are on |
|
|
31
|
+
| `parse` | parse \*\*\*plain - its syntax and structure, never its code or tests - regenerating nothing - a module whose functional specs have scenarios but which has no test reqs, or no conformance testing script for its stack, passes with a warning naming the next step (see [the steps of adding tests](regenerating.md#the-steps-of-adding-tests)) |
|
|
30
32
|
| `engine` | show or choose the model the code is regenerated on - see [below](#choosing-a-model) |
|
|
31
33
|
| `pulses` | the pulses - running, planned and finished - or one of them |
|
|
32
34
|
| `halt` | halt a pulse, a module's planned pulses, or what `activate` started |
|
|
@@ -53,10 +55,10 @@ A regeneration takes minutes, so in the TUI every tool that regenerates starts t
|
|
|
53
55
|
|
|
54
56
|
- **The panel** above the input, while something runs, shows on top what finished - a line a module, the module that finished last on top, its pulses newest 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 ✓ 4 conformance · ✓ 3 regenerate · … 2 earlier`, the newest that fit the width - then a rule, and under it each running pulse, the newest first - `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` - from the moment it is asked for: while its code is deleted for a start from scratch and what is left to regenerate is worked out, as well as while the planner, an agent, cuts it into pulses; a `/regenerate` says it began the moment it is sent. A \*\*\*plain change is looked at quietly, and shows only once the planner runs. 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.
|
|
55
57
|
- **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`).
|
|
56
|
-
- **A \*\*\*plain diff**, in the side panel, reads as an editor shows a diff: its [\*\*\*plain](https://plainlang.org/docs/) highlighted
|
|
58
|
+
- **A \*\*\*plain diff**, in the side panel, reads as an editor shows a diff: its [\*\*\*plain](https://plainlang.org/docs/) highlighted by turri's own grammar, which started from [plyn](https://github.com/plainlang/plyn)'s - section headers, the `# <id>` heading of a functional spec or a scenario, 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.
|
|
57
59
|
- **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.
|
|
58
60
|
|
|
59
|
-
Every item of the module's \*\*\*plain - a definition, a req, a functional spec with its
|
|
61
|
+
Every item of the module's \*\*\*plain - a definition, a req, a functional spec with its body and its scenarios, what turri plans and diffs by - is a row in the file's order - a functional spec's row is as its file writes it, its `# <func-id>` heading and its body's first line under it; opened, its scenarios follow, `***conformance tests***` and `***acceptance tests***` and the scenarios under them indented one step under the functional spec, a line of space after each scenario - its \*\*\*plain highlighted 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.
|
|
60
62
|
|
|
61
63
|
| Mark | Legend | Means |
|
|
62
64
|
| --- | --- | --- |
|
|
@@ -68,13 +70,13 @@ A regeneration takes minutes, so in the TUI every tool that regenerates starts t
|
|
|
68
70
|
| `!` | needs you | in the pulse that did not land and halted the module, or the planner found no plan |
|
|
69
71
|
| `−` | 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 |
|
|
70
72
|
|
|
71
|
-
`◇` and `◆` are both green
|
|
73
|
+
`◇` and `◆` are both green: a functional spec regenerated and not conformed is `◇` - and one with no scenarios stays there, its last rung, not tested. Tests written for an earlier text, or for scenarios that changed since, leave it at `◇`. Items and pulses are matched by func-id, and the cursor stays on a functional spec by its func-id whatever is written above it. Definitions and reqs are never conformed: `◇` is as far as they go. Test reqs are always shown; where no functional spec of the module has scenarios, nothing uses them, and the first says `not used: no scenarios`. 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.
|
|
72
74
|
|
|
73
75
|
**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.
|
|
74
76
|
|
|
75
|
-
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.
|
|
77
|
+
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`. A module whose functional specs have scenarios but which skipped a step of adding tests has it said after that - `· scenarios not tested: no test reqs`, or `· scenarios not tested: no conformance testing script` - until the step is taken ([the steps of adding tests](regenerating.md#the-steps-of-adding-tests)). `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.
|
|
76
78
|
|
|
77
|
-
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
|
|
79
|
+
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 the rest of its body and its scenarios, under `***conformance tests***` and `***acceptance tests***`, each `# <scenario-id>` and its text, as its test plain file 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.
|
|
78
80
|
|
|
79
81
|
`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), the newest first in each - running by when it started, queued by when it was queued, finished by when it ended - 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 under it a row for each file its \*\*\*plain diff changes, as a pulse of this session has - 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)).
|
|
80
82
|
|
|
@@ -83,12 +85,12 @@ A regeneration takes minutes, so in the TUI every tool that regenerates starts t
|
|
|
83
85
|
- **`/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).
|
|
84
86
|
- **`/sentinel`** says whether the [sentinel](sentinel.md) checks every pulse that finishes: `ask` - you are asked before it first opens in a session - `on`, or `off`; written to `config.turri.yaml`.
|
|
85
87
|
- **`/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).
|
|
86
|
-
- **`/regenerate [module | file.plain] [
|
|
88
|
+
- **`/regenerate [module | file.plain] [module:func-id ...] [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 starts at once, with nothing to confirm: it 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`. Functional specs named after the module, each as `<module>:<func-id>` - `/regenerate shorty shorty:shorten shorty:expand` - are regenerated though their \*\*\*plain did not change, on the model chosen for functionalities: as `turri regenerate shorty.plain shorty:shorten shorty:expand` does, and the chat's `regenerate` with `funcIds`. A bare func-id is refused: the module is always named. Code built before functional specs had func-ids - its record numbers them - is never built on: the module's whole chain has to be regenerated from scratch, and the chat asks you first, as for a start over.
|
|
87
89
|
- **`/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.
|
|
88
90
|
- **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. Once `/regenerate` has started pulses, turri says so in the chat too - what runs and what is planned, and that you need not wait for it - without starting anything more for it; a module already up to date gets the plan's answer alone. A halted pulse reaches it as every pulse's end does.
|
|
89
91
|
- **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.
|
|
90
92
|
- **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.
|
|
91
|
-
- **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 -
|
|
93
|
+
- **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 - the scenarios of its functional specs, by the test reqs - 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`. A functional spec with no test plain file, or an empty one, gets none: to have it tested, ask for conformance tests, or let the [test advisor](test-advisor.md) find that it needs them - either way the chat writes its scenarios with `change_plain`, and the loop tests them once the code is built. `conform` runs them now, 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.
|
|
92
94
|
- **A pulse that fails** - its work errors, or it does not pass - is run once more; one stopped on what only you can fix - the machine lacks something, a testing script no code could pass - is not. 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.
|
|
93
95
|
- **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.
|
|
94
96
|
- **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.
|
|
@@ -97,7 +99,7 @@ In `print`, `json` and `rpc` modes the process ends when the answer is written,
|
|
|
97
99
|
|
|
98
100
|
## The project's config.turri.yaml
|
|
99
101
|
|
|
100
|
-
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, `
|
|
102
|
+
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, `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).
|
|
101
103
|
|
|
102
104
|
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; 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).
|
|
103
105
|
|
|
@@ -179,7 +181,7 @@ It is in `.turri/ledger/` in the project, a JSON line an event, one file a run -
|
|
|
179
181
|
| --- | --- |
|
|
180
182
|
| runs | `run.started` (how it was started, turri's version and commit, the machine), `run.ended` |
|
|
181
183
|
| pulses | `job.queued` (with the functional specs it covers, by case), `job.diff` (its \*\*\*plain diff, whole, and where its lines are written - again each time steering moves it), `job.started`, every `job.stage`, `job.retried`, `job.finished` - its status, whether it landed, how long it ran, what it cost, how it ended |
|
|
182
|
-
| the work loop | `loop.halted`, `loop.stopped` (a pulse \*\*\*plain moved on from), `planner.failed`, `planner.fellBack` |
|
|
184
|
+
| the work loop | `loop.halted`, `loop.stopped` (a pulse \*\*\*plain moved on from), `loop.renamed` (a pure rename turri made itself - a func-id or a scenario-id, from and to), `planner.failed`, `planner.fellBack` |
|
|
183
185
|
| 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) |
|
|
184
186
|
| 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) |
|
|
185
187
|
| the gates | `gate.round`, `review.verdict`, `arbiter.ruling`, `judge.verdict` |
|
|
@@ -200,19 +202,19 @@ It is for looking up, never for deciding: what to build comes from the records a
|
|
|
200
202
|
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).
|
|
201
203
|
|
|
202
204
|
```bash
|
|
203
|
-
turri
|
|
205
|
+
turri parse <file.plain> # parse ***plain, its syntax and structure - no model, no API key
|
|
204
206
|
turri drift <file.plain> # what is left to regenerate - no model, starts nothing
|
|
205
207
|
turri regenerate <file.plain> # regenerate what the code is not built from yet, and what it requires
|
|
206
208
|
turri glimpse <file.plain> # plan it, and print the first job's prompt - starts nothing
|
|
207
|
-
turri regenerate <file.plain>
|
|
209
|
+
turri regenerate <file.plain> app:add-task # regenerate a functional spec again, changed or not
|
|
208
210
|
turri regenerate <file.plain> --from-scratch # delete the module's code and tests (its lineage keeps them), regenerate all of it
|
|
209
211
|
turri regenerate <file.plain> --from-scratch-chain # the same for every module of its requires chain, from the bottom
|
|
210
212
|
turri change-plain <file.plain> <diff> # apply a ***plain diff, and regenerate it
|
|
211
|
-
turri conform <file.plain>
|
|
213
|
+
turri conform <file.plain> app:add-task # black-box tests for one functional spec
|
|
212
214
|
turri lineage <module> # what each regeneration changed in a module, and when
|
|
213
215
|
turri ledger [pulse] # what turri did here: the last runs and their pulses, or one pulse
|
|
214
216
|
turri cultivate debug-specs # run a skill directly - by name, the project's too, or by its folder
|
|
215
217
|
turri release <file.plain> # release what is regenerated to dist/<module>, and activate it here
|
|
216
218
|
```
|
|
217
219
|
|
|
218
|
-
The ones that build take `--model provider/model-id`; `regenerate` also takes `--build-dir` and `--dry-run`, which plans the work, prints the prompt of each codebase's first pulse and starts nothing. Template directories come from `PLAIN_TEMPLATE_DIRS`. `--help` on any of them lists the rest.
|
|
220
|
+
`turri cultivate` runs a skill on the project's harness model, as the chat does, unless `--model` names another. The ones that build take `--model provider/model-id`; `regenerate` also takes `--build-dir` and `--dry-run`, which plans the work, prints the prompt of each codebase's first pulse and starts nothing. Template directories come from `PLAIN_TEMPLATE_DIRS`. `--help` on any of them lists the rest.
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
---
|
|
2
|
-
description: Bullet and continuation syntax for
|
|
2
|
+
description: Bullet and continuation syntax for the definitions, implementation reqs and test reqs of .plain files
|
|
3
3
|
---
|
|
4
4
|
|
|
5
5
|
# Rules for bullets and continuations in `.plain` files
|
|
6
6
|
|
|
7
|
-
These rules apply to
|
|
7
|
+
These rules apply to `***definitions***`, `***implementation reqs***`, and `***test reqs***`, and to concept explanations. The body of a functional spec, and of a scenario in a test plain file, is free text: these rules do not apply there (see `func-specs.md` and `test-plain-files.md`).
|
|
8
8
|
|
|
9
9
|
## No line-length limit
|
|
10
10
|
|
|
@@ -15,17 +15,17 @@ These rules apply to every section and to concept explanations.
|
|
|
15
15
|
|
|
16
16
|
## One statement per line
|
|
17
17
|
|
|
18
|
-
This is the preferred `.plain` authoring convention, not something
|
|
18
|
+
This is the preferred `.plain` authoring convention, not something turri enforces. A line break in these sections means one of exactly two things: a new `- ` statement, or a nested `- ` clarification of the statement above it. There is no third, typographic reason to break a line.
|
|
19
19
|
|
|
20
20
|
- Write each statement on a single line, however long
|
|
21
21
|
- Never break a line just for width: a bare continuation (an indented line without `- `) adds a line break that carries no structure — to every future reader and editing agent it is indistinguishable from a deliberate sub-point
|
|
22
22
|
- The test: if content deserves its own line, it deserves its own `- ` bullet; if it doesn't, keep it on the parent's line
|
|
23
|
-
- What
|
|
23
|
+
- What turri does enforce is the top level of `***definitions***`, `***implementation reqs***` and `***test reqs***`: every statement there must be a `- ` list item - a flush-left prose line is a syntax error. A bare continuation, by contrast, is accepted and folded into its parent item verbatim
|
|
24
24
|
|
|
25
25
|
BAD — line broken for width (bare continuation):
|
|
26
26
|
|
|
27
27
|
```plain
|
|
28
|
-
***
|
|
28
|
+
***definitions***
|
|
29
29
|
|
|
30
30
|
- :GatewayWebhook: hands off :StripeRequest: to :StripeIntegration:.handle(),
|
|
31
31
|
which returns a list of :EventEnvelope: dicts conforming to the gateway contract.
|
|
@@ -34,7 +34,7 @@ BAD — line broken for width (bare continuation):
|
|
|
34
34
|
GOOD — one valid list item:
|
|
35
35
|
|
|
36
36
|
```plain
|
|
37
|
-
***
|
|
37
|
+
***definitions***
|
|
38
38
|
|
|
39
39
|
- :GatewayWebhook: hands off :StripeRequest: to :StripeIntegration:.handle(), which returns a list of :EventEnvelope: dicts conforming to the gateway contract.
|
|
40
40
|
```
|
|
@@ -42,7 +42,7 @@ GOOD — one valid list item:
|
|
|
42
42
|
BEST — separate clarifications expressed as nested bullets:
|
|
43
43
|
|
|
44
44
|
```plain
|
|
45
|
-
***
|
|
45
|
+
***definitions***
|
|
46
46
|
|
|
47
47
|
- :GatewayWebhook: hands off :StripeRequest: to :StripeIntegration:.handle().
|
|
48
48
|
- The method returns a list of :EventEnvelope: dicts.
|
|
@@ -52,7 +52,7 @@ BEST — separate clarifications expressed as nested bullets:
|
|
|
52
52
|
## Presenting `.plain` examples
|
|
53
53
|
|
|
54
54
|
- Show every example under its owning section header
|
|
55
|
-
- Separate top-level list items with one blank line
|
|
55
|
+
- Separate top-level list items, functional specs and scenarios with one blank line
|
|
56
56
|
- Keep nested clarifications directly under their parent without a blank line
|
|
57
57
|
- BAD, WRONG, `Before:`, and `Too complex:` examples may intentionally demonstrate invalid syntax
|
|
58
58
|
|