@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,64 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: resolve-section-ownership
|
|
3
|
+
description: >-
|
|
4
|
+
Route every fact in a .plain bullet, section, or file to the section that owns it — splitting a bullet that carries several kinds of fact, and moving a misplaced one to where the renderer will read it. Use when a bullet answers more than one of what it is / what it does / how it is built / how it is tested, when a requirement looks like it is in the wrong section, or when auditing a file for section ownership. Not for a spec that is merely too large (use break-down-func-spec) or for two specs that contradict each other (use resolve-spec-conflict).
|
|
5
|
+
allowed-tools: [read, write, edit, bash, ls, grep, find]
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# Resolve Section Ownership
|
|
9
|
+
|
|
10
|
+
This is the ownership counterpart to `break-down-func-spec`. That skill splits one functional spec into several because it is **too large**; this one moves facts between sections because they are in the **wrong place**. The two are unrelated: a correctly placed spec can be too large, and a one-line bullet can be misplaced.
|
|
11
|
+
|
|
12
|
+
## When to Use
|
|
13
|
+
|
|
14
|
+
- A bullet answers more than one of *what is it*, *what does it do*, *how is it built*, *how is it tested*.
|
|
15
|
+
- A requirement states a mechanism (retry, cache, token refresh, connection handling) but sits in `***functional specs***`.
|
|
16
|
+
- A `:UnitTests:` fact sits in `***test reqs***`, or a `:ConformanceTests:` fact sits in `***implementation reqs***` — the generators read only their own section, so the fact is silently ignored today.
|
|
17
|
+
- A functional spec describes an external system rather than the software being built.
|
|
18
|
+
- A whole file is being audited for placement.
|
|
19
|
+
|
|
20
|
+
## When Not to Use
|
|
21
|
+
|
|
22
|
+
- The spec is correctly placed but too large → `analyze-if-func-spec-too-complex`, then `break-down-func-spec`.
|
|
23
|
+
- Two specs contradict each other → `analyze-func-specs`, then `resolve-spec-conflict`.
|
|
24
|
+
- The wording is merely verbose → trim per `concise-specs.md`; no fact changes section.
|
|
25
|
+
|
|
26
|
+
## Input
|
|
27
|
+
|
|
28
|
+
The bullet, section, or `.plain` file to resolve.
|
|
29
|
+
|
|
30
|
+
## Workflow
|
|
31
|
+
|
|
32
|
+
1. **Read the full `.plain` file** — every section, plus its `import` and `requires` chains, so an existing home for a fact is visible before a new one is written.
|
|
33
|
+
2. **Identify the target** — one bullet, one section, or the whole file.
|
|
34
|
+
3. **Decompose each bullet into atomic facts.** A clause joined by *and*, *which*, *so that*, or a comma often carries a second fact. Keep each fact in the author's own words for now; rewording comes at step 6.
|
|
35
|
+
4. **Route each fact** by the *Section ownership* table in `module-structure.md`. Where a fact resists the table, apply the question it encodes: **would a caller of the finished software observe this?** Yes → `***functional specs***`. No, it is how the result is reached → `***implementation reqs***`. It says what a term means → `***definitions***`. It shapes `:ConformanceTests:` → `***test reqs***`.
|
|
36
|
+
5. **Split a fact that carries both a mechanism and its consequence.** The consequence is observable and becomes the functional spec; the mechanism becomes the implementation req. *"Sends the same idempotency key on every retry, so a retried write creates at most one record"* is two facts, not one.
|
|
37
|
+
6. **Give each moved fact a subject that exists in the built system.** A fact leaving a functional spec often loses its subject: name the component that owns the behavior, and add it to `***definitions***` if it is not defined yet. `:Implementation:` is the whole codebase — use it only for a fact that holds everywhere.
|
|
38
|
+
7. **Author each fact into its owning section** with the right edit skill — `add-concept`, `add-implementation-requirement`, `add-test-requirement`, `add-acceptance-test`. **Never hand-author `***functional specs***`**: a fact moving *into* that section goes through `add-functional-spec`, which runs the complexity and conflict analyzers for you.
|
|
39
|
+
8. **Remove each fact from where it no longer belongs.** If a bullet is left stating nothing observable, delete the bullet; do not leave a stub. Deleting the last bullet of a section deletes the section.
|
|
40
|
+
9. **Verify nothing was lost and nothing was duplicated.** Walk every fact from the original text and confirm it appears in exactly one section. A fact that now reads as obvious in its new home is still a cut decision — make it deliberately, per `concise-specs.md`.
|
|
41
|
+
10. **Run `validate`** on the module. Moving facts changes the renderable surface; do not finish while it reports errors.
|
|
42
|
+
11. **Read the file again** to confirm canonical section order (`***definitions***` → `***implementation reqs***` → `***test reqs***` → `***functional specs***`), correct syntax, and that every referenced concept is still defined before use.
|
|
43
|
+
|
|
44
|
+
## Error Handling
|
|
45
|
+
|
|
46
|
+
- **`add-functional-spec` reports the moved fact is too complex** → the fact was a whole spec in disguise; run `break-down-func-spec` on it, then place the resulting specs.
|
|
47
|
+
- **`add-functional-spec` reports a conflict** → resolve it with `resolve-spec-conflict` **before** removing the fact from its old section, so no version of it is lost in between.
|
|
48
|
+
- **A fact appears to belong to two sections** → it is two facts; split it per step 5. If it still resists, it is a mechanism whose consequence has not been written down — write the consequence, and the split follows.
|
|
49
|
+
- **The owning section does not exist in the file** → create it in canonical order. An import module carries no `***functional specs***` at all (see `import-modules.md`), so an observable fact found there belongs on the root module that imports it.
|
|
50
|
+
- **The move empties a functional spec** → delete the spec rather than leaving a placeholder, and check whether any acceptance test nested under it must move or go with it.
|
|
51
|
+
|
|
52
|
+
## Worked examples
|
|
53
|
+
|
|
54
|
+
`concise-specs-examples.md` (*Split one crowded bullet across its owning sections*) shows one bullet carrying four kinds of fact. `concise-specs-integration-examples.md` works through the mechanism-versus-result cases — retry policy, idempotency, auth — which are where this comes up most.
|
|
55
|
+
|
|
56
|
+
## Validation Checklist
|
|
57
|
+
|
|
58
|
+
- [ ] Every fact from the original text appears in exactly one section, or was deliberately cut
|
|
59
|
+
- [ ] No fact was reworded into a different meaning while being moved
|
|
60
|
+
- [ ] Each moved fact names a subject that exists in the built system
|
|
61
|
+
- [ ] Every fact moved into `***functional specs***` went through `add-functional-spec`
|
|
62
|
+
- [ ] No `:UnitTests:` fact outside `***implementation reqs***`, no `:ConformanceTests:` fact outside `***test reqs***`
|
|
63
|
+
- [ ] Sections are in canonical order and every referenced concept is defined before use
|
|
64
|
+
- [ ] Emptied bullets and sections were removed, not left as stubs
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: resolve-spec-conflict
|
|
3
|
+
description: >-
|
|
4
|
+
Resolve a conflict between two functional specs in a ***plain spec file. Use
|
|
5
|
+
when conformance tests for a previously passing spec start failing after a new
|
|
6
|
+
spec is rendered, or when a potential conflict is detected while adding a new
|
|
7
|
+
functional spec (via `add-functional-spec` or `add-functional-specs`).
|
|
8
|
+
allowed-tools: [read, write, edit, bash, ls, grep, find]
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
# Resolve Spec Conflict
|
|
12
|
+
|
|
13
|
+
## When This Applies
|
|
14
|
+
|
|
15
|
+
A conflict exists (or is suspected) when:
|
|
16
|
+
- Conformance tests for a **previously passing** functional spec begin to fail after a new spec is rendered.
|
|
17
|
+
- A new spec being added (via the `add-functional-spec` or `add-functional-specs` skill) appears to contradict an existing spec.
|
|
18
|
+
- Two specs make incompatible assertions about the same behavior, data, or state.
|
|
19
|
+
|
|
20
|
+
## Workflow
|
|
21
|
+
|
|
22
|
+
### Step 1: Identify the Conflicting Pair
|
|
23
|
+
|
|
24
|
+
Read the `.plain` file and pinpoint the two specs in tension. If the conflict was detected via a conformance test failure, also read the failing test and the generated implementation in `plain_modules/` to understand what behavior changed.
|
|
25
|
+
|
|
26
|
+
### Step 2: Diagnose the Root Cause
|
|
27
|
+
|
|
28
|
+
There are three possible outcomes — determine which one applies **before** making changes:
|
|
29
|
+
|
|
30
|
+
| Outcome | Symptom | Fix |
|
|
31
|
+
|---------|---------|-----|
|
|
32
|
+
| **Implementation is incorrect** | Generated code doesn't match the spec's intent. The specs themselves are fine. | Clarify the ambiguous spec so the renderer interprets it correctly. Re-render. |
|
|
33
|
+
| **Conformance tests are incorrect** | The tests don't accurately verify the spec. The implementation is actually correct. | Adjust `***test reqs***` or `***acceptance tests***` to guide better test generation. Re-render. |
|
|
34
|
+
| **Requirements truly conflict** | The two specs are inherently contradictory — no implementation can satisfy both. | One or both specs must be revised. See Step 3. |
|
|
35
|
+
|
|
36
|
+
**How to diagnose:**
|
|
37
|
+
1. Read both functional specs carefully. Can a single implementation satisfy both simultaneously?
|
|
38
|
+
2. If yes → the specs are compatible; the issue is ambiguity (outcome 1) or bad tests (outcome 2).
|
|
39
|
+
3. If no → the requirements truly conflict (outcome 3).
|
|
40
|
+
|
|
41
|
+
### Step 3: Resolve the Conflict
|
|
42
|
+
|
|
43
|
+
When the requirements truly conflict, choose a resolution strategy:
|
|
44
|
+
|
|
45
|
+
**Strategy A: Add detail to disambiguate.** Often the specs aren't contradictory — they're just ambiguous enough that the renderer picks a conflicting interpretation. Adding explicit context to one or both specs eliminates the ambiguity.
|
|
46
|
+
|
|
47
|
+
Before (ambiguous):
|
|
48
|
+
```plain
|
|
49
|
+
***functional specs***
|
|
50
|
+
|
|
51
|
+
- All :Resource: items are returned.
|
|
52
|
+
|
|
53
|
+
- Only active :Resource: items are returned.
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
After (disambiguated):
|
|
57
|
+
```plain
|
|
58
|
+
***functional specs***
|
|
59
|
+
|
|
60
|
+
- All :Resource: items are returned when no filter is specified.
|
|
61
|
+
|
|
62
|
+
- When the "active" filter is specified, only active :Resource: items are returned.
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
**Strategy B: Revise the newer spec.** If the new spec introduced the conflict, rewrite it to be compatible with the established behavior from earlier specs.
|
|
66
|
+
|
|
67
|
+
**Strategy C: Revise the older spec.** If the older spec was under-specified and the new requirement reveals a better design, update the older spec. This is more disruptive — all conformance tests for that spec and everything after it may need re-rendering.
|
|
68
|
+
|
|
69
|
+
**Strategy D: Merge into one spec.** If the two specs describe overlapping behavior, combine them into a single, clear spec. Remove the redundant one. Be mindful of the 200 LOC limit.
|
|
70
|
+
|
|
71
|
+
### Step 4: Validate the Resolution
|
|
72
|
+
|
|
73
|
+
After editing:
|
|
74
|
+
1. Re-read the full `***functional specs***` section to confirm no new conflicts were introduced.
|
|
75
|
+
2. Check that any revised spec still respects the 200 LOC limit.
|
|
76
|
+
3. Check that chronological ordering still makes sense (earlier specs should not depend on later ones).
|
|
77
|
+
4. If resolving via test adjustments, verify the `***test reqs***` or `***acceptance tests***` changes are appropriate.
|
|
78
|
+
|
|
79
|
+
## Prevention (for use during add-functional-spec / add-functional-specs)
|
|
80
|
+
|
|
81
|
+
Before adding a new spec, run through this quick conflict check:
|
|
82
|
+
|
|
83
|
+
1. List every existing functional spec that touches the same `:Concepts:` as the new spec.
|
|
84
|
+
2. For each, ask: "Can a single implementation satisfy both this existing spec and the new one?"
|
|
85
|
+
3. If any answer is "not obviously yes" — add explicit detail to the new spec to eliminate the ambiguity **before** inserting it.
|
|
86
|
+
|
|
87
|
+
## Validation Checklist
|
|
88
|
+
|
|
89
|
+
- [ ] Root cause diagnosed (implementation / tests / true conflict)
|
|
90
|
+
- [ ] Resolution strategy chosen and applied
|
|
91
|
+
- [ ] Revised specs are language-agnostic
|
|
92
|
+
- [ ] Revised specs trimmed per `concise-specs.md` — added disambiguation stays as short as it can be
|
|
93
|
+
- [ ] Revised specs each imply ≤ 200 LOC
|
|
94
|
+
- [ ] No new conflicts introduced by the fix
|
|
95
|
+
- [ ] Chronological ordering preserved
|
|
96
|
+
- [ ] All referenced `:Concepts:` are still defined
|
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: spawn
|
|
3
|
+
description: >-
|
|
4
|
+
The quick twin of `brood`: asks only what the user wants to build and which stack to build it on -
|
|
5
|
+
one question, "you choose" among its answers - then writes the whole `***plain` spec on its own - the
|
|
6
|
+
stack, the unit tests and the product, feature by feature - making every choice itself where the user
|
|
7
|
+
said nothing, and queues each feature as a render spec
|
|
8
|
+
diff job the moment it is written. Conformance tests are off unless the user asks for them. No
|
|
9
|
+
interview beyond intent, no gates. The user then keeps
|
|
10
|
+
talking in the conversation, and every change they ask for is a spec diff: what is left to build
|
|
11
|
+
is worked out again from the spec. Use when the user wants to start a new project fast, or says to just build
|
|
12
|
+
it. For a full interview that settles the stack, the testing and every feature with the user, use
|
|
13
|
+
`brood`. Not for adding a feature to an existing project (use `bud`).
|
|
14
|
+
allowed-tools: [read, write, edit, bash, ls, grep, find, write_spec_diff]
|
|
15
|
+
metadata:
|
|
16
|
+
# What each phase does, in a line - shown under the skill's progress while it runs.
|
|
17
|
+
phases:
|
|
18
|
+
intent: "what you want to build, and which stack to build it on"
|
|
19
|
+
groundwork: "the language, frameworks, storage and architecture"
|
|
20
|
+
product: "writes the features one by one - each starts a regenerate pulse"
|
|
21
|
+
# It writes its spec changes one after another on its own: no answer between them.
|
|
22
|
+
paced: false
|
|
23
|
+
---
|
|
24
|
+
|
|
25
|
+
# Spawn
|
|
26
|
+
|
|
27
|
+
Load the `***plain` syntax rules before writing anything - they are in the system prompt under
|
|
28
|
+
`<plain-rules>`; read `genome/rules/<name>.md` for the full text of any rule you need to follow
|
|
29
|
+
closely.
|
|
30
|
+
|
|
31
|
+
## Role
|
|
32
|
+
|
|
33
|
+
Act as a `***plain` spec writer who decides. The only output is `.plain` specification files and
|
|
34
|
+
the project's testing scripts and config - never code. Code is generated from the specs into
|
|
35
|
+
`plain_modules/`, read-only; never write or edit it. Say everything in terms of specs: "I'll put
|
|
36
|
+
this in the spec."
|
|
37
|
+
|
|
38
|
+
The user tells you what they want, once, and which stack to build it on - or leaves that to you.
|
|
39
|
+
Everything else is yours to choose, on best effort: the architecture, the tests, the features and
|
|
40
|
+
their order, every rule a feature needs - and the stack, when the user leaves it to you. Choose what
|
|
41
|
+
a careful engineer would for this intent - common, well supported, simple - and say what you chose.
|
|
42
|
+
Never ask about any of the rest. The user corrects it afterwards in the conversation,
|
|
43
|
+
where every change is a spec diff and the queued builds are reworked around it.
|
|
44
|
+
|
|
45
|
+
Read what is already there first: an existing `.plain` file, `config.turri.yaml` (or the old
|
|
46
|
+
`config.yaml`, with no `config.turri.yaml`), `template/`,
|
|
47
|
+
`test_scripts/`. What the project already settled - a stack, a template, a testing script, a
|
|
48
|
+
`conformance-tests:` decision - is kept, not chosen again.
|
|
49
|
+
|
|
50
|
+
## Reference
|
|
51
|
+
|
|
52
|
+
- Templates go in `template/`; import paths omit the `template/` prefix. Resources go in `resources/`.
|
|
53
|
+
- Generated code lands in `plain_modules/<module>/code/`, conformance tests in
|
|
54
|
+
`plain_modules/<module>/tests/` (both read-only).
|
|
55
|
+
- One module, unless the intent needs two stacks - a web frontend and an API backend. Then two
|
|
56
|
+
modules side by side, neither requiring the other, sharing what they both mean through one
|
|
57
|
+
imported module in `template/`. Each is on a stack of its own, whose testing scripts are named in
|
|
58
|
+
`config.turri.<stack>.yaml` beside `config.turri.yaml` (`unittests-script:`, `conformance-tests-script:`) -
|
|
59
|
+
`config.turri.backend.yaml`, `config.turri.frontend.yaml`. A stack is chosen, never worked out: tell the
|
|
60
|
+
user to choose one with `/stack <stack>` (`--stack <stack>` on the command line) before building its part.
|
|
61
|
+
|
|
62
|
+
## Steps
|
|
63
|
+
|
|
64
|
+
### intent
|
|
65
|
+
|
|
66
|
+
Find out what the user wants to build - the only thing you ask about.
|
|
67
|
+
|
|
68
|
+
If what the user has already said answers it, do not ask again. Otherwise ask one open question with
|
|
69
|
+
`ask_user` and `free_form: true`: what should it do, and for whom? Ask at most two follow-ups, and
|
|
70
|
+
only when the answer leaves open something that changes what gets built - what kind of software it
|
|
71
|
+
is (a web app, an API, a CLI), or what the first version must do. Never ask about testing, structure
|
|
72
|
+
or details: those are yours.
|
|
73
|
+
|
|
74
|
+
Then the stack - the one technical question, because it has to be one this machine can build: the
|
|
75
|
+
agents use its toolchains and install none, so a Go project on a laptop without Go stops at its first
|
|
76
|
+
build. `<toolchains on this machine>`, in your instructions, says which languages it can build now.
|
|
77
|
+
When the user already named a stack - a language, a framework - take it; when it is in a language the
|
|
78
|
+
machine lacks, say what to install first and that `turri doctor` checks it, and ask whether to wait for
|
|
79
|
+
that or choose another. Otherwise ask one `ask_user` question: first `You choose`, then the standard
|
|
80
|
+
library's stacks for the kind of software it is (the stdlib rule, in `<plain-rules>`, lists them - for
|
|
81
|
+
an API the backend stacks, for a command-line tool the CLI ones) - only those in a language the machine
|
|
82
|
+
can build now; the "Something else…" that `ask_user` adds takes a stack of the user's own. A product of two kinds - a web app and its API - is one question a module. `You choose` leaves it
|
|
83
|
+
to you, as the groundwork step says.
|
|
84
|
+
|
|
85
|
+
Then state the brief back in five short bullets - the problem, the primary user, what they can do
|
|
86
|
+
with it, what makes the first version useful, and what the first version leaves out - with the stack
|
|
87
|
+
in a line under them (the user's, or `chosen when it is built` when it is left to you) - and ask one
|
|
88
|
+
`ask_user` question with the options `Build it` and `Change something`, the brief passed whole as its
|
|
89
|
+
`context`: the dialog shows only the question, its options and its `context`. On a change, fold it in and
|
|
90
|
+
ask again. Write nothing to disk in this step.
|
|
91
|
+
|
|
92
|
+
### groundwork
|
|
93
|
+
|
|
94
|
+
Set up everything the first build needs, on your own, with the ordinary file tools - no questions,
|
|
95
|
+
nothing built, and `write_spec_diff` not used.
|
|
96
|
+
|
|
97
|
+
1. **The module and its stack.** Create the `.plain` file (and the template, when it helps), with
|
|
98
|
+
its frontmatter, and write the stack into `***implementation reqs***`: the language, the
|
|
99
|
+
framework, storage, the architecture and layout - one requirement per fact, at the right scope,
|
|
100
|
+
the shared ones on the template. The stack is the user's, when they named or chose one; left to
|
|
101
|
+
you, choose what fits the intent - a mainstream stack, with the fewest moving parts - in a language
|
|
102
|
+
this machine can build now (`<toolchains on this machine>`). Follow
|
|
103
|
+
`genome/rules/impl-reqs.md`. When the standard library has a template
|
|
104
|
+
for it - a backend, a frontend, a command-line tool or a library (the stdlib rule, in `<plain-rules>`) -
|
|
105
|
+
import that template and write only what the module needs beyond it.
|
|
106
|
+
2. **Unit tests.** A `:UnitTests:` requirement - the framework, and how they are run - unless the
|
|
107
|
+
imported stack already sets it, and the unit testing script, for the stack's test framework. Keep one turri already finds (named in `config.turri.yaml`, or the one
|
|
108
|
+
`run_unittests*.sh` in the root or `test_scripts/`); otherwise write
|
|
109
|
+
`test_scripts/run_unittests_<language>.sh` by following the `implement-unit-testing-script`
|
|
110
|
+
skill. Never link it from the specs. The script runs the stack's static checks with the unit
|
|
111
|
+
tests - the formatter in check mode, the linter, the type checker - and they count as the tests do.
|
|
112
|
+
3. **Conformance tests: off**, so each feature is built fast. Keep a `conformance-tests:` decision
|
|
113
|
+
the project's config already holds - `config.turri.yaml`, or the old `config.yaml`. Otherwise write `conformance-tests: false` into
|
|
114
|
+
`config.turri.yaml`, changing nothing else in it - a project with only a `config.yaml` has it copied
|
|
115
|
+
to `config.turri.yaml` first, and `config.yaml` is never written - and write no `***test reqs***`, import no standard library
|
|
116
|
+
conformance template, and write no conformance testing script.
|
|
117
|
+
Only when the user asked for conformance tests, turn them on instead: `conformance-tests: true`,
|
|
118
|
+
the `***test reqs***` - `:ConformanceTests:` requirements for the framework, how the tests reach
|
|
119
|
+
the software (a subprocess, HTTP against a server they start, the public entry point), setup and
|
|
120
|
+
outside services, and the command that runs the suite, following `genome/rules/test-reqs.md`, or
|
|
121
|
+
the stack's standard library conformance template imported when the stack is from it -
|
|
122
|
+
and `test_scripts/run_conformance_tests_<language>.sh`, by following the
|
|
123
|
+
`implement-conformance-testing-script` skill, unless turri already finds one.
|
|
124
|
+
|
|
125
|
+
Run `validate` on every module you wrote, and fix what it finds. Then say in a few lines what you
|
|
126
|
+
chose - the stack, the architecture, the tests - and carry on. Do not ask for confirmation.
|
|
127
|
+
|
|
128
|
+
### product
|
|
129
|
+
|
|
130
|
+
Write the product, feature by feature, as spec diffs - on your own, without asking.
|
|
131
|
+
|
|
132
|
+
1. **Plan the features** from the brief, in build order: the smallest useful thing first, then
|
|
133
|
+
what builds on it, up to what the first version must do - and nothing it leaves out. Each
|
|
134
|
+
feature is one functional spec a render can build in one go (well under 200 lines of code),
|
|
135
|
+
language-agnostic, and explicit about its interface where it has one. A feature too big for
|
|
136
|
+
that is two.
|
|
137
|
+
2. **The description first.** A one-sentence product description in the frontmatter, as the first
|
|
138
|
+
spec diff.
|
|
139
|
+
3. **Then one spec diff per feature**, in order, holding everything that feature needs across
|
|
140
|
+
sections: the concepts it introduces, into `***definitions***` - defined in the same change as
|
|
141
|
+
the functional spec that first uses them, following `add-concept`; a technology-free policy it
|
|
142
|
+
states, into `***implementation reqs***`; and its functional spec, following
|
|
143
|
+
`add-functional-spec`. Decide its edge cases, validation rules and permissions yourself, the way
|
|
144
|
+
the intent implies, and write them into the spec.
|
|
145
|
+
4. **Write each with `write_spec_diff`** - a git diff of the `.plain` file(s): `diff --git
|
|
146
|
+
a/<file> b/<file>`, `--- a/<file>`, `+++ b/<file>`, then the hunks with a few lines of context,
|
|
147
|
+
paths from the project root. It applies the change and queues its render spec diff job - with
|
|
148
|
+
conformance on, its conformance tests follow it. Go straight on to the next feature -
|
|
149
|
+
never wait for a build. Never change a `.plain` file in this step by any other means.
|
|
150
|
+
|
|
151
|
+
The step is done only once every feature you planned is written, one spec diff each. Never stop
|
|
152
|
+
part-way to ask whether to go on, or which feature comes next: nobody answers in this step, and the
|
|
153
|
+
skill ends when your turn does - so a question here leaves the product unwritten.
|
|
154
|
+
|
|
155
|
+
Correct a change you find wrong once written with a diff and `fixes_earlier`, never by editing the
|
|
156
|
+
file.
|
|
157
|
+
|
|
158
|
+
End with the handover, in a few lines: the features in build order, the jobs they were queued as,
|
|
159
|
+
that conformance tests are off unless they were asked for (turning them on later is one line in
|
|
160
|
+
`config.turri.yaml`, `conformance-tests: true`, with the test reqs and the script added in the
|
|
161
|
+
conversation), and what the user can do now - try each feature as its build lands (ask to try it, and the chat
|
|
162
|
+
deploys it), and ask for any change in the conversation. A feature added, changed or dropped is one
|
|
163
|
+
spec diff, and what is left to build is worked out again from the spec.
|
package/package.json
CHANGED
|
@@ -1,6 +1,70 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@turri-dev/cli",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"
|
|
5
|
-
"
|
|
6
|
-
|
|
3
|
+
"version": "0.1.1",
|
|
4
|
+
"license": "MIT",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"repository": {
|
|
7
|
+
"type": "git",
|
|
8
|
+
"url": "git+https://github.com/plainlang/turri.git"
|
|
9
|
+
},
|
|
10
|
+
"homepage": "https://github.com/plainlang/turri#readme",
|
|
11
|
+
"bugs": "https://github.com/plainlang/turri/issues",
|
|
12
|
+
"description": "An agent that builds code from ***plain specifications, with harness-enforced review and conformance gates.",
|
|
13
|
+
"engines": {
|
|
14
|
+
"node": ">=22.19"
|
|
15
|
+
},
|
|
16
|
+
"bin": {
|
|
17
|
+
"turri": "bin/turri.mjs"
|
|
18
|
+
},
|
|
19
|
+
"scripts": {
|
|
20
|
+
"cultivate": "tsx src/cultivate-cli.ts",
|
|
21
|
+
"typecheck": "tsc --noEmit",
|
|
22
|
+
"chat": "pi -e ./src/extension.ts",
|
|
23
|
+
"ask": "pi -p --no-session -e ./src/extension.ts",
|
|
24
|
+
"test": "vitest run",
|
|
25
|
+
"test:watch": "vitest",
|
|
26
|
+
"mutation": "node scripts/mutation.mjs",
|
|
27
|
+
"conform": "tsx src/conform-cli.ts",
|
|
28
|
+
"regenerate": "tsx src/regenerate-cli.ts",
|
|
29
|
+
"validate": "tsx src/validate-cli.ts",
|
|
30
|
+
"doctor": "node bin/turri.mjs doctor",
|
|
31
|
+
"build": "node scripts/build.mjs",
|
|
32
|
+
"prepack": "npm run build",
|
|
33
|
+
"grammar:sync": "node scripts/sync-plyn-grammar.mjs"
|
|
34
|
+
},
|
|
35
|
+
"dependencies": {
|
|
36
|
+
"@earendil-works/pi-coding-agent": "^1.1.0",
|
|
37
|
+
"@earendil-works/pi-tui": "^1.1.0",
|
|
38
|
+
"@langchain/langgraph": "1.4.15",
|
|
39
|
+
"gray-matter": "^4.0.3",
|
|
40
|
+
"js-yaml": "^5.4.2",
|
|
41
|
+
"liquidjs": "^10.29.0",
|
|
42
|
+
"mdast-util-from-markdown": "^2.0.3",
|
|
43
|
+
"remark-parse": "^11.0.0",
|
|
44
|
+
"typebox": "^1.3.7",
|
|
45
|
+
"unified": "^11.0.5"
|
|
46
|
+
},
|
|
47
|
+
"devDependencies": {
|
|
48
|
+
"@stryker-mutator/core": "^10.0.0",
|
|
49
|
+
"@stryker-mutator/vitest-runner": "^10.0.0",
|
|
50
|
+
"@types/js-yaml": "^4.0.9",
|
|
51
|
+
"@types/node": "^24.0.0",
|
|
52
|
+
"tsx": "^4.19.0",
|
|
53
|
+
"typescript": "^5.7.0",
|
|
54
|
+
"vitest": "^5.0.1"
|
|
55
|
+
},
|
|
56
|
+
"publishConfig": {
|
|
57
|
+
"access": "public"
|
|
58
|
+
},
|
|
59
|
+
"files": [
|
|
60
|
+
"assets",
|
|
61
|
+
"bin",
|
|
62
|
+
"dist",
|
|
63
|
+
".pi",
|
|
64
|
+
"genome",
|
|
65
|
+
"stdlib",
|
|
66
|
+
"docs",
|
|
67
|
+
"README.md",
|
|
68
|
+
"LICENSE"
|
|
69
|
+
]
|
|
70
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: What every stdlib backend shares - a layered JSON HTTP service, its configuration, errors, logs and shutdown.
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
***implementation reqs***
|
|
6
|
+
|
|
7
|
+
- :Implementation: is an HTTP service that serves JSON.
|
|
8
|
+
- :Implementation: reads its configuration from environment variables once, at start, into one typed settings object that it passes to what needs it.
|
|
9
|
+
- Every setting has a default, except secrets.
|
|
10
|
+
- It listens on all interfaces, on the port in `PORT`, 8000 when unset.
|
|
11
|
+
- It does not start when a setting is missing or invalid: it writes which one to standard error and exits 1.
|
|
12
|
+
- :Implementation: is layered: a handler parses and validates the request, calls a service, and turns its result into the response; a service holds the business logic and knows nothing of HTTP; data access sits behind repository interfaces that the services are given.
|
|
13
|
+
- :Implementation: reports a failure of the business logic by raising one of its domain errors, which one place turns into the response.
|
|
14
|
+
- Not found is 404 with the code `not_found`, a conflict 409 `conflict`, invalid input 422 `validation_error`, a missing or bad credential 401 `unauthenticated`, a forbidden action 403 `forbidden`.
|
|
15
|
+
- :Implementation: answers every error, the framework's own included, with the JSON body `{"error": {"code": "<snake_case code>", "message": "<text for a person>"}}`.
|
|
16
|
+
- A `validation_error` body adds `details`: one `{"field": "<dotted path>", "message": "<text>"}` per invalid field.
|
|
17
|
+
- :Implementation: answers an unexpected failure with 500 and the code `internal_error`, never with its message or stack trace, and logs it at `ERROR` with the stack trace.
|
|
18
|
+
- :Implementation: logs to standard output as JSON, one object per line, with `timestamp` (ISO 8601, UTC), `level`, `logger`, `message`, and the `request_id` of the request it belongs to.
|
|
19
|
+
- It logs at the level in `LOG_LEVEL`, `INFO` when unset.
|
|
20
|
+
- It logs one `INFO` line when each request ends, with `method`, `path`, `status` and `duration_ms`.
|
|
21
|
+
- It never logs a secret, a token, a password, or a request or response body.
|
|
22
|
+
- :Implementation: takes each request's id from its `X-Request-ID` header, or makes a UUID v4 when it has none, and sends it back in `X-Request-ID`.
|
|
23
|
+
- :Implementation: stops on SIGTERM or SIGINT: it stops taking connections and finishes the requests under way, for at most 10 seconds.
|
|
24
|
+
- :UnitTests: cover the services directly, and each handler through the framework's in-process test client, never a listening port.
|
|
25
|
+
- :UnitTests: replace every repository and outside service with an in-memory stand-in.
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: How every stdlib backend's conformance tests run - against the service started from the built code, over HTTP only.
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
***test reqs***
|
|
6
|
+
|
|
7
|
+
- :ConformanceTests: start the service from the built code as a separate process, on a free port given in `PORT`, and wait until it accepts connections, for at most 30 seconds.
|
|
8
|
+
- They build and start it exactly as its stack's implementation reqs say - "It builds with ..." and "It starts with ..." - from the built code's own folder.
|
|
9
|
+
- :ConformanceTests: reach the service over HTTP only, and never import its code.
|
|
10
|
+
- :ConformanceTests: check a failure by its status code and the `error.code` of its body, never by its message.
|
|
11
|
+
- :ConformanceTests: stop the service when they finish, passing or failing.
|
|
12
|
+
- :ConformanceTests: pass the formatter, the linter and the type checker of their language as the conformance testing script runs them, and are typed throughout where their language allows it.
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
---
|
|
2
|
+
import:
|
|
3
|
+
- stdlib/backend/conformance/common
|
|
4
|
+
description: Conformance tests for stdlib/backend/go-gin as a Go module with go test.
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
***test reqs***
|
|
8
|
+
|
|
9
|
+
- :ConformanceTests: are a Go module of their own, using the `testing` package and `net/http`, run with `go test ./...`.
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
---
|
|
2
|
+
import:
|
|
3
|
+
- stdlib/backend/conformance/common
|
|
4
|
+
description: Conformance tests for stdlib/backend/go-nethttp as a Go module with go test.
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
***test reqs***
|
|
8
|
+
|
|
9
|
+
- :ConformanceTests: are a Go module of their own, using the `testing` package and `net/http`, run with `go test ./...`.
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
---
|
|
2
|
+
import:
|
|
3
|
+
- stdlib/backend/conformance/common
|
|
4
|
+
description: Conformance tests for stdlib/backend/java-springboot as a Maven project with JUnit 5 and the JDK's HttpClient.
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
***test reqs***
|
|
8
|
+
|
|
9
|
+
- :ConformanceTests: are a Maven project of their own, using JUnit 5, AssertJ and the JDK's `HttpClient`, run with `mvn test`.
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
---
|
|
2
|
+
import:
|
|
3
|
+
- stdlib/backend/conformance/common
|
|
4
|
+
description: Conformance tests for stdlib/backend/rust-axum as a Cargo package with reqwest.
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
***test reqs***
|
|
8
|
+
|
|
9
|
+
- :ConformanceTests: are a Cargo package of their own, using tokio and reqwest, run with `cargo test`.
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
---
|
|
2
|
+
import:
|
|
3
|
+
- stdlib/backend/conformance/common
|
|
4
|
+
description: Conformance tests for stdlib/backend/typescript-nestjs in Jest with fetch.
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
***test reqs***
|
|
8
|
+
|
|
9
|
+
- :ConformanceTests: use Jest with ts-jest and the built-in `fetch`, and run with `npx jest`.
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
---
|
|
2
|
+
import:
|
|
3
|
+
- stdlib/lang/go
|
|
4
|
+
- stdlib/backend/common
|
|
5
|
+
description: A backend in Go 1.25 with Gin, validator tags and slog.
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
***implementation reqs***
|
|
9
|
+
|
|
10
|
+
- :Implementation: uses Gin, made with `gin.New()` and its own middleware only, in release mode.
|
|
11
|
+
- :Implementation: keeps `main` in `cmd/server/`, and the rest in packages under `internal/`: `config`, `httpapi` - routes, handlers and middleware - `service` and `store`.
|
|
12
|
+
- It builds with `go build -o bin/server ./cmd/server`.
|
|
13
|
+
- :Implementation: parses the environment into a `Config` struct in `internal/config`.
|
|
14
|
+
- :Implementation: binds every request body with `ShouldBindJSON` and validates it with `binding` tags, answering a failure as `validation_error` with its `details`.
|
|
15
|
+
- :Implementation: has handlers report an error with `c.Error(err)`, and one middleware write the error body for the last one, finding the domain error with `errors.As`, and recover from panics as `internal_error`.
|
|
16
|
+
- :Implementation: logs with a `slog.JSONHandler` on standard output, with middleware that sets the request id and logs the request line; Gin's own logger is not used.
|
|
17
|
+
- :Implementation: stops with `http.Server.Shutdown` when the context from `signal.NotifyContext` ends.
|
|
18
|
+
- :UnitTests: use `net/http/httptest` against the Gin engine.
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
---
|
|
2
|
+
import:
|
|
3
|
+
- stdlib/lang/go
|
|
4
|
+
- stdlib/backend/common
|
|
5
|
+
description: A backend in Go 1.25 with the standard library's net/http and slog, and no framework.
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
***implementation reqs***
|
|
9
|
+
|
|
10
|
+
- :Implementation: uses the standard library's `net/http` with its method and path patterns, and no web framework.
|
|
11
|
+
- :Implementation: keeps `main` in `cmd/server/`, and the rest in packages under `internal/`: `config`, `httpapi` - handlers, middleware and error writing - `service` and `store`.
|
|
12
|
+
- It builds with `go build -o bin/server ./cmd/server`.
|
|
13
|
+
- :Implementation: parses the environment into a `Config` struct in `internal/config`.
|
|
14
|
+
- :Implementation: decodes every request body with `encoding/json` with `DisallowUnknownFields`, into a struct whose `Validate() error` reports every invalid field.
|
|
15
|
+
- :Implementation: writes handlers that return an error, adapted to `http.Handler` in one place that writes the error body, finding the domain error with `errors.As`.
|
|
16
|
+
- :Implementation: logs with a `slog.JSONHandler` on standard output, with middleware that adds the request id to the request's context and logs the request line.
|
|
17
|
+
- :Implementation: stops with `http.Server.Shutdown` when the context from `signal.NotifyContext` ends.
|
|
18
|
+
- :UnitTests: use `net/http/httptest` for handlers and middleware.
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
---
|
|
2
|
+
import:
|
|
3
|
+
- stdlib/lang/java
|
|
4
|
+
- stdlib/backend/common
|
|
5
|
+
description: A backend in Java 21 with Spring Boot 3, Maven, Bean Validation and Logback JSON logs.
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
***implementation reqs***
|
|
9
|
+
|
|
10
|
+
- :Implementation: uses Spring Boot 3 with Spring Web and Bean Validation, built with Maven through the Maven wrapper from `pom.xml` with the Spring Boot parent.
|
|
11
|
+
- :Implementation: keeps its code in one base package with the subpackages `web` - controllers, request and response records - `service`, `repository`, `domain`, `config` and `error`.
|
|
12
|
+
- It builds with `./mvnw package` into one executable jar, started with `java -jar target/<artifact>.jar` - `pom.xml` sets `<finalName>${project.artifactId}</finalName>`, so the jar's name carries no version.
|
|
13
|
+
- :Implementation: binds its settings from `application.yaml` into `@ConfigurationProperties` records, each set from an environment variable with its default, as `server.port: ${PORT:8000}`.
|
|
14
|
+
- :Implementation: validates every request body with `@Valid`, and answers `MethodArgumentNotValidException` as `validation_error` with its `details`.
|
|
15
|
+
- :Implementation: turns errors into responses in one `@RestControllerAdvice`, for the domain errors, Spring's own errors and `Exception`.
|
|
16
|
+
- :Implementation: logs as JSON with Logback and logstash-logback-encoder, set up in `logback-spring.xml`.
|
|
17
|
+
- A `OncePerRequestFilter` puts the request id in the MDC as `request_id`, sends it back, and logs the request line.
|
|
18
|
+
- :Implementation: shuts down with `server.shutdown: graceful` and a 10 second timeout per shutdown phase.
|
|
19
|
+
- :UnitTests: cover services with plain JUnit, and controllers with `@WebMvcTest` and `MockMvc` with the services mocked, run with `./mvnw test`.
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
---
|
|
2
|
+
import:
|
|
3
|
+
- stdlib/lang/python
|
|
4
|
+
- stdlib/backend/common
|
|
5
|
+
description: A backend in Python 3.12 with Django 5, Django REST Framework, Gunicorn and python-json-logger.
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
***implementation reqs***
|
|
9
|
+
|
|
10
|
+
- :Implementation: uses Django 5 with Django REST Framework, served by Gunicorn with 2 workers.
|
|
11
|
+
- :Implementation: keeps the Django project in the package `config/` - `settings.py`, `urls.py`, `wsgi.py` - and each area of the domain in a Django app under `apps/`.
|
|
12
|
+
- Each app has `models.py`, `serializers.py`, `views.py` with DRF views, `services.py`, `urls.py`, and its migrations in `migrations/`.
|
|
13
|
+
- :Implementation: reads its settings from the environment in `config/settings.py`, and its database from `DATABASE_URL` with dj-database-url, an SQLite file `db.sqlite3` when unset.
|
|
14
|
+
- It starts with `uv run python manage.py migrate --noinput && uv run gunicorn config.wsgi --bind 0.0.0.0:$PORT --workers 2`.
|
|
15
|
+
- :Implementation: keeps the business logic in each app's `services.py`, never in a view or a serializer.
|
|
16
|
+
- :Implementation: sets DRF's `EXCEPTION_HANDLER` to one function that turns the domain errors, DRF's exceptions and `Http404` into the error body, and DRF's `ValidationError` into `validation_error` with its `details`.
|
|
17
|
+
- :Implementation: logs as JSON with python-json-logger, set up in `LOGGING` in `config/settings.py`, with a middleware that keeps the request id in a context variable, adds it to every record, and logs the request line.
|
|
18
|
+
- :UnitTests: use pytest-django with DRF's `APIClient`, against a test database Django creates.
|
|
19
|
+
- `[tool.pytest.ini_options]` sets `DJANGO_SETTINGS_MODULE = "config.settings"`, so pytest-django finds the settings without an environment variable.
|