@turri-dev/cli 0.0.0-stage → 0.1.1
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/.pi/themes/turri-dark.json +80 -0
- package/.pi/themes/turri-light.json +82 -0
- package/LICENSE +21 -0
- package/README.md +272 -2
- package/assets/jellyfish/dark-00.png +0 -0
- package/assets/jellyfish/dark-01.png +0 -0
- package/assets/jellyfish/dark-02.png +0 -0
- package/assets/jellyfish/dark-03.png +0 -0
- package/assets/jellyfish/dark-04.png +0 -0
- package/assets/jellyfish/dark-05.png +0 -0
- package/assets/jellyfish/dark-06.png +0 -0
- package/assets/jellyfish/dark-07.png +0 -0
- package/assets/jellyfish/dark-08.png +0 -0
- package/assets/jellyfish/dark-09.png +0 -0
- package/assets/jellyfish/dark-10.png +0 -0
- package/assets/jellyfish/dark-11.png +0 -0
- package/assets/jellyfish/dark-12.png +0 -0
- package/assets/jellyfish/dark-13.png +0 -0
- package/assets/jellyfish/dark-14.png +0 -0
- package/assets/jellyfish/fallback.txt +252 -0
- package/assets/jellyfish/light-00.png +0 -0
- package/assets/jellyfish/light-01.png +0 -0
- package/assets/jellyfish/light-02.png +0 -0
- package/assets/jellyfish/light-03.png +0 -0
- package/assets/jellyfish/light-04.png +0 -0
- package/assets/jellyfish/light-05.png +0 -0
- package/assets/jellyfish/light-06.png +0 -0
- package/assets/jellyfish/light-07.png +0 -0
- package/assets/jellyfish/light-08.png +0 -0
- package/assets/jellyfish/light-09.png +0 -0
- package/assets/jellyfish/light-10.png +0 -0
- package/assets/jellyfish/light-11.png +0 -0
- package/assets/jellyfish/light-12.png +0 -0
- package/assets/jellyfish/light-13.png +0 -0
- package/assets/jellyfish/light-14.png +0 -0
- package/bin/turri.mjs +29 -0
- package/dist/ask.js +149 -0
- package/dist/brand.js +50 -0
- package/dist/btw.js +174 -0
- package/dist/chat-models.js +37 -0
- package/dist/chat-skills.js +18 -0
- package/dist/cli.js +178 -0
- package/dist/config-command.js +197 -0
- package/dist/config-edit.js +367 -0
- package/dist/config.js +489 -0
- package/dist/conform-cli.js +29 -0
- package/dist/cultivate-cli.js +110 -0
- package/dist/drift-cli.js +25 -0
- package/dist/environment.js +70 -0
- package/dist/extension.js +2372 -0
- package/dist/genome.js +301 -0
- package/dist/graph.js +246 -0
- package/dist/identity.js +35 -0
- package/dist/init-config.js +511 -0
- package/dist/judge.js +62 -0
- package/dist/lineage-cli.js +13 -0
- package/dist/loop-cli.js +132 -0
- package/dist/loop-tools.js +225 -0
- package/dist/mature-tools.js +193 -0
- package/dist/model-check.js +117 -0
- package/dist/module-report.js +9 -0
- package/dist/pi-launch.js +31 -0
- package/dist/pi.js +813 -0
- package/dist/plain/check.js +76 -0
- package/dist/plain/concepts.js +213 -0
- package/dist/plain/errors.js +43 -0
- package/dist/plain/files.js +57 -0
- package/dist/plain/highlight.js +195 -0
- package/dist/plain/index.js +15 -0
- package/dist/plain/liquid.js +129 -0
- package/dist/plain/markdown.js +208 -0
- package/dist/plain/parser.js +752 -0
- package/dist/plain/plain.tmLanguage.json +124 -0
- package/dist/plain/spec.js +291 -0
- package/dist/plain/stdlib.js +44 -0
- package/dist/question-dialog.js +125 -0
- package/dist/questioning.js +109 -0
- package/dist/quiet.js +125 -0
- package/dist/regenerate/activity.js +187 -0
- package/dist/regenerate/agent-env.js +190 -0
- package/dist/regenerate/agent-shell.js +114 -0
- package/dist/regenerate/arbiter.js +133 -0
- package/dist/regenerate/audit.js +203 -0
- package/dist/regenerate/cases.js +83 -0
- package/dist/regenerate/change-view.js +102 -0
- package/dist/regenerate/choose-conformance.js +117 -0
- package/dist/regenerate/chunk-plan.js +625 -0
- package/dist/regenerate/codebase.js +75 -0
- package/dist/regenerate/command-check.js +312 -0
- package/dist/regenerate/compaction.js +20 -0
- package/dist/regenerate/confine.js +310 -0
- package/dist/regenerate/conform.js +133 -0
- package/dist/regenerate/conformance-agents.js +955 -0
- package/dist/regenerate/conformance.js +936 -0
- package/dist/regenerate/conversation-writes.js +100 -0
- package/dist/regenerate/credentials.js +88 -0
- package/dist/regenerate/debug-log.js +228 -0
- package/dist/regenerate/debugger.js +90 -0
- package/dist/regenerate/delivery.js +19 -0
- package/dist/regenerate/diagnose-job.js +40 -0
- package/dist/regenerate/diagnose.js +81 -0
- package/dist/regenerate/diff-view.js +240 -0
- package/dist/regenerate/drift.js +194 -0
- package/dist/regenerate/engine.js +118 -0
- package/dist/regenerate/environment-error.js +220 -0
- package/dist/regenerate/job-log.js +102 -0
- package/dist/regenerate/job-panel.js +336 -0
- package/dist/regenerate/jobs-tree.js +300 -0
- package/dist/regenerate/jobs.js +427 -0
- package/dist/regenerate/lanes.js +32 -0
- package/dist/regenerate/lineage.js +384 -0
- package/dist/regenerate/loop.js +1435 -0
- package/dist/regenerate/masking.js +95 -0
- package/dist/regenerate/notes.js +397 -0
- package/dist/regenerate/os-sandbox.js +243 -0
- package/dist/regenerate/paths.js +165 -0
- package/dist/regenerate/plain-palette.js +83 -0
- package/dist/regenerate/prompt.js +704 -0
- package/dist/regenerate/records.js +294 -0
- package/dist/regenerate/regenerate.js +598 -0
- package/dist/regenerate/regression.js +181 -0
- package/dist/regenerate/release.js +262 -0
- package/dist/regenerate/requires.js +241 -0
- package/dist/regenerate/resources.js +164 -0
- package/dist/regenerate/self-check.js +111 -0
- package/dist/regenerate/services.js +155 -0
- package/dist/regenerate/shell.js +319 -0
- package/dist/regenerate/side-panel.js +278 -0
- package/dist/regenerate/spec-map-view.js +574 -0
- package/dist/regenerate/spec-map.js +561 -0
- package/dist/regenerate/spec-steps.js +527 -0
- package/dist/regenerate/state.js +10 -0
- package/dist/regenerate/steer.js +91 -0
- package/dist/regenerate/steering.js +119 -0
- package/dist/regenerate/stop-report.js +161 -0
- package/dist/regenerate/submit-work.js +71 -0
- package/dist/regenerate/test-kinds.js +124 -0
- package/dist/regenerate/test-scripts.js +695 -0
- package/dist/regenerate/testing.js +406 -0
- package/dist/regenerate/transcript-view.js +1 -0
- package/dist/regenerate/unit-gate.js +20 -0
- package/dist/regenerate/unit-review.js +347 -0
- package/dist/regenerate/web.js +256 -0
- package/dist/regenerate/workspace.js +99 -0
- package/dist/regenerate-cli.js +68 -0
- package/dist/release-agent.js +114 -0
- package/dist/release-cli.js +131 -0
- package/dist/release-tools.js +148 -0
- package/dist/review/live.js +165 -0
- package/dist/rules.js +47 -0
- package/dist/self-invocation.js +110 -0
- package/dist/setup.js +245 -0
- package/dist/skill-progress.js +78 -0
- package/dist/skill.js +149 -0
- package/dist/skills.js +74 -0
- package/dist/spec-diff-cli.js +37 -0
- package/dist/spec-tools.js +155 -0
- package/dist/stack.js +158 -0
- package/dist/stacks.js +97 -0
- package/dist/stdlib.js +100 -0
- package/dist/terminal-images.js +46 -0
- package/dist/tools.js +242 -0
- package/dist/validate-cli.js +52 -0
- package/docs/README.md +58 -0
- package/docs/authoring-plain.md +245 -0
- package/docs/config.md +353 -0
- package/docs/deploy.md +109 -0
- package/docs/development.md +75 -0
- package/docs/headless.md +148 -0
- package/docs/images/how-turri-works.svg +50 -0
- package/docs/images/turri-banner.png +0 -0
- package/docs/installation.md +118 -0
- package/docs/parser.md +108 -0
- package/docs/regenerating.md +1116 -0
- package/docs/sandbox.md +233 -0
- package/docs/skills-and-gates.md +254 -0
- package/docs/theming.md +76 -0
- package/docs/using.md +454 -0
- package/genome/rules/bullet-continuation.md +69 -0
- package/genome/rules/concise-specs-examples.md +431 -0
- package/genome/rules/concise-specs-integration-examples.md +270 -0
- package/genome/rules/concise-specs.md +80 -0
- package/genome/rules/definitions.md +107 -0
- package/genome/rules/examples/integration-embedded-testing/run_conformance_tests_java.sh +163 -0
- package/genome/rules/examples/integration-embedded-testing/run_unittests_java.sh +51 -0
- package/genome/rules/exported-concepts.md +39 -0
- package/genome/rules/func-specs.md +129 -0
- package/genome/rules/impl-reqs.md +68 -0
- package/genome/rules/import-modules.md +48 -0
- package/genome/rules/integration-embedded-testing.md +276 -0
- package/genome/rules/integration-embedded.md +171 -0
- package/genome/rules/integration-standalone.md +135 -0
- package/genome/rules/integrations.md +137 -0
- package/genome/rules/linked-resources.md +69 -0
- package/genome/rules/module-structure.md +105 -0
- package/genome/rules/project-model.md +88 -0
- package/genome/rules/required-concepts.md +44 -0
- package/genome/rules/requires-modules.md +80 -0
- package/genome/rules/stdlib.md +73 -0
- package/genome/rules/test-reqs.md +57 -0
- package/genome/skills/add-acceptance-test/SKILL.md +100 -0
- package/genome/skills/add-concept/SKILL.md +71 -0
- package/genome/skills/add-functional-spec/SKILL.md +84 -0
- package/genome/skills/add-functional-specs/SKILL.md +118 -0
- package/genome/skills/add-implementation-requirement/SKILL.md +86 -0
- package/genome/skills/add-resource/SKILL.md +105 -0
- package/genome/skills/add-template/SKILL.md +64 -0
- package/genome/skills/add-test-requirement/SKILL.md +73 -0
- package/genome/skills/analyze-2-func-specs/SKILL.md +101 -0
- package/genome/skills/analyze-func-specs/SKILL.md +123 -0
- package/genome/skills/analyze-if-func-spec-too-complex/SKILL.md +211 -0
- package/genome/skills/break-down-func-spec/SKILL.md +205 -0
- package/genome/skills/brood/SKILL.md +229 -0
- package/genome/skills/brood/references/intent.md +35 -0
- package/genome/skills/brood/references/product.md +101 -0
- package/genome/skills/brood/references/stack.md +61 -0
- package/genome/skills/brood/references/testing.md +56 -0
- package/genome/skills/bud/SKILL.md +148 -0
- package/genome/skills/bud/references/checklist.md +44 -0
- package/genome/skills/bud/references/intent.md +30 -0
- package/genome/skills/check-plain-env/SKILL.md +285 -0
- package/genome/skills/consolidate-concepts/SKILL.md +192 -0
- package/genome/skills/create-import-module/SKILL.md +104 -0
- package/genome/skills/create-requires-module/SKILL.md +109 -0
- package/genome/skills/debug-specs/SKILL.md +188 -0
- package/genome/skills/implement-conformance-testing-script/SKILL.md +238 -0
- package/genome/skills/implement-conformance-testing-script/assets/run_conformance_tests_cypress.ps1 +337 -0
- package/genome/skills/implement-conformance-testing-script/assets/run_conformance_tests_golang.ps1 +111 -0
- package/genome/skills/implement-conformance-testing-script/assets/run_conformance_tests_java.sh +136 -0
- package/genome/skills/implement-conformance-testing-script/assets/run_conformance_tests_python.ps1 +103 -0
- package/genome/skills/implement-conformance-testing-script/assets/run_conformance_tests_python.sh +150 -0
- package/genome/skills/implement-unit-testing-script/SKILL.md +160 -0
- package/genome/skills/implement-unit-testing-script/assets/run_unittests_flutter.ps1 +87 -0
- package/genome/skills/implement-unit-testing-script/assets/run_unittests_golang.ps1 +73 -0
- package/genome/skills/implement-unit-testing-script/assets/run_unittests_java.sh +72 -0
- package/genome/skills/implement-unit-testing-script/assets/run_unittests_python.ps1 +81 -0
- package/genome/skills/implement-unit-testing-script/assets/run_unittests_python.sh +98 -0
- package/genome/skills/implement-unit-testing-script/assets/run_unittests_react.ps1 +87 -0
- package/genome/skills/init-plain-project/SKILL.md +117 -0
- package/genome/skills/init-plain-project/references/checklist.md +30 -0
- package/genome/skills/mature/SKILL.md +126 -0
- package/genome/skills/refactor-module/SKILL.md +196 -0
- package/genome/skills/resolve-section-ownership/SKILL.md +64 -0
- package/genome/skills/resolve-spec-conflict/SKILL.md +96 -0
- package/genome/skills/spawn/SKILL.md +163 -0
- package/package.json +68 -4
- package/stdlib/backend/common.plain +25 -0
- package/stdlib/backend/conformance/common.plain +12 -0
- package/stdlib/backend/conformance/go-gin.plain +9 -0
- package/stdlib/backend/conformance/go-nethttp.plain +9 -0
- package/stdlib/backend/conformance/java-springboot.plain +9 -0
- package/stdlib/backend/conformance/python-django.plain +9 -0
- package/stdlib/backend/conformance/python-fastapi.plain +9 -0
- package/stdlib/backend/conformance/python-flask.plain +9 -0
- package/stdlib/backend/conformance/rust-axum.plain +9 -0
- package/stdlib/backend/conformance/typescript-express.plain +9 -0
- package/stdlib/backend/conformance/typescript-fastify.plain +9 -0
- package/stdlib/backend/conformance/typescript-nestjs.plain +9 -0
- package/stdlib/backend/go-gin.plain +18 -0
- package/stdlib/backend/go-nethttp.plain +18 -0
- package/stdlib/backend/java-springboot.plain +19 -0
- package/stdlib/backend/python-django.plain +19 -0
- package/stdlib/backend/python-fastapi.plain +23 -0
- package/stdlib/backend/python-flask.plain +23 -0
- package/stdlib/backend/rust-axum.plain +17 -0
- package/stdlib/backend/typescript-express.plain +19 -0
- package/stdlib/backend/typescript-fastify.plain +19 -0
- package/stdlib/backend/typescript-nestjs.plain +19 -0
- package/stdlib/cli/common.plain +17 -0
- package/stdlib/cli/conformance/common.plain +11 -0
- package/stdlib/cli/conformance/go-cobra.plain +9 -0
- package/stdlib/cli/conformance/go-flag.plain +9 -0
- package/stdlib/cli/conformance/java-picocli.plain +9 -0
- package/stdlib/cli/conformance/python-argparse.plain +9 -0
- package/stdlib/cli/conformance/python-click.plain +9 -0
- package/stdlib/cli/conformance/python-typer.plain +9 -0
- package/stdlib/cli/conformance/rust-clap.plain +9 -0
- package/stdlib/cli/conformance/typescript-commander.plain +9 -0
- package/stdlib/cli/go-cobra.plain +14 -0
- package/stdlib/cli/go-flag.plain +14 -0
- package/stdlib/cli/java-picocli.plain +15 -0
- package/stdlib/cli/python-argparse.plain +18 -0
- package/stdlib/cli/python-click.plain +16 -0
- package/stdlib/cli/python-typer.plain +16 -0
- package/stdlib/cli/rust-clap.plain +14 -0
- package/stdlib/cli/typescript-commander.plain +17 -0
- package/stdlib/frontend/angular.plain +17 -0
- package/stdlib/frontend/common.plain +17 -0
- package/stdlib/frontend/conformance/angular.plain +5 -0
- package/stdlib/frontend/conformance/common.plain +11 -0
- package/stdlib/frontend/conformance/react-vite.plain +5 -0
- package/stdlib/frontend/conformance/sveltekit-spa.plain +5 -0
- package/stdlib/frontend/conformance/vue-vite.plain +5 -0
- package/stdlib/frontend/react-vite.plain +16 -0
- package/stdlib/frontend/sveltekit-spa.plain +16 -0
- package/stdlib/frontend/vue-vite.plain +16 -0
- package/stdlib/lang/go.plain +13 -0
- package/stdlib/lang/java.plain +12 -0
- package/stdlib/lang/python.plain +18 -0
- package/stdlib/lang/rust.plain +12 -0
- package/stdlib/lang/typescript.plain +13 -0
- package/stdlib/library/common.plain +15 -0
- package/stdlib/library/conformance/common.plain +9 -0
- package/stdlib/library/conformance/go.plain +9 -0
- package/stdlib/library/conformance/java-gradle.plain +9 -0
- package/stdlib/library/conformance/java-maven.plain +9 -0
- package/stdlib/library/conformance/python.plain +9 -0
- package/stdlib/library/conformance/rust.plain +9 -0
- package/stdlib/library/conformance/typescript.plain +9 -0
- package/stdlib/library/go.plain +12 -0
- package/stdlib/library/java-gradle.plain +13 -0
- package/stdlib/library/java-maven.plain +13 -0
- package/stdlib/library/python.plain +13 -0
- package/stdlib/library/rust.plain +12 -0
- package/stdlib/library/typescript.plain +15 -0
|
@@ -0,0 +1,245 @@
|
|
|
1
|
+
# Authoring \*\*\*plain
|
|
2
|
+
|
|
3
|
+
Rendering turns a spec into code; authoring is the half that decides what gets built. The code is
|
|
4
|
+
only ever as good as the spec - a fact in the wrong section is silently ignored, a functional spec
|
|
5
|
+
that is too big comes out half-built, two specs that contradict each other break each other's
|
|
6
|
+
tests. So this repo carries as much for writing specs as for rendering them: the authoring rules,
|
|
7
|
+
a library of skills that apply them, and independent review of what they write.
|
|
8
|
+
|
|
9
|
+
The rule underneath all of it: **the specs are the source of truth**. The generated code in
|
|
10
|
+
`plain_modules/` is never edited by hand. A bug is fixed in the spec, and a change that is wanted
|
|
11
|
+
goes into the spec - that is what persists and what the next render builds from.
|
|
12
|
+
|
|
13
|
+
## A \*\*\*plain project
|
|
14
|
+
|
|
15
|
+
```
|
|
16
|
+
my-project/
|
|
17
|
+
app.plain # a root module: what the software does
|
|
18
|
+
users.plain # another module, perhaps one that `requires` app
|
|
19
|
+
template/ # import modules: shared definitions and reqs
|
|
20
|
+
resources/ # files specs link to: schemas, API specs, sample data
|
|
21
|
+
test_scripts/ # scripts that run the unit and conformance tests
|
|
22
|
+
plain_modules/ # generated - code, tests, history; never edited by hand
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
## What a spec is made of
|
|
26
|
+
|
|
27
|
+
A `.plain` file is one module: optional YAML frontmatter, then up to four sections, in this order.
|
|
28
|
+
|
|
29
|
+
| section | holds | e.g. |
|
|
30
|
+
| --- | --- | --- |
|
|
31
|
+
| `***definitions***` | the concepts, written `:CamelCase:` - every concept is defined before it is used | `- :Task: is a unit of work with a title and a due date.` |
|
|
32
|
+
| `***implementation reqs***` | HOW it is built: stack, architecture, coding standards, and everything about unit tests | `- :Implementation: is written in Python 3.12.` |
|
|
33
|
+
| `***test reqs***` | everything about the black-box conformance tests: framework, how they run, mocking | `- :ConformanceTests: use pytest and call the CLI as a subprocess.` |
|
|
34
|
+
| `***functional specs***` | WHAT it does - observable, language-agnostic behaviour, one functionality per item, in the order they are built | `- A :User: can add a :Task:.` |
|
|
35
|
+
|
|
36
|
+
Under a functional spec, a nested `***acceptance tests***` list states how that one
|
|
37
|
+
functionality is checked end to end.
|
|
38
|
+
|
|
39
|
+
The frontmatter connects modules:
|
|
40
|
+
|
|
41
|
+
| field | |
|
|
42
|
+
| --- | --- |
|
|
43
|
+
| `description` | one line on what the module is |
|
|
44
|
+
| `import` | take the definitions, implementation reqs and test reqs of an import module - shared content, no functional specs of its own |
|
|
45
|
+
| `requires` | build on top of another module: its functional specs and its generated code come first |
|
|
46
|
+
| `exported_concepts` | which concepts a module that `requires` this one may use |
|
|
47
|
+
| `required_concepts` | concepts an import module expects the importing module to define |
|
|
48
|
+
|
|
49
|
+
Beyond that: a spec links to files under `resources/` with markdown link syntax, and the file's
|
|
50
|
+
contents go to the renderer with the spec; lines starting with `>` are comments for people; and
|
|
51
|
+
files are processed with Liquid first, so a template can be included with parameters.
|
|
52
|
+
|
|
53
|
+
**Section ownership is the rule that bites.** The renderer reads each kind of fact only from the
|
|
54
|
+
section that owns it. A tech choice written into a functional spec, or a behaviour written into
|
|
55
|
+
the implementation reqs, is not flagged - it is simply not read.
|
|
56
|
+
|
|
57
|
+
## The standard library
|
|
58
|
+
|
|
59
|
+
turri ships a standard library of import modules in `stdlib/` that settle everything about a
|
|
60
|
+
generic piece of software on one stack except what it does: its language and framework, layout,
|
|
61
|
+
configuration, error handling, logging, shutdown, and how it is unit and conformance tested. A spec
|
|
62
|
+
imports one and writes only its own concepts and functional specs:
|
|
63
|
+
|
|
64
|
+
```plain
|
|
65
|
+
---
|
|
66
|
+
import:
|
|
67
|
+
- stdlib/backend/python-flask
|
|
68
|
+
- stdlib/backend/conformance/python-flask # leave it out to skip conformance tests
|
|
69
|
+
---
|
|
70
|
+
|
|
71
|
+
***functional specs***
|
|
72
|
+
|
|
73
|
+
- ...
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
| kind | stacks - each with `stdlib/<kind>/conformance/<stack>` beside it |
|
|
77
|
+
| --- | --- |
|
|
78
|
+
| `stdlib/backend/` | `python-fastapi`, `python-flask`, `python-django`, `typescript-express`, `typescript-fastify`, `typescript-nestjs`, `java-springboot`, `go-nethttp`, `go-gin`, `rust-axum` |
|
|
79
|
+
| `stdlib/frontend/` | `react-vite`, `vue-vite`, `sveltekit-spa`, `angular` |
|
|
80
|
+
| `stdlib/cli/` | `python-typer`, `python-click`, `python-argparse`, `typescript-commander`, `go-cobra`, `go-flag`, `rust-clap`, `java-picocli` |
|
|
81
|
+
| `stdlib/library/` | `python`, `typescript`, `java-maven`, `java-gradle`, `go`, `rust` |
|
|
82
|
+
|
|
83
|
+
It is built in three layers, and a stack imports the other two:
|
|
84
|
+
|
|
85
|
+
- **The language** - `stdlib/lang/python`, `typescript`, `java`, `go`, `rust`: the version, the
|
|
86
|
+
dependency manager, formatting, linting and type checking (ruff and mypy for Python; Prettier, ESLint
|
|
87
|
+
and `tsc --noEmit` for TypeScript), how a failure is reported (an exception, a returned error, a
|
|
88
|
+
`Result`), the logging library, and the unit test framework and layout. A module on a stack the library does not have can import its language alone.
|
|
89
|
+
The formatter, linter and type checker a language names are enforced by the testing scripts, which
|
|
90
|
+
run them with the tests - the unit testing script on the code, the conformance testing script on the
|
|
91
|
+
tests - and count them as the tests (see [the static checks](regenerating.md#the-static-checks)).
|
|
92
|
+
- **The kind's `common`** - what every stack of a kind shares:
|
|
93
|
+
|
|
94
|
+
| kind | settles |
|
|
95
|
+
| --- | --- |
|
|
96
|
+
| backend | settings from the environment into one typed object, checked at start (exit 1 when one is missing); handler, service and repository layers; domain errors turned in one place into `{"error": {"code", "message"}}` - `not_found` 404, `conflict` 409, `validation_error` 422 with `details` per field, `unauthenticated` 401, `forbidden` 403, `internal_error` 500 with no message and the stack trace logged; JSON logs on standard output with `timestamp`, `level`, `logger`, `message` and `request_id`, level from `LOG_LEVEL`, one line per request, never a secret or a body; `X-Request-ID` taken or made and sent back; shutdown on SIGTERM within 10 seconds |
|
|
97
|
+
| frontend | one API layer that turns a failed call into an `ApiError` with the backend's error code, or `network_error`; errors shown in place, and a page for anything else at the root; one logger module, no `console` elsewhere; accessible roles and test ids |
|
|
98
|
+
| cli | settings from flags, then `<TOOL>_<SETTING>`, then defaults; the work apart from parsing; errors as `Error: <message>` on standard error, exit 0, 1, 2 for a usage error, 130 on Ctrl-C, a stack trace only with `--debug`; logs on standard error, warnings by default, `--verbose`, `--debug` |
|
|
99
|
+
| library | one public entry point; one base error type, with a dependency's errors wrapped; logs through the language's logging with no output set up; no environment variables, output or global state |
|
|
100
|
+
|
|
101
|
+
- **The stack** - the framework and how it does each of those: for `python-flask`, an application
|
|
102
|
+
factory, one Blueprint per resource, Pydantic for bodies, `@app.errorhandler` for the error body,
|
|
103
|
+
structlog JSON logs with the request id bound in a hook, Gunicorn. For `go-cobra`, `RunE`
|
|
104
|
+
commands with Cobra's own error printing off, so `main` alone writes the error and picks the exit
|
|
105
|
+
code, and slog on standard error.
|
|
106
|
+
|
|
107
|
+
Each conformance template imports its kind's `conformance/common`: a backend's tests start the
|
|
108
|
+
built service on a free port and reach it over HTTP only, checking a failure by status and
|
|
109
|
+
`error.code`; a frontend's run the served build in Chromium through Playwright with the backend
|
|
110
|
+
stood in; a command-line tool's run the built tool as a process; a library's go through its public
|
|
111
|
+
entry point.
|
|
112
|
+
|
|
113
|
+
- **Conformance tests apart.** A spec without the conformance template has no test reqs, so no
|
|
114
|
+
conformance tests are written or run for it. Turning them on later is adding that one import.
|
|
115
|
+
- **A reserved name.** An import that starts with `stdlib/` is always turri's template, never
|
|
116
|
+
a file of the project's. A project with a module at such a path - `stdlib/backend/python-fastapi.plain`
|
|
117
|
+
beside the spec, in its `template/` folder or in `PLAIN_TEMPLATE_DIRS` - gets an error saying the
|
|
118
|
+
name is reserved, so which one a spec builds on never depends on what is on disk beside it.
|
|
119
|
+
- **No concepts.** Its requirements are on the predefined `:Implementation:`, `:UnitTests:` and
|
|
120
|
+
`:ConformanceTests:` only, so it never collides with a spec's own definitions.
|
|
121
|
+
- **Pinned to a toolchain.** It names its stack's toolchain - Python 3.12, Node.js 24, Java 21, Go 1.25, Rust -
|
|
122
|
+
which the agents use from this machine; `turri doctor` says when the machine lacks it.
|
|
123
|
+
- **Not storage, not the testing scripts.** A module with a database says which on top of its
|
|
124
|
+
stack. It still needs its unit testing script, and a conformance testing script when it has
|
|
125
|
+
conformance tests.
|
|
126
|
+
- **Known to the agents.** The `stdlib` rule, in every spec-writing agent's system prompt, says when
|
|
127
|
+
to import which, and lists every template with what it is and the folder it is in, read from
|
|
128
|
+
`stdlib/` when the rules load. `spawn` and `brood` import a stack when the one chosen
|
|
129
|
+
is in it, and its conformance template when conformance tests are on.
|
|
130
|
+
- **Only what this machine can build.** The agents use this machine's toolchains and install none, so
|
|
131
|
+
a Go stack on a laptop without Go stops at its first build. Every spec-writing agent and the
|
|
132
|
+
conversation are told which of the standard library's languages the machine can build now - Python
|
|
133
|
+
needs `uv`, TypeScript and every frontend `node` and `npm`, Go `go`, Rust `cargo`, Java a JDK
|
|
134
|
+
(`src/stack.ts`, `toolchainsBlock`) - and `spawn` and `brood` offer, and choose, only stacks in those.
|
|
135
|
+
A stack the person names in a language the machine lacks is not refused: they are told what to install
|
|
136
|
+
first, and that `turri doctor` checks it.
|
|
137
|
+
|
|
138
|
+
A requirement beyond the stack - storage, an outside service - is added on top of it. A module that
|
|
139
|
+
differs from its stack on something the stack settles - another framework, another layout - writes
|
|
140
|
+
its own instead: an import cannot be taken back in part.
|
|
141
|
+
|
|
142
|
+
## The rules
|
|
143
|
+
|
|
144
|
+
`genome/rules/` holds the authoring rules. They are in the system prompt of every agent that
|
|
145
|
+
writes specs - the conversation, the interview skills, the reviewers - so they are not something
|
|
146
|
+
an agent has to decide to go and read.
|
|
147
|
+
|
|
148
|
+
| rule | covers |
|
|
149
|
+
| --- | --- |
|
|
150
|
+
| `module-structure.md` | the file skeleton, frontmatter fields, section order, module kinds, section ownership |
|
|
151
|
+
| `definitions.md` | concept syntax, predefined concepts, define before use, no circular definitions, granularity |
|
|
152
|
+
| `func-specs.md` | voice, the complexity limit, chronological order, no conflicts, language agnosticism, testability, acceptance tests |
|
|
153
|
+
| `impl-reqs.md` | HOW not WHAT, unit tests live here, what belongs and what does not |
|
|
154
|
+
| `test-reqs.md` | conformance tests only, what belongs, test types |
|
|
155
|
+
| `concise-specs.md` (+ two example banks) | keeping specs short enough for a person to review: no obvious lines, plain words, no uncheckable adjectives, no hedging |
|
|
156
|
+
| `bullet-continuation.md` | bullets and continuation lines in every section |
|
|
157
|
+
| `linked-resources.md` | a linked resource is one text file on disk, or a PNG or JPEG image for a model that reads images; protocol artifacts are linked, never transcribed |
|
|
158
|
+
| `import-modules.md`, `requires-modules.md` | the two ways modules build on each other |
|
|
159
|
+
| `exported-concepts.md`, `required-concepts.md` | which concepts cross module boundaries |
|
|
160
|
+
| `integrations.md` (+ standalone, embedded, embedded testing) | specs for REST API integrations, deployed standalone or embedded in an existing codebase |
|
|
161
|
+
| `stdlib.md` | the standard library: when to import which template, and every template with what it is and where it is, read from `stdlib/` |
|
|
162
|
+
|
|
163
|
+
The ones that shape a spec most:
|
|
164
|
+
|
|
165
|
+
- **One functionality is at most ~200 lines of code.** A bigger one is split before it is written
|
|
166
|
+
(`analyze-if-func-spec-too-complex`, `break-down-func-spec`).
|
|
167
|
+
- **Functional specs are in build order.** Each is rendered on top of the ones before it.
|
|
168
|
+
- **They state facts, with a concrete subject.** "A :User: can add a :Task:" - not "Implement
|
|
169
|
+
adding tasks", not "The system should…".
|
|
170
|
+
- **Nothing a test cannot check.** No `robust`, `seamless`, `as appropriate`; state the checkable
|
|
171
|
+
fact or cut the word.
|
|
172
|
+
- **No two specs contradict each other.** A conflict is found and resolved when a spec is added,
|
|
173
|
+
not when its tests fail.
|
|
174
|
+
|
|
175
|
+
## Writing specs with turri
|
|
176
|
+
|
|
177
|
+
In a conversation (`turri`), say what you want and the matching skill runs. Two build a
|
|
178
|
+
project from scratch, and write each spec change as a git diff that is built in the background the
|
|
179
|
+
moment it is written. `brood` interviews you one question at a time and writes the specs as
|
|
180
|
+
you answer; `spawn`, its quick twin, asks only what you want to build and which stack - or leaves the stack
|
|
181
|
+
to it - and writes the rest on its own:
|
|
182
|
+
|
|
183
|
+
| skill | phases | how a spec change is written |
|
|
184
|
+
| --- | --- | --- |
|
|
185
|
+
| `brood` | intent → stack → testing → product, feature by feature | first, how many questions the stack and product phases may ask - light 3, medium 5, thorough 10 or best judgement, broader with fewer, covering the same ground - and what they leave open turri decides itself, each decision a spec change of its own; every product answer is still a spec change, built before the next question; in testing, whether there are conformance tests, their `***test reqs***` and script - written as files, building nothing; then one spec change per answer, across whatever sections it needs, as a git diff with `write_spec_diff`, rendered as a render spec diff job and conformance tested as it is built |
|
|
186
|
+
| `spawn` | intent → groundwork → product | only the intent and the stack are asked - the stack one question, `You choose` first, skipped when the user named one; the stack and the unit tests are written as files, with conformance tests off unless the user asks for them; then one spec change per feature, in build order, as a git diff with `write_spec_diff`, one after another with no answer between them. Changes after it are asked for in the conversation, as spec diffs, and what is left to build is worked out again from the spec |
|
|
187
|
+
|
|
188
|
+
`mature` starts from a spec you already have, rough or hand-written, and runs on its own until
|
|
189
|
+
it is fully rendered - every functionality built from the spec as it stands, its tests passing. It
|
|
190
|
+
renders what is not built and watches the agents building it; when one struggles on something the
|
|
191
|
+
spec leaves open or unclear - sent back again and again, the judge going back and forth, a spec
|
|
192
|
+
conflict - it writes what the spec should say as a spec diff, stops that build, and renders it again.
|
|
193
|
+
What the builds had to decide themselves - their assumptions, pitfalls, a failed build's diagnosis -
|
|
194
|
+
goes into the spec the same way, when a user, a caller or a test can see it. It never asks: it takes
|
|
195
|
+
the reading that fits the rest of the spec, and marks each such decision in its report. It leaves
|
|
196
|
+
code, environment and turri problems to the builds, and stops when a functionality keeps
|
|
197
|
+
struggling after the spec was clarified, when two cycles change nothing, or after twenty.
|
|
198
|
+
|
|
199
|
+
For an existing project, `bud` works the same way, scoped to one feature: intent → scope →
|
|
200
|
+
product → review. After a short feature-intent interview it asks product questions one at a time -
|
|
201
|
+
a trigger and its outcome, an edge case, a rule - and every answer is one spec change, a git diff
|
|
202
|
+
written with `write_spec_diff` and built the moment it is written. A change to something already
|
|
203
|
+
specified, built or still to build, is put to the user first and is a spec diff too: what is left to
|
|
204
|
+
build is worked out again from the spec.
|
|
205
|
+
|
|
206
|
+
What keeps the interview honest is the harness, not the interviewer:
|
|
207
|
+
|
|
208
|
+
- **Every question offers choices** (`ask_user` refuses a question without them), and each
|
|
209
|
+
answer maps to one spec change.
|
|
210
|
+
- **Every answer is written before the next question.** In `brood`'s product phase `ask_user` refuses
|
|
211
|
+
a new question until the last answer is a spec change - see [skills and gates](skills-and-gates.md).
|
|
212
|
+
- **Each phase ends on the person's confirmation** of a short summary, not on the interviewer's word.
|
|
213
|
+
|
|
214
|
+
## The skills
|
|
215
|
+
|
|
216
|
+
Every skill in `genome/skills/` can be run from a conversation; most are also used by the bigger ones. A project's own,
|
|
217
|
+
in `.turri/skills/` or `.agents/skills/`, can be too (see [skills and gates](skills-and-gates.md#a-projects-own-skills)).
|
|
218
|
+
|
|
219
|
+
| to… | skills |
|
|
220
|
+
| --- | --- |
|
|
221
|
+
| start a project | `brood`, `spawn`, `init-plain-project` |
|
|
222
|
+
| make a rough spec build | `mature` |
|
|
223
|
+
| add to a spec | `bud`, `add-functional-spec`, `add-functional-specs`, `add-concept`, `add-implementation-requirement`, `add-test-requirement`, `add-acceptance-test`, `add-resource`, `add-template` |
|
|
224
|
+
| check what is written | `analyze-if-func-spec-too-complex`, `analyze-2-func-specs`, `analyze-func-specs`, `resolve-section-ownership` |
|
|
225
|
+
| fix what is written | `break-down-func-spec`, `resolve-spec-conflict`, `debug-specs` |
|
|
226
|
+
| restructure modules | `create-import-module`, `create-requires-module`, `refactor-module`, `consolidate-concepts` |
|
|
227
|
+
| set up the project to build | `implement-unit-testing-script`, `implement-conformance-testing-script`, `check-plain-env` |
|
|
228
|
+
|
|
229
|
+
`debug-specs` is the one for a running application that misbehaves: it reads the generated code
|
|
230
|
+
to find the cause, traces it back to the spec, and fixes only the `.plain` files.
|
|
231
|
+
|
|
232
|
+
## Checking a spec
|
|
233
|
+
|
|
234
|
+
```bash
|
|
235
|
+
turri validate app.plain # or validate in a conversation
|
|
236
|
+
```
|
|
237
|
+
|
|
238
|
+
It parses the spec with this repo's own parser, resolves its imports and required modules, and
|
|
239
|
+
reports every syntax, concept and structure error with its line - no model, no API key. The
|
|
240
|
+
interview skills run it before a phase ends, and `write_spec_diff`
|
|
241
|
+
refuses a change that leaves a file that does not parse. Pass the project directory to check every
|
|
242
|
+
module at once.
|
|
243
|
+
|
|
244
|
+
What `check` cannot see - a fact in the wrong section, a spec that is too big, two that conflict,
|
|
245
|
+
wording no test can check - is what the rules, the skills and the reviewers are for.
|
package/docs/config.md
ADDED
|
@@ -0,0 +1,353 @@
|
|
|
1
|
+
# The project config
|
|
2
|
+
|
|
3
|
+
A project says how its specs are built in `config.turri.yaml`, at the project root. Everything in it
|
|
4
|
+
is optional: with no file at all, turri finds the testing scripts on its own, turns conformance
|
|
5
|
+
tests on wherever a module has test reqs, and runs on pi's configured model. A project whose
|
|
6
|
+
parts are on different stacks - a backend and a frontend - adds a file per stack,
|
|
7
|
+
`config.turri.<stack>.yaml`, beside it, and chooses the stack to build with (see
|
|
8
|
+
[stacks](#stacks)). turri never writes a `config.yaml`: it may be another tool's - codeplain's - and
|
|
9
|
+
is read only by a project that has no file of turri's yet (see [the old config.yaml](#the-old-configyaml)).
|
|
10
|
+
|
|
11
|
+
`turri init-config` starts one: the two models, a strong one to talk to and a smaller one to write the code. It
|
|
12
|
+
asks which vendor's models you use - `--vendor` says it without asking, and with neither, as from a script, it is
|
|
13
|
+
`other`:
|
|
14
|
+
|
|
15
|
+
| vendor | harness-model | coding-model | key pi reads |
|
|
16
|
+
| --- | --- | --- | --- |
|
|
17
|
+
| `anthropic` | `anthropic/claude-opus-5-5` | `anthropic/claude-haiku-5-5` | `ANTHROPIC_API_KEY` |
|
|
18
|
+
| `openai` | `openai/gpt-6-astra` | `openai/gpt-6-luna` | `OPENAI_API_KEY` |
|
|
19
|
+
| `google` | `google/gemini-3.1-pro-preview` | `google/gemini-3.1-flash-lite` | `GEMINI_API_KEY` |
|
|
20
|
+
| `amazon-bedrock`, Anthropic | `amazon-bedrock/<region>.anthropic.claude-opus-5-5` | `amazon-bedrock/<region>.anthropic.claude-haiku-5-5` | `AWS_PROFILE` or `AWS_BEARER_TOKEN_BEDROCK`, and `AWS_REGION` |
|
|
21
|
+
| `amazon-bedrock`, OpenAI | `amazon-bedrock/<region>.openai.gpt-6-astra` | `amazon-bedrock/<region>.openai.gpt-6-luna` | `AWS_PROFILE` or `AWS_BEARER_TOKEN_BEDROCK`, and `AWS_REGION` |
|
|
22
|
+
| `other` | `anthropic/claude-opus-5-5` | `google/gemini-3.1-flash-lite` | `ANTHROPIC_API_KEY`, `GEMINI_API_KEY` |
|
|
23
|
+
|
|
24
|
+
Amazon Bedrock asks two more things - whose models, Anthropic's or OpenAI's (`--maker`), and the region of the
|
|
25
|
+
inference profile they run in (`--region`): `global`, `us`, `eu`, `jp` or `au` for Anthropic's, `global` or `us` for
|
|
26
|
+
OpenAI's. It says which `AWS_REGION` to set for it - pi calls Bedrock in `us-east-1` when none is. A `config.turri.yaml`
|
|
27
|
+
already there keeps everything but its two models, which it sets; `--force` writes the whole file again.
|
|
28
|
+
|
|
29
|
+
Each is written with how hard it thinks - `harness-model: openai/gpt-6-astra:medium`, `coding-model:
|
|
30
|
+
openai/gpt-6-luna:low` - so the file shows it. The reviews and judges run on the coding model but think medium (see
|
|
31
|
+
[models per role](#models-per-role)).
|
|
32
|
+
|
|
33
|
+
They are an example, not a requirement - any model [pi](https://pi.dev) supports works, signed in any way pi
|
|
34
|
+
supports; `other` is the starting point for another provider. What we recommend is that split, a capable model to
|
|
35
|
+
talk to and a smaller, cheaper one for the coding. Once written, it says what to do next: the key to set in your
|
|
36
|
+
shell - or `/login` in turri, which pi keeps in `~/.pi/agent/auth.json` - and that the file belongs to that folder:
|
|
37
|
+
turri reads the nearest `config.turri.yaml` upwards from where it starts, so one written anywhere else is not seen.
|
|
38
|
+
|
|
39
|
+
Below them it writes every other setting out, each with what it does and the value turri uses for it - `review: false`,
|
|
40
|
+
`turn-limit: 120`, `parallel-jobs` as this machine's cores make it - so the file says how the project is built, and you
|
|
41
|
+
change one by changing it. The optional ones, which have no value of their own - the testing scripts (written in by the
|
|
42
|
+
skill that writes them), `module`, `conformance-budget`, `judge-model`, `models` - are there commented out, each with an
|
|
43
|
+
example: uncomment one to use it. A key taken out of the file is still read as the table below says.
|
|
44
|
+
|
|
45
|
+
Beside a `config.yaml` - codeplain's - the new file takes from it the keys turri reads too, each in its place, its
|
|
46
|
+
testing scripts above all, and says which it left (see [the old config.yaml](#the-old-configyaml)); `config.yaml`
|
|
47
|
+
is left as it is. A `config.turri.yaml` already there keeps everything but its two models, which it sets - `--force` writes the whole
|
|
48
|
+
file again - and a `config.yaml` turri read until then is copied to it first. It takes a folder other than the current
|
|
49
|
+
one as its argument.
|
|
50
|
+
|
|
51
|
+
## Where it is read from
|
|
52
|
+
|
|
53
|
+
A spec belongs to its **project**: the nearest folder, from the spec's own folder up, that holds a
|
|
54
|
+
`config.turri.yaml`, a stack's `config.turri.<stack>.yaml`, the old `config.yaml` or a `plain_modules/`.
|
|
55
|
+
Without any of them, the spec's own folder is the project. That folder's config is the one read, and
|
|
56
|
+
its `plain_modules/` is where the code is built, wherever turri was started. So a project that keeps
|
|
57
|
+
its specs in `plain/`, with `plain/config.turri.yaml`, builds `plain/app.plain` by
|
|
58
|
+
`plain/config.turri.yaml` even when `turri` runs in the folder above.
|
|
59
|
+
|
|
60
|
+
When it is read:
|
|
61
|
+
|
|
62
|
+
- **In the chat**, afresh: the chat's agent is told what it says on every turn, and each render
|
|
63
|
+
reads it when it is queued. An edit to the file holds from the next turn and the next render, with
|
|
64
|
+
no restart. The agent is told the config of the session's folder and of the projects one folder
|
|
65
|
+
down.
|
|
66
|
+
- **On the command line**, once, when the command starts. `turri regenerate` takes `module:` from
|
|
67
|
+
the folder it is run in, and the rest from the spec's own project.
|
|
68
|
+
|
|
69
|
+
## Changing it from the chat
|
|
70
|
+
|
|
71
|
+
`/turri-config` changes any key in the chat, and writes it into the `config.turri.yaml` of the folder turri was started in -
|
|
72
|
+
its comments, and the commented-out examples, kept. Alone, it lists every key with what it is now, and asks for the
|
|
73
|
+
value of the one you pick the way the key takes it: `true` or `false`, one of its words, a number from a few offered or
|
|
74
|
+
typed, a model from those this machine can reach and then how hard it thinks, a script or a `.plain` file the project
|
|
75
|
+
has, rules ticked on and off. `/turri-config review` asks for review's value; `/turri-config review false` sets it, and
|
|
76
|
+
`/turri-config review unset` takes it out, back to its default. Completion offers the keys, then each one's values.
|
|
77
|
+
Each role's model and turn limit is `models.<role>` and `models.<role>.turn-limit` - `models.coding`,
|
|
78
|
+
`models.planner.turn-limit`.
|
|
79
|
+
|
|
80
|
+
Each change says when it takes effect. None needs turri started again - what turri fixes as it starts is changed as it
|
|
81
|
+
runs:
|
|
82
|
+
|
|
83
|
+
| when | keys |
|
|
84
|
+
| --- | --- |
|
|
85
|
+
| now | `harness-model` and `models.chat` - the conversation switches to it; `parallel-jobs` - a pulse waiting for a slot starts if there is one now; `allow-to-stop-jobs` |
|
|
86
|
+
| your next message | `rules-off` - for the conversation; a skill's agents from the next skill run |
|
|
87
|
+
| the next pulse - one running keeps what it started with | `coding-model`, `judge-model`, every other `models.<role>`, `review`, `turn-limit`, `compact-at`, `agent-env`, `toolchains` |
|
|
88
|
+
| the next time the work is planned | `conformance-tests`, `conformance-budget`, `max-specs-per-pulse`, `max-lines-per-pulse` |
|
|
89
|
+
| the next test run - the running pulse's too | the testing scripts, `testing-scripts-run-on` |
|
|
90
|
+
| the next pulse that lands - the running one too | `release-after-pulse` |
|
|
91
|
+
| the next regeneration that names no `.plain` file | `module` |
|
|
92
|
+
|
|
93
|
+
A coding model chosen this session - `--coding-model`, or `engine` - gives way to one set here: the last word wins. It
|
|
94
|
+
says, too, what wins over the change: `models.chat` over `harness-model` (and so on), and a stack's
|
|
95
|
+
`config.turri.<stack>.yaml`, which is not changed here, while that stack is chosen. In a project still read from
|
|
96
|
+
`config.yaml`, the first change copies it to `config.turri.yaml` first - see [the old config.yaml](#the-old-configyaml).
|
|
97
|
+
|
|
98
|
+
## The keys
|
|
99
|
+
|
|
100
|
+
```yaml
|
|
101
|
+
module: app.plain # what to build when no .plain file is named
|
|
102
|
+
harness-model: # the model you chat with
|
|
103
|
+
model: anthropic/claude-opus-5-5
|
|
104
|
+
thinking: high
|
|
105
|
+
coding-model: # every render (default: harness-model)
|
|
106
|
+
model: amazon-bedrock/eu.anthropic.claude-sonnet-5
|
|
107
|
+
thinking: low
|
|
108
|
+
judge-model: # the verdicts of a conformance run (default: coding-model)
|
|
109
|
+
model: amazon-bedrock/eu.anthropic.claude-sonnet-5
|
|
110
|
+
thinking: medium
|
|
111
|
+
conformance-tests: true # write and run conformance tests where there are test reqs (default: false)
|
|
112
|
+
unittests-script: test_scripts/run_unittests_python.sh
|
|
113
|
+
conformance-tests-script: test_scripts/run_conformance_tests_python.sh
|
|
114
|
+
testing-scripts-run-on: copy # what the testing scripts run on: copy - a fresh copy (default: in-place - the code itself)
|
|
115
|
+
turn-limit: 150 # seconds each agent turn has (default: 120; 0 for none)
|
|
116
|
+
models: # each agent role's model, thinking and turn limit - see below
|
|
117
|
+
default: amazon-bedrock/global.openai.gpt-6-luna
|
|
118
|
+
coding: { model: amazon-bedrock/eu.anthropic.claude-sonnet-5, thinking: high, turn-limit: 180 }
|
|
119
|
+
explorer: google/gemini-3.1-flash-lite
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
| key | what it sets | left out |
|
|
123
|
+
| --- | --- | --- |
|
|
124
|
+
| `module` | the `.plain` file `turri regenerate` and the chat's `regenerate` build when none is named, from the project root | a file has to be named |
|
|
125
|
+
| `harness-model` | the model `turri` starts on - the one you talk to - the one that diagnoses a job that failed for good, and, in the conversation, the planner that cuts the work into jobs (on whatever model the chat is on when it plans); under `turri regenerate`, the planner too | pi's configured model |
|
|
126
|
+
| `coding-model` | every render: functionalities, spec diffs, conformance tests, repairs, planning spec diffs, the explorer | `harness-model` |
|
|
127
|
+
| `judge-model` | the judge and the test review of a conformance run | `coding-model` |
|
|
128
|
+
| `conformance-tests` | `all` (or `true`): conformance tests for every functional spec of every module with test reqs, written once each module's code is built, before anything is built on it. `chosen`: for the functional specs chosen - every one with acceptance tests, and those the chooser picks as each code pulse ends, from what that pulse regenerated. `module`: the same, but the chooser is asked once, when the whole module is regenerated, and plans the conformance pulses too. Under both, the choice is made inside the pulse, before anything is built on the module, and the pulse's report says what was chosen and why; those the person asks for with `conform` are written too; once chosen, a functional spec keeps them, and they are rewritten when it changes - see [who chooses, and when](regenerating.md#conformance-tests-in-the-work-loop). `false`: none written, and none of the existing ones run - until the conversation asks with `conform` | `false` |
|
|
129
|
+
| `conformance-budget` | under `conformance-tests: chosen` or `module`: how many of a module's functional specs may be chosen for conformance tests, beside those with acceptance tests - a count (`2`) or a share of its functional specs (`30%`, rounded up, at least one). Every choice for the module counts, whenever it was made; the chooser is told what is left, and can choose no more. Your own `conform` is not limited. A stack's `config.turri.<stack>.yaml` can set its own | no limit |
|
|
130
|
+
| `review` | `true` or `false`: whether the code and its unit tests - and the conformance tests - are reviewed once they pass: two reviews at most, the second weighed by the arbiter; off, a job lands once its tests pass - see [regenerating the code](regenerating.md#the-code-and-its-unit-tests-are-reviewed) | `false` |
|
|
131
|
+
| `rules-off` | a list of rule names - `integrations`, `concise-specs`, the files of `genome/rules/` without `.md` - left out of the conversation's prompt and every spec-writing agent's: a rule this project's own skills or AGENTS.md replace, such as one that says to probe a live API before any spec is written. The project's alone | none |
|
|
132
|
+
| `allow-to-stop-jobs` | `true` or `false`: whether turri stops a running job the spec has moved on from - a functional spec it regenerates changed, or a file one links was edited - rolls it back and regenerates the spec as it now is. `false`: the job runs to its end and lands, and what changed since is regenerated on top of it by the next job - see [regenerating the code](regenerating.md). Your own `halt` stops a job either way | `true` |
|
|
133
|
+
| `release-after-pulse` | `true` or `false`: whether every pulse that lands code releases it, in the chat, as its last stage - before the module's next pulse starts, whatever the chat is doing: an agent on the coding model looks for the project's release guidance as the chat does (what was passed to `release` this conversation, AGENTS.md, the project's skills) and, when it says to release after a build, does what it says - see [deploy](deploy.md#after-every-pulse). One release a pulse that lands; a release that fails is said in the pulse's report, and what it landed stays | `false` |
|
|
134
|
+
| `unittests-script` | the unit testing script, by path from the project root - it runs the code's static checks too, exiting 80 when only they fail (see [the static checks](regenerating.md#the-static-checks)) | looked for - see below |
|
|
135
|
+
| `conformance-tests-script` | the conformance testing script, by path from the project root | looked for - see below |
|
|
136
|
+
| `prepare-environment-script` | optional, as codeplain has it: run as `<script> <build>` before every run of the conformance testing script, which runs only once it passes - see [the testing scripts](regenerating.md#the-testing-scripts). turri never writes one | the one `prepare_environment*.sh` at the root or in `test_scripts/`, else none |
|
|
137
|
+
| `compact-at` | how many tokens of work - past its instructions - an agent kept through a job holds before the harness compacts it between turns; at least 1000; the project's alone | 100,000 - see [agent sessions](regenerating.md#agent-sessions) |
|
|
138
|
+
| `max-specs-per-pulse` | the most functional specs the planner groups into one pulse - what builds one feature goes together, up to this many; a stack's file wins | 3 |
|
|
139
|
+
| `max-lines-per-pulse` | the most lines of spec in one pulse - its functional specs with the definitions they bring; a group past it is split again, a functional spec a pulse; a stack's file wins | 120 |
|
|
140
|
+
| `parallel-jobs` | how many jobs the chat runs at once - modules that share nothing build side by side up to it; read when the chat starts, from the folder it starts in | half this machine's cores, between one and four - see [how many jobs at once](#how-many-jobs-at-once) |
|
|
141
|
+
| `agent-env` | credentials the agents' commands need while they work - a live API probed, tests run against a real service: `files: [...]`, dotenv files by path from the project root, every variable in them; `vars: [...]`, variables by name from the environment turri was started in. A stack's `config.turri.<stack>.yaml` adds its own to the project's. See [credentials for the agents](#credentials-for-the-agents) | none: an agent's commands get no credential |
|
|
142
|
+
| `toolchains` | folders of this machine an agent's commands may read beside the system's, the `PATH`'s and those the toolchain variables point at - a toolchain installed somewhere unusual (`[/srv/zig]`). A stack's add to the project's. See [the OS sandbox](sandbox.md#the-os-sandbox) | none |
|
|
143
|
+
| `turn-limit` | seconds each turn of an agent a job starts has, for every role without its own (`models:`); `0` for none. The config is the only place it is set. A turn past it ends at its next request to the model - see [regenerating the code](regenerating.md) | 120 |
|
|
144
|
+
| `models` | each agent role's own model, thinking and turn limit - see [models per role](#models-per-role) | every role on `coding-model`, the conversation's side on `harness-model` |
|
|
145
|
+
| `testing-scripts-run-on` | what the testing scripts run on - always on this machine, in your environment. `in-place`: the real build and tests folders, the script at its own path, from the project - so a script that finds a host codebase from its own path finds it. `copy`: a fresh copy of the code, staged in `plain_modules/.turri/scripts/`, with the script copied beside it; it gives the coding agent `can_it_fail` (see [the testing scripts](regenerating.md#the-testing-scripts)) | `in-place` |
|
|
146
|
+
|
|
147
|
+
Keys are written with dashes; underscores are read the same (`conformance_tests`). A key turri
|
|
148
|
+
does not know is ignored - a codeplain `config.yaml`, read while it is the old file, carries several - and so is a value of the
|
|
149
|
+
wrong type, as if it were not there. `model:`, from before there were separate models, is read as
|
|
150
|
+
`coding-model`.
|
|
151
|
+
|
|
152
|
+
## Models per role
|
|
153
|
+
|
|
154
|
+
`models:` gives each kind of agent its own model, thinking level and turn limit. Each entry is a model
|
|
155
|
+
(`provider/id`, or `provider/id:thinking`) or `{ model, thinking, turn-limit }`, any of which may be left out:
|
|
156
|
+
|
|
157
|
+
| role | the agents |
|
|
158
|
+
| --- | --- |
|
|
159
|
+
| `default` | every role not named below - but the conversation's side |
|
|
160
|
+
| `chat` | `turri`'s model - the same as `harness-model`, which it wins over |
|
|
161
|
+
| `chooser`, `planner`, `diagnosis` | the conversation's side: the conformance chooser of `turri regenerate`, the planner's agents, the diagnosis of a failed job. Unnamed, the chat's model |
|
|
162
|
+
| `release` | `turri release`'s agent and release-after-pulse's: it follows steps - a script, a start command - so it runs on the coding model, thinking low |
|
|
163
|
+
| `coding` | the coding agent, the code fixer of a conformance run and the repair agent - the same as `coding-model`, which it wins over. `--model` on the command line, or a model chosen in the conversation (`engine`), wins over both |
|
|
164
|
+
| `conformance` | the conformance agent |
|
|
165
|
+
| `explorer` | the explorers of a conformance run |
|
|
166
|
+
| `reviewer` | the review of the code and its unit tests |
|
|
167
|
+
| `test-reviewer` | the review of the conformance tests - `judge-model` too, when it is set |
|
|
168
|
+
| `judge` | the judge of a failing conformance run - the same as `judge-model`, which it wins over |
|
|
169
|
+
| `arbiter` | the arbiters that weigh a second review |
|
|
170
|
+
| `debugger` | the fresh look at unit tests that keep failing the same way, or a coding agent going round in circles. Unnamed, `judge-model` when it is set, else the harness model - not `default` |
|
|
171
|
+
|
|
172
|
+
What an entry leaves out it takes from `default`: `reviewer: { thinking: low }` runs the default model, thinking
|
|
173
|
+
little. A turn limit is the role's own, else `default`'s, else `turn-limit:`, else two minutes. A stack's `config.turri.<stack>.yaml` lays its own `models:` over the project's, role by role - all but
|
|
174
|
+
`chat`, the project's alone.
|
|
175
|
+
|
|
176
|
+
A role whose thinking nothing names - neither its entry, `default`'s, nor a `:level` on the model it runs on - thinks
|
|
177
|
+
as its kind of work needs: **low** for the agents that do the work (`coding`, `conformance`, `explorer`, `release`),
|
|
178
|
+
which work from specs that leave little to guess and take many turns, and **medium** for the ones that judge
|
|
179
|
+
(`reviewer`, `test-reviewer`, `judge`, `arbiter`, `debugger`, `planner`, `chooser`, `diagnosis`), where a wrong
|
|
180
|
+
call costs pulses. A judging role that falls back on the coding model takes its model alone, not its thinking. The
|
|
181
|
+
conversation keeps its own level, as pi sets it.
|
|
182
|
+
|
|
183
|
+
The thinking level reaches the model as each provider takes it. OpenAI's models on Bedrock are sent it as their
|
|
184
|
+
reasoning effort (`none`, `low`, `medium`, `high`, `xhigh`) by turri itself, as pi sends them none.
|
|
185
|
+
|
|
186
|
+
## How many jobs at once
|
|
187
|
+
|
|
188
|
+
Jobs on the same module, or on modules of one `requires` chain, always run one after another.
|
|
189
|
+
Modules that share nothing - a backend, a frontend, a worker, ten of them - build side by side, each
|
|
190
|
+
pulse's builds and tests running on this machine beside your own work. So the chat runs at most `parallel-jobs` jobs
|
|
191
|
+
at once; a job whose turn has come waits for a free slot past that, and its line says so. Unset, it is half this
|
|
192
|
+
machine's cores, at least one and at most four. The config is the only place it is set. It is the project's alone,
|
|
193
|
+
never read from a stack's file.
|
|
194
|
+
|
|
195
|
+
The testing scripts all run on this machine, each on a copy of its module's code, so they never
|
|
196
|
+
share files - but they do share its network: two modules whose conformance scripts start a server
|
|
197
|
+
on the same port collide when they run at once. Give each its own.
|
|
198
|
+
|
|
199
|
+
## Models
|
|
200
|
+
|
|
201
|
+
Each model is a model reference and a thinking level - `off`, `minimal`, `low`, `medium`, `high`,
|
|
202
|
+
`xhigh` or `max` - and either may be left out:
|
|
203
|
+
|
|
204
|
+
```yaml
|
|
205
|
+
coding-model:
|
|
206
|
+
model: google/gemini-3.1-flash # the provider and the model id, as pi names them
|
|
207
|
+
thinking: medium
|
|
208
|
+
judge-model: google/gemini-3.1-pro:high # the same, as one string
|
|
209
|
+
harness-model: anthropic/claude-opus-5-5 # a model alone
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
What a model leaves out it takes from the one it inherits: `judge-model` from `coding-model` - its model, not its
|
|
213
|
+
thinking, which the judging roles have their own of - and `coding-model` from `harness-model`. So with only a harness model, everything runs on it;
|
|
214
|
+
`coding-model: {thinking: low}` renders on the harness model, thinking less.
|
|
215
|
+
|
|
216
|
+
They are defaults. What is asked for wins over them:
|
|
217
|
+
|
|
218
|
+
| model | wins over the config |
|
|
219
|
+
| --- | --- |
|
|
220
|
+
| harness | `turri --harness-model <m>` (or pi's own `--model`), and pi's `/model` in the conversation |
|
|
221
|
+
| coding | `turri --coding-model <m>`, `engine` in the conversation, and `--model` / `--coding-model` on `turri regenerate`, `conform` and `spec-diff` |
|
|
222
|
+
| judge | `--judge-model` on `turri conform` |
|
|
223
|
+
|
|
224
|
+
A job keeps the model it was queued with: choosing another holds for what is queued after.
|
|
225
|
+
|
|
226
|
+
## Conformance tests
|
|
227
|
+
|
|
228
|
+
`conformance-tests:` is the one switch, and the chat, every render and `brood`'s testing phase
|
|
229
|
+
follow it. `false` turns them off entirely: no conformance tests are written, the ones already in a
|
|
230
|
+
codebase are not run, and no agent is shown the conformance testing script. No key is the same as
|
|
231
|
+
`false`: conformance tests are off by default. `all` (or `true`) means on wherever a module has
|
|
232
|
+
`***test reqs***` - a module without them has nothing to write conformance tests from, so it writes
|
|
233
|
+
none, but still keeps the codebase's existing ones passing. Asked in the
|
|
234
|
+
conversation, or with `--conformance` / `--no-conformance` on the command line, a render does
|
|
235
|
+
otherwise.
|
|
236
|
+
|
|
237
|
+
`spawn` writes `conformance-tests: false` into a new project unless you ask for tests;
|
|
238
|
+
`brood` asks, whatever the key says, offering what it says first.
|
|
239
|
+
|
|
240
|
+
## The testing scripts
|
|
241
|
+
|
|
242
|
+
A build needs a unit testing script - and, with conformance on, a conformance testing script - to
|
|
243
|
+
decide when it is done. They are looked for at the project root, in this order:
|
|
244
|
+
|
|
245
|
+
1. named in the config - the chosen stack's file first, then `config.turri.yaml` - by path from the root;
|
|
246
|
+
2. the one `run_unittests*.sh` / `run_conformance_tests*.sh` in the project root;
|
|
247
|
+
3. the one of each in `test_scripts/`.
|
|
248
|
+
|
|
249
|
+
The optional prepare script - `prepare-environment-script:`, else the one `prepare_environment*.sh` - is looked for the
|
|
250
|
+
same way, and run on the build before the conformance testing script.
|
|
251
|
+
|
|
252
|
+
A folder holding two of a kind is not guessed between: name the one to use. A script named inside
|
|
253
|
+
`plain_modules/` is refused - agents write there, so it could be changed by the work it judges. The
|
|
254
|
+
scripts are never linked from the specs. What they are and how they run is in
|
|
255
|
+
[regenerating the code](regenerating.md#the-testing-scripts).
|
|
256
|
+
|
|
257
|
+
## Credentials for the agents
|
|
258
|
+
|
|
259
|
+
An agent's commands do not get the environment turri was started in: it holds your providers' API keys, cloud
|
|
260
|
+
credentials and tokens. They get only the path, the locale, the terminal and the user, the variables that locate your
|
|
261
|
+
toolchains (`JAVA_HOME`, `NVM_DIR`, `RUSTUP_HOME`, ... - the ones in your home found even when your shell sets none),
|
|
262
|
+
and turri's own: a home folder of the module's own (`plain_modules/.turri/home/<module>`, kept between pulses, with a
|
|
263
|
+
git identity of its own), a `TMPDIR`, the package caches in `plain_modules/.turri/cache`, and git stopped at the module
|
|
264
|
+
folder, so a project that is itself a repository is never found from the code.
|
|
265
|
+
|
|
266
|
+
A project whose agents need a credential while they work names it:
|
|
267
|
+
|
|
268
|
+
```yaml
|
|
269
|
+
agent-env:
|
|
270
|
+
files: [.env] # every variable of these dotenv files, by path from the project root
|
|
271
|
+
vars: [D365_TENANT_ID] # these variables, from the environment turri was started in
|
|
272
|
+
```
|
|
273
|
+
|
|
274
|
+
What it names reaches every agent with a shell, as environment variables of its commands only - the file stays where
|
|
275
|
+
it is, outside every agent's reach - and never replaces turri's own home, `TMPDIR` or caches. A stack's file adds
|
|
276
|
+
to the project's. A file that is not there, or a variable unset or empty, is reported as missing. The testing scripts
|
|
277
|
+
are not affected: they run in your own environment.
|
|
278
|
+
|
|
279
|
+
Their values never appear in what an agent is shown or what turri writes: each is replaced by its name -
|
|
280
|
+
`[D365_CLIENT_SECRET]` - whole, URL-encoded, or split over a command's output as it streams: in every tool result and
|
|
281
|
+
refusal an agent is given (and so in the session transcripts, which hold only those), in the whole outputs kept for
|
|
282
|
+
`read_output`, and in the job and debug logs. A value shorter than four characters is not masked - `1` or `eu` is
|
|
283
|
+
everywhere in ordinary output.
|
|
284
|
+
|
|
285
|
+
## Stacks
|
|
286
|
+
|
|
287
|
+
One project can hold parts that share nothing but a common module - a Python backend and a React
|
|
288
|
+
frontend side by side, each importing `base` from `template/`, neither requiring the other - each
|
|
289
|
+
needing its own testing scripts. Each stack gets a file of its own beside `config.turri.yaml`,
|
|
290
|
+
`config.turri.<stack>.yaml`, holding what differs for it - typically its testing scripts, its
|
|
291
|
+
`toolchains:`, its models:
|
|
292
|
+
|
|
293
|
+
```
|
|
294
|
+
config.turri.yaml # the project's: harness-model, coding-model, conformance-tests
|
|
295
|
+
config.turri.backend.yaml # the stack backend: its testing scripts
|
|
296
|
+
config.turri.frontend.yaml # the stack frontend: its testing scripts, and a coding model of its own
|
|
297
|
+
backend.plain # import: [base, python_fastapi_template]
|
|
298
|
+
frontend.plain # import: [base, react_vite_template]
|
|
299
|
+
template/ # base.plain, python_fastapi_template.plain, react_vite_template.plain
|
|
300
|
+
test_scripts/ # both stacks' scripts
|
|
301
|
+
```
|
|
302
|
+
|
|
303
|
+
```yaml
|
|
304
|
+
# config.turri.frontend.yaml
|
|
305
|
+
unittests-script: test_scripts/run_unittests_typescript.sh
|
|
306
|
+
conformance-tests-script: test_scripts/run_conformance_tests_typescript.sh
|
|
307
|
+
coding-model:
|
|
308
|
+
thinking: high
|
|
309
|
+
```
|
|
310
|
+
|
|
311
|
+
A stack is chosen, never worked out from the spec:
|
|
312
|
+
|
|
313
|
+
- **In the chat**, with `/stack` - a list of the project's stacks and none - or `/stack backend`, `/stack none`.
|
|
314
|
+
The chat's model has a `stack` tool too, and switches the stack when you ask it to, or for work on a part another
|
|
315
|
+
stack builds. `/stack` alone says which is chosen now.
|
|
316
|
+
- **On the command line**, with `--stack <name>` on any command - `turri regenerate`, `conform`, `spec-diff`, `glimpse`,
|
|
317
|
+
`release`, `cultivate` - and on `turri --stack backend` for the chat; or with `TURRI_STACK` in the environment.
|
|
318
|
+
|
|
319
|
+
The choice lasts for the session only: nothing remembers it, and turri started again has none chosen. A stack the
|
|
320
|
+
project has no file for is refused, with the stacks it has. With none chosen, `config.turri.yaml` alone is used.
|
|
321
|
+
|
|
322
|
+
How a stack's config is made:
|
|
323
|
+
|
|
324
|
+
- **Laid over `config.turri.yaml`, key by key.** What the stack's file gives wins; what it leaves out
|
|
325
|
+
is the project's. A model it names takes what it leaves out from the project's same model - above,
|
|
326
|
+
with the stack frontend chosen, everything renders on the project's coding model, thinking `high`.
|
|
327
|
+
- **For everything built while it is chosen.** Every pulse that starts, every test run and every
|
|
328
|
+
script found from then on is the chosen stack's; a pulse already running keeps the stack it
|
|
329
|
+
started with. Choose the stack before building the part it is for: `/stack backend`, then
|
|
330
|
+
`regenerate backend.plain`.
|
|
331
|
+
- **The project's alone.** `module:`, `harness-model:`, `parallel-jobs:`, `rules-off:` and `compact-at:` are read
|
|
332
|
+
only from `config.turri.yaml` - there is one conversation, one module built when none is named, one machine.
|
|
333
|
+
`agent-env:` and `toolchains:` in a stack's file add to the project's.
|
|
334
|
+
|
|
335
|
+
The chat is told each stack's config and the one chosen. The skills that write testing scripts put
|
|
336
|
+
each stack's in its own file once a project has two stacks, and say which stack to choose before
|
|
337
|
+
building each part.
|
|
338
|
+
|
|
339
|
+
## The old config.yaml
|
|
340
|
+
|
|
341
|
+
turri's settings were once in `config.yaml`, and a stack's in `config.<stack>.yaml`. A `config.yaml`
|
|
342
|
+
may be another tool's, so turri never writes one. While a project folder has no `config.turri*.yaml`
|
|
343
|
+
at all, turri still reads its `config.yaml`, and each `config.<name>.yaml` as the stack `<name>`.
|
|
344
|
+
|
|
345
|
+
The first time turri itself writes a setting there with `/turri-config`, it copies `config.yaml` to
|
|
346
|
+
`config.turri.yaml` and each `config.<name>.yaml` to `config.turri.<name>.yaml` first, leaves the old
|
|
347
|
+
files as they are, and from then on reads only the new ones. `turri init-config` copies the stacks'
|
|
348
|
+
files the same way, but writes `config.turri.yaml` as its quick start, taking from `config.yaml` only
|
|
349
|
+
the keys turri reads too - codeplain's `unittests-script`, `conformance-tests-script` and
|
|
350
|
+
`prepare-environment-script`, say - each in its place in the file, and saying which keys it left:
|
|
351
|
+
`template_dir`, `verbose`, `api`, codeplain's alone. The two models are the vendor's, never taken. To change a
|
|
352
|
+
setting by hand in such a project, do the same: copy the files to their new names, and edit the
|
|
353
|
+
copies - never `config.yaml`.
|