@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,80 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Conciseness and plain-language rules that keep .plain specs reviewable by a human
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
# Rules for keeping `.plain` specs concise
|
|
6
|
+
|
|
7
|
+
These rules apply to every section, to concept explanations, and to acceptance tests. `concise-specs-examples.md` holds a worked BAD to GOOD pair for each failure mode named here, and `concise-specs-integration-examples.md` covers the failure modes specific to integration specs.
|
|
8
|
+
|
|
9
|
+
## Why this matters
|
|
10
|
+
|
|
11
|
+
- A spec is the source of truth, so a human has to be able to review it
|
|
12
|
+
- A long, dense, or ornate spec is skipped rather than reviewed — an unreviewed spec is an unverified spec, however correct it happens to be
|
|
13
|
+
- Length is not a proxy for rigor: a spec earns trust by being checkable, not by being thorough-looking
|
|
14
|
+
- Conciseness is about removing what carries no information, never about removing required detail — the deterministic-interface, disambiguation, and testability rules always win (see `func-specs.md`)
|
|
15
|
+
|
|
16
|
+
## Do not write the obvious
|
|
17
|
+
|
|
18
|
+
Delete any line a competent reader would already assume. Ask of every line: *if this were missing, would anything be built differently?* If not, cut it.
|
|
19
|
+
|
|
20
|
+
Do not write:
|
|
21
|
+
|
|
22
|
+
- Restatements of a concept's own name (`:UserEmail: is the email address of a :User:.` adds nothing beyond what `definitions.md` already requires the concept to carry)
|
|
23
|
+
- Universal software defaults: that invalid input is rejected, that errors are reported, that data is saved when saving is the point of the operation
|
|
24
|
+
- Rationale, motivation, benefits, or background — the spec states what is, not why it was chosen
|
|
25
|
+
- Standard behavior of a technology already named in `***implementation reqs***` (an HTTP framework parses HTTP; a database driver opens connections)
|
|
26
|
+
- Restatements of a nearby bullet in different words, or of anything already stated in an `import`ed or `require`d module
|
|
27
|
+
- Anything about work the spec is not about ("no changes to the existing UI")
|
|
28
|
+
- Meta-commentary about the spec itself ("this spec is intentionally small", "as described above")
|
|
29
|
+
|
|
30
|
+
Worked pairs for every item above — padding, rationale, defaults, technology behavior,
|
|
31
|
+
meta-commentary, and duplication of an `import` — are in `concise-specs-examples.md`.
|
|
32
|
+
|
|
33
|
+
## Use plain words
|
|
34
|
+
|
|
35
|
+
- Prefer the shortest word that is still exact; a technical term needs to be exact, an elaborate one is not
|
|
36
|
+
- Write short sentences: one clause plus, at most, one qualifier
|
|
37
|
+
- Use the same word for the same thing every time — synonym variety reads as two different things
|
|
38
|
+
- Do not use adverbs and adjectives that no test can check: `robust`, `seamless`, `efficient`, `scalable`, `comprehensive`, `modern`, `properly`, `gracefully`, `as appropriate`, `where applicable`, `best practices`. Either state the checkable fact (`a response is returned within 200 ms`) or delete the word
|
|
39
|
+
- Do not hedge: `should ideally`, `may optionally`, `typically`, `generally`, `if possible`. A spec states one behavior; a hedge tells the renderer to guess
|
|
40
|
+
|
|
41
|
+
| Do not write | Write |
|
|
42
|
+
|---|---|
|
|
43
|
+
| utilize, leverage | use |
|
|
44
|
+
| facilitate, enable the ability to | let, or name the action |
|
|
45
|
+
| in order to | to |
|
|
46
|
+
| prior to / subsequent to | before / after |
|
|
47
|
+
| in the event that | if |
|
|
48
|
+
| is responsible for handling | handles |
|
|
49
|
+
| performs the validation of | validates |
|
|
50
|
+
| a plurality of | several, or a number |
|
|
51
|
+
| terminate, initiate | stop, start |
|
|
52
|
+
| functionality for X | X |
|
|
53
|
+
|
|
54
|
+
## Keep the shape small
|
|
55
|
+
|
|
56
|
+
- One statement per bullet, one bullet per fact (see `bullet-continuation.md`)
|
|
57
|
+
- Nest a sub-bullet only to remove a real ambiguity; a sub-bullet that merely elaborates is filler
|
|
58
|
+
- Consecutive specs that share a subject are usually one spec: if a top-level bullet only tells part of the story of one operation, merge the set, lead with the operation's result, and demote the steps and failure modes to sub-bullets (see *One operation fragmented into several specs* in `concise-specs-integration-examples.md`)
|
|
59
|
+
- Tables, prose paragraphs, and code blocks do not belong in a section; external artifacts are linked from `resources/` (see `linked-resources.md`)
|
|
60
|
+
- A bullet that answers more than one of *what is it*, *what does it do*, *how is it built*, and *how is it tested* is not one fact — split it across the sections that own those facts, which reads better and is the only way the renderer sees each part (see `module-structure.md`, and *Split one crowded bullet across its owning sections* in `concise-specs-examples.md`)
|
|
61
|
+
|
|
62
|
+
## Terse is not a defect
|
|
63
|
+
|
|
64
|
+
A finished spec usually reads as abrupt: clipped bullets, no connective tissue, no lead-ins. That is the intended shape, not something to smooth out afterwards.
|
|
65
|
+
|
|
66
|
+
- Add a word only to make a line correct, unambiguous, or checkable — never to make it read better
|
|
67
|
+
- Do not merge short bullets into one sentence for rhythm, and do not open a section or a bullet with a lead-in
|
|
68
|
+
- The bar is the other rules — correct, intent intact, reviewable, testable. A spec that clears them is finished however clipped it sounds
|
|
69
|
+
- Filler is not neutral: the renderer reads every line as a requirement, so a line written for flow can invent one
|
|
70
|
+
|
|
71
|
+
## Review pass before writing to disk
|
|
72
|
+
|
|
73
|
+
Read the drafted lines once more and apply, in order:
|
|
74
|
+
|
|
75
|
+
1. **The deletion test** — cut every line whose removal would change nothing about what gets built
|
|
76
|
+
2. **The word test** — replace every ornate word with a plain one, and delete every uncheckable adjective, adverb, and hedge
|
|
77
|
+
3. **The duplication test** — cut anything already stated in this file, an `import`, or a `requires` chain
|
|
78
|
+
4. **The read-aloud test** — a line you cannot read aloud in one breath is either two facts or one padded one
|
|
79
|
+
|
|
80
|
+
If a cut would remove an interface detail, a boundary case, or a disambiguation, keep the fact and shorten the wording instead — see *Do not over-cut* in `concise-specs-examples.md` for what a spec cut past its content looks like.
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Rules for writing ***definitions*** sections in .plain files
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
# Rules for writing `***definitions***`
|
|
6
|
+
|
|
7
|
+
When writing or editing a `***definitions***` section in a `.plain` file, always follow these rules:
|
|
8
|
+
|
|
9
|
+
## Concept syntax
|
|
10
|
+
- Wrap concept names in colons: `:ConceptName:`
|
|
11
|
+
- Use CamelCase starting with an uppercase letter
|
|
12
|
+
- Valid characters: letters, digits, `+`, `-`, `.`, `_`
|
|
13
|
+
|
|
14
|
+
## Uniqueness
|
|
15
|
+
- Concept names must be globally unique across the spec and all its imports
|
|
16
|
+
- Check for collisions with concepts coming from `import` and `requires` modules before adding
|
|
17
|
+
|
|
18
|
+
## Predefined concepts (do not redefine)
|
|
19
|
+
- ***plain ships several predefined concepts that are available in every module without being defined
|
|
20
|
+
- Never add a `***definitions***` entry for any of them — the renderer treats redefinitions as conflicts
|
|
21
|
+
|
|
22
|
+
| Predefined concept | Meaning |
|
|
23
|
+
|--------------------|---------|
|
|
24
|
+
| `:plainDefinitions:` | Content of the `***definitions***` section |
|
|
25
|
+
| `:plainImplementationReqs:` | Content of the `***implementation reqs***` section |
|
|
26
|
+
| `:plainFunctionality:` | Content of the `***functional specs***` section |
|
|
27
|
+
| `:plainTestReqs:` | Content of the `***test reqs***` section |
|
|
28
|
+
| `:Implementation:` | The system implementing `:plainFunctionality:` |
|
|
29
|
+
| `:plainImplementationCode:` | The generated implementation code |
|
|
30
|
+
| `:UnitTests:` | Auto-generated unit tests for individual functionalities |
|
|
31
|
+
| `:ConformanceTests:` | Auto-generated tests verifying conformance to the spec |
|
|
32
|
+
| `:AcceptanceTest:` / `:AcceptanceTests:` | Tests validating specific aspects of the implementation |
|
|
33
|
+
|
|
34
|
+
## Conciseness
|
|
35
|
+
- A definition is one plain sentence plus, where needed, its attributes — see `concise-specs.md`
|
|
36
|
+
- Never restate the concept name as its own definition, and never explain why the concept exists
|
|
37
|
+
|
|
38
|
+
## Define before use
|
|
39
|
+
- A concept must be defined before it is referenced in any section (definitions, implementation reqs, functional specs, test reqs)
|
|
40
|
+
- Sources of definitions: the module's own `***definitions***`, an `import`ed module's definitions, or a `require`d module's `exported_concepts`
|
|
41
|
+
|
|
42
|
+
## No circular references
|
|
43
|
+
- Concept references must not form cycles — if A references B, then B must not reference A (directly or indirectly)
|
|
44
|
+
- Insert each concept after any concepts it references
|
|
45
|
+
|
|
46
|
+
Bad — circular:
|
|
47
|
+
|
|
48
|
+
```plain
|
|
49
|
+
***definitions***
|
|
50
|
+
|
|
51
|
+
- :Customer: is a user who has placed at least one :Order:.
|
|
52
|
+
|
|
53
|
+
- :Order: is placed by :Customer: and contains :OrderItem: entries.
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
`:Order:` references `:Customer:`, and `:Customer:` references `:Order:`. Fix by removing the back-reference:
|
|
57
|
+
|
|
58
|
+
```plain
|
|
59
|
+
***definitions***
|
|
60
|
+
|
|
61
|
+
- :Customer: is a user of the system.
|
|
62
|
+
|
|
63
|
+
- :Order: is placed by :Customer: and contains :OrderItem: entries.
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
## Exported concepts are not transitive
|
|
67
|
+
- If module A exports a concept and module B `requires` A, a module C with `requires: [B]` does **not** gain access to A's exports
|
|
68
|
+
- To use an ancestor's exports, list that ancestor in `requires` as well; concepts shared more broadly belong in a common import module
|
|
69
|
+
|
|
70
|
+
## Description quality
|
|
71
|
+
- Descriptions must be clear, concise, and language-agnostic
|
|
72
|
+
- Nest attributes and constraints as sub-bullets
|
|
73
|
+
- Do not use programming language constructs (generics, annotations, framework types) in definitions
|
|
74
|
+
|
|
75
|
+
## Concept granularity and technical components
|
|
76
|
+
- Definitions are not restricted to high-level domain objects. When an implementation warrants abstract, technical, or infrastructural components to clarify responsibilities, it is desired and encouraged to define them.
|
|
77
|
+
- Group related technical concepts using the `of` keyword or by stating their relationship explicitly.
|
|
78
|
+
|
|
79
|
+
Example of defining technical components alongside their behavior:
|
|
80
|
+
|
|
81
|
+
```plain
|
|
82
|
+
***definitions***
|
|
83
|
+
|
|
84
|
+
- :CsvToJsonConverter: bidirectionally converts csv and json strings.
|
|
85
|
+
|
|
86
|
+
- :CsvToJson: of :CsvToJsonConverter: converts a list of csv strings (e.g., "value1,value2,value3") to a list of json strings (e.g., "{\"key1\":\"value1\"}").
|
|
87
|
+
|
|
88
|
+
- :JsonToCsv: of :CsvToJsonConverter: converts a list of json strings (e.g., "{\"key1\":\"value1\"}") to a list of csv strings (e.g., "value1,value2").
|
|
89
|
+
|
|
90
|
+
- :StorageService: is an abstraction for a remote blob storage backend.
|
|
91
|
+
|
|
92
|
+
- :CsvFileWriter: writes CSV rows to local files, automatically rotates to new files when size limits are reached, and flushes completed files to :StorageService:.
|
|
93
|
+
|
|
94
|
+
- :CsvFileReader: reads CSV rows in a batched manner.
|
|
95
|
+
|
|
96
|
+
- :CsvJoiner: joins csvs and uses :CsvFileWriter: to create the merged CSV.
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
## Format
|
|
100
|
+
|
|
101
|
+
```plain
|
|
102
|
+
***definitions***
|
|
103
|
+
|
|
104
|
+
- :ConceptName: is a description of what it represents.
|
|
105
|
+
- Attribute one (required)
|
|
106
|
+
- Attribute two (optional)
|
|
107
|
+
```
|
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
|
|
3
|
+
# Export Java 21
|
|
4
|
+
#export JAVA_HOME="/opt/homebrew/opt/openjdk@21/libexec/openjdk.jdk/Contents/Home"
|
|
5
|
+
#export PATH="$JAVA_HOME/bin:$PATH"
|
|
6
|
+
echo "JAVA_HOME: $JAVA_HOME"
|
|
7
|
+
|
|
8
|
+
# Check if build folder name is provided
|
|
9
|
+
if [ -z "$1" ]; then
|
|
10
|
+
printf "Error: No build folder name provided.\n"
|
|
11
|
+
printf "Usage: $0 <build_folder_name> <conformance_tests_folder>\n"
|
|
12
|
+
exit 1
|
|
13
|
+
fi
|
|
14
|
+
|
|
15
|
+
# Check if conformance tests folder name is provided
|
|
16
|
+
if [ -z "$2" ]; then
|
|
17
|
+
printf "Error: No conformance tests folder name provided.\n"
|
|
18
|
+
printf "Usage: $0 <build_folder_name> <conformance_tests_folder>\n"
|
|
19
|
+
exit 1
|
|
20
|
+
fi
|
|
21
|
+
|
|
22
|
+
current_dir=$(pwd)
|
|
23
|
+
|
|
24
|
+
# Resolve the conformance tests folder to an absolute path ($2 may be relative
|
|
25
|
+
# on older renderers; newer renderers pass it as an absolute path).
|
|
26
|
+
case "$2" in
|
|
27
|
+
/*) CONFORMANCE_TESTS_SOURCE="$2" ;;
|
|
28
|
+
*) CONFORMANCE_TESTS_SOURCE="$current_dir/$2" ;;
|
|
29
|
+
esac
|
|
30
|
+
|
|
31
|
+
# Host-relative paths of the module, as declared in ***implementation reqs***.
|
|
32
|
+
# Replace the placeholders with the module's integration source path and its
|
|
33
|
+
# :UnitTests: source path (the same paths run_unittests_java.sh copies to).
|
|
34
|
+
INTEGRATION_CODE_PATH="<placeholder for integration code>"
|
|
35
|
+
INTEGRATION_TESTS_PATH="<placeholder for integration code tests>"
|
|
36
|
+
|
|
37
|
+
# Host codebase root: read from HOST_CODEBASE_ROOT (the one configured fact),
|
|
38
|
+
# defaulting to three levels up from this script.
|
|
39
|
+
MAIN_PROJECT_FOLDER="${HOST_CODEBASE_ROOT:-$(cd "$(dirname "$0")/../../.." && pwd)}"
|
|
40
|
+
|
|
41
|
+
# Stage the generated code into the host codebase (same copy as run_unittests_java.sh)
|
|
42
|
+
if [ "${VERBOSE:-}" -eq 1 ] 2>/dev/null; then
|
|
43
|
+
printf "Copying generated code to main project folder: $MAIN_PROJECT_FOLDER\n"
|
|
44
|
+
fi
|
|
45
|
+
|
|
46
|
+
# Check if the main project folder exists
|
|
47
|
+
if [ ! -d "$MAIN_PROJECT_FOLDER" ]; then
|
|
48
|
+
echo "Error: Main project folder '$MAIN_PROJECT_FOLDER' does not exist."
|
|
49
|
+
exit 2
|
|
50
|
+
fi
|
|
51
|
+
|
|
52
|
+
# clean existing code of this module only from the main project folder
|
|
53
|
+
rm -rf "$MAIN_PROJECT_FOLDER/$INTEGRATION_CODE_PATH"/*
|
|
54
|
+
rm -rf "$MAIN_PROJECT_FOLDER/$INTEGRATION_TESTS_PATH"/*
|
|
55
|
+
|
|
56
|
+
# copy generated code to the main project folder
|
|
57
|
+
mkdir -p "$MAIN_PROJECT_FOLDER/$INTEGRATION_CODE_PATH"
|
|
58
|
+
mkdir -p "$MAIN_PROJECT_FOLDER/$INTEGRATION_TESTS_PATH"
|
|
59
|
+
cp -R "$1/$INTEGRATION_CODE_PATH"/* "$MAIN_PROJECT_FOLDER/$INTEGRATION_CODE_PATH"
|
|
60
|
+
cp -R "$1/$INTEGRATION_TESTS_PATH"/* "$MAIN_PROJECT_FOLDER/$INTEGRATION_TESTS_PATH"
|
|
61
|
+
|
|
62
|
+
# Move to the host codebase and build / install it, so the conformance project
|
|
63
|
+
# resolves this build from the local Maven repository (~/.m2), not a stale one.
|
|
64
|
+
echo "Moving to: $MAIN_PROJECT_FOLDER"
|
|
65
|
+
cd "$MAIN_PROJECT_FOLDER" 2>/dev/null
|
|
66
|
+
|
|
67
|
+
if [ $? -ne 0 ]; then
|
|
68
|
+
printf "Error: Java build folder '$MAIN_PROJECT_FOLDER' does not exist.\n"
|
|
69
|
+
exit 2
|
|
70
|
+
fi
|
|
71
|
+
|
|
72
|
+
# Remove target directory to avoid conflicts with a previous build
|
|
73
|
+
rm -rf ./target || echo "Warning: some files may be locked"
|
|
74
|
+
|
|
75
|
+
# -DskipTests is legitimate here: this step only installs the host build;
|
|
76
|
+
# the conformance tests run below.
|
|
77
|
+
echo "Running maven install in the host codebase..."
|
|
78
|
+
mvn clean install -Dspring-boot.repackage.skip=true -DskipTests
|
|
79
|
+
|
|
80
|
+
install_exit_code=$?
|
|
81
|
+
if [ $install_exit_code -ne 0 ]; then
|
|
82
|
+
echo "Error: Maven install failed in the host codebase with exit code $install_exit_code..."
|
|
83
|
+
exit 69
|
|
84
|
+
fi
|
|
85
|
+
|
|
86
|
+
# Scratch folder lives in the system temp directory so no build debris is left
|
|
87
|
+
# inside the project. It is removed again when the script exits.
|
|
88
|
+
CONFORMANCE_TESTS_FOLDER="/tmp/java_conformance"
|
|
89
|
+
|
|
90
|
+
trap 'rm -rf "$CONFORMANCE_TESTS_FOLDER"' EXIT
|
|
91
|
+
|
|
92
|
+
printf "Preparing Java conformance tests subfolder: $CONFORMANCE_TESTS_FOLDER\n"
|
|
93
|
+
|
|
94
|
+
# Check if the conformance tests scratch folder exists
|
|
95
|
+
if [ -d "$CONFORMANCE_TESTS_FOLDER" ]; then
|
|
96
|
+
# Find and delete all files and folders
|
|
97
|
+
find "$CONFORMANCE_TESTS_FOLDER" -mindepth 1 -exec rm -rf {} +
|
|
98
|
+
|
|
99
|
+
if [ "${VERBOSE:-}" -eq 1 ] 2>/dev/null; then
|
|
100
|
+
printf "Cleanup completed.\n"
|
|
101
|
+
fi
|
|
102
|
+
else
|
|
103
|
+
# print the current directory
|
|
104
|
+
printf "Current directory: $(pwd)\n"
|
|
105
|
+
|
|
106
|
+
printf "Subfolder does not exist. Creating it...\n"
|
|
107
|
+
mkdir -p $CONFORMANCE_TESTS_FOLDER
|
|
108
|
+
fi
|
|
109
|
+
|
|
110
|
+
printf "Copying all files and folders from $CONFORMANCE_TESTS_SOURCE to $CONFORMANCE_TESTS_FOLDER...\n"
|
|
111
|
+
cp -R "$CONFORMANCE_TESTS_SOURCE"/* $CONFORMANCE_TESTS_FOLDER
|
|
112
|
+
|
|
113
|
+
# Move to the subfolder
|
|
114
|
+
printf "Moving to $CONFORMANCE_TESTS_FOLDER...\n"
|
|
115
|
+
cd "$CONFORMANCE_TESTS_FOLDER" 2>/dev/null
|
|
116
|
+
|
|
117
|
+
if [ $? -ne 0 ]; then
|
|
118
|
+
printf "Error: Java conformance tests folder '$CONFORMANCE_TESTS_FOLDER' does not exist.\n"
|
|
119
|
+
exit 2
|
|
120
|
+
fi
|
|
121
|
+
|
|
122
|
+
echo "Running maven install in the conformance tests folder..."
|
|
123
|
+
mvn clean install -DskipTests
|
|
124
|
+
|
|
125
|
+
|
|
126
|
+
if [ $? -ne 0 ]; then
|
|
127
|
+
exit 2
|
|
128
|
+
fi
|
|
129
|
+
|
|
130
|
+
echo "Running maven test in the conformance tests folder..."
|
|
131
|
+
|
|
132
|
+
output=$(mvn test --no-transfer-progress 2>&1)
|
|
133
|
+
exit_code=$?
|
|
134
|
+
|
|
135
|
+
echo "Finished running maven test in the conformance tests folder..."
|
|
136
|
+
|
|
137
|
+
# Check if no tests were run
|
|
138
|
+
if echo "$output" | grep -q 'Tests run: [1-9][0-9]*, Failures: 0, Errors: 0, Skipped: 0'; then
|
|
139
|
+
echo "All tests passed"
|
|
140
|
+
echo "$output"
|
|
141
|
+
exit $exit_code
|
|
142
|
+
else
|
|
143
|
+
# Check if no tests were run
|
|
144
|
+
if echo "$output" | grep -q 'Tests run: 0, Failures: 0, Errors: 0, Skipped: 0'; then
|
|
145
|
+
echo "Tests run: 0"
|
|
146
|
+
echo "Error: No tests were executed (Tests run: 0)"
|
|
147
|
+
echo "You are seeing this cause the following appeared in the output: Tests run: 0, Failures: 0, Errors: 0, Skipped: 0"
|
|
148
|
+
else
|
|
149
|
+
echo "Some tests failed, had errors, or were skipped. This is not allowed. All tests must pass."
|
|
150
|
+
fi
|
|
151
|
+
fi
|
|
152
|
+
|
|
153
|
+
# If there was an error, print the output and exit with the error code
|
|
154
|
+
if [ $exit_code -ne 0 ]; then
|
|
155
|
+
echo "Error: Maven test failed in the conformance tests folder with exit code $exit_code..."
|
|
156
|
+
echo "$output"
|
|
157
|
+
exit $exit_code
|
|
158
|
+
fi
|
|
159
|
+
|
|
160
|
+
echo "Finished running conformance tests..."
|
|
161
|
+
echo "$output"
|
|
162
|
+
# Echo the original exit code of the unittest command
|
|
163
|
+
exit $exit_code
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
|
|
3
|
+
# Export Java 21
|
|
4
|
+
#export JAVA_HOME="/opt/homebrew/opt/openjdk@21/libexec/openjdk.jdk/Contents/Home"
|
|
5
|
+
#export PATH="$JAVA_HOME/bin:$PATH"
|
|
6
|
+
echo "JAVA_HOME: $JAVA_HOME"
|
|
7
|
+
|
|
8
|
+
# Check if subfolder name is provided
|
|
9
|
+
if [ -z "$1" ]; then
|
|
10
|
+
echo "Error: No subfolder name provided."
|
|
11
|
+
echo "Usage: $0 <subfolder_name>"
|
|
12
|
+
exit 1
|
|
13
|
+
fi
|
|
14
|
+
# FIX: Changed this line from "../../" to "../../.." to go up three levels to the main project folder
|
|
15
|
+
MAIN_PROJECT_FOLDER="$(cd "$(dirname "$0")/../../.." && pwd)"
|
|
16
|
+
|
|
17
|
+
if [ "${VERBOSE:-}" -eq 1 ] 2>/dev/null; then
|
|
18
|
+
printf "Copying generated code to main project folder: $MAIN_PROJECT_FOLDER\n"
|
|
19
|
+
fi
|
|
20
|
+
|
|
21
|
+
# Check if the main project folder exists
|
|
22
|
+
if [ ! -d "$MAIN_PROJECT_FOLDER" ]; then
|
|
23
|
+
echo "Error: Main project folder '$MAIN_PROJECT_FOLDER' does not exist."
|
|
24
|
+
exit 2
|
|
25
|
+
fi
|
|
26
|
+
|
|
27
|
+
# clean existing code from the main project folder
|
|
28
|
+
rm -rf $MAIN_PROJECT_FOLDER/<placeholder for integration code>/*
|
|
29
|
+
rm -rf $MAIN_PROJECT_FOLDER/<placeholder for integration code tests>/*
|
|
30
|
+
|
|
31
|
+
# copy generated code to the main project folder
|
|
32
|
+
# FIX: added the mkdir lines otherwise it complains that the cp destintation directory does not exist
|
|
33
|
+
mkdir -p $MAIN_PROJECT_FOLDER/<placeholder for integration code>
|
|
34
|
+
mkdir -p $MAIN_PROJECT_FOLDER/<placeholder for integration code tests>
|
|
35
|
+
cp -R $1/<placeholder for integration code>/* $MAIN_PROJECT_FOLDER/<placeholder for integration code>
|
|
36
|
+
cp -R $1/<placeholder for integration code tests>/* $MAIN_PROJECT_FOLDER/<placeholder for integration code tests>
|
|
37
|
+
|
|
38
|
+
# Move to the subfolder
|
|
39
|
+
# FIX: added this line to print the current directory to help the LLM
|
|
40
|
+
echo "Moving to: $MAIN_PROJECT_FOLDER"
|
|
41
|
+
cd "$MAIN_PROJECT_FOLDER" 2>/dev/null
|
|
42
|
+
|
|
43
|
+
if [ $? -ne 0 ]; then
|
|
44
|
+
printf "Error: Java build folder '$MAIN_PROJECT_FOLDER' does not exist.\n"
|
|
45
|
+
exit 2
|
|
46
|
+
fi
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
# Execute all Java unittests in the subfolder
|
|
50
|
+
echo "Running Java unittests in $1..."
|
|
51
|
+
mvn test -Dtest='<placeholder for integration code tests package>.**.*Test' checkstyle:check
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Rules for using exported_concepts in .plain files
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
# Rules for `exported_concepts`
|
|
6
|
+
|
|
7
|
+
When adding or editing `exported_concepts` in a `.plain` file's frontmatter, always follow these rules:
|
|
8
|
+
|
|
9
|
+
## What exported_concepts does
|
|
10
|
+
- `exported_concepts` declares which concepts from this module are visible to modules that `require` it
|
|
11
|
+
- Concepts not listed in `exported_concepts` are internal to the module and invisible to downstream modules
|
|
12
|
+
- Only modules that use `requires` receive exported concepts — `import` gives access to all definitions, not just exports
|
|
13
|
+
- An exported concept is visible with its full definition text
|
|
14
|
+
|
|
15
|
+
## When to use it
|
|
16
|
+
- Use `exported_concepts` on any module that other modules will `require`
|
|
17
|
+
- List only the concepts that downstream modules actually need to reference
|
|
18
|
+
- Keep the exported surface small — expose only what is necessary
|
|
19
|
+
|
|
20
|
+
## Concepts must be defined
|
|
21
|
+
- Every concept listed in `exported_concepts` must be defined in the module's own `***definitions***` section
|
|
22
|
+
- Do not export concepts that are not defined in the module
|
|
23
|
+
|
|
24
|
+
## Exports are not transitive
|
|
25
|
+
- If module A exports `:Foo:` and module B `requires` A, a module C with `requires: [B]` does **not** gain access to `:Foo:`
|
|
26
|
+
- If module C also needs `:Foo:`, it should list A in its `requires` as well (`requires: [A, B]`) or get `:Foo:` through a common import module
|
|
27
|
+
|
|
28
|
+
## Format
|
|
29
|
+
|
|
30
|
+
```plain
|
|
31
|
+
---
|
|
32
|
+
import:
|
|
33
|
+
- airplain
|
|
34
|
+
exported_concepts: [":User:", ":JwtToken:"]
|
|
35
|
+
description: Exports User and JwtToken for downstream modules
|
|
36
|
+
---
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
List concepts as a YAML array with each concept in `:ConceptName:` notation.
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Rules for writing ***functional specs*** and ***acceptance tests*** in .plain files
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
# Rules for writing `***functional specs***`
|
|
6
|
+
|
|
7
|
+
When writing or editing a `***functional specs***` section in a `.plain` file, always follow these rules:
|
|
8
|
+
|
|
9
|
+
## Voice
|
|
10
|
+
- Write each functional spec as a **statement of fact** — present tense, indicative mood — describing what the software *is* or *does* once built, not as a command telling a developer what to build
|
|
11
|
+
- **Name a concrete subject**: an actor (`A :User: can …`), a named component or concept (`:PdfRenderer: lays out …`, `:Order: processing depends on …`), or the affected data in passive voice (`All :Resource: items are returned`)
|
|
12
|
+
- **Avoid the vague subject "The system …"** — it says nothing about *which* part does the work. Prefer the concrete component/concept, or passive voice with the affected concept as the subject
|
|
13
|
+
- **Never open a spec with an imperative** like "Implement …", "Add …", "Create …", "Show …", "Display …", "Build …" — those read as instructions, not specifications
|
|
14
|
+
- Do **not** use "should" / "should be able to" in a functional spec — that is requirement-modal voice; state the fact directly
|
|
15
|
+
|
|
16
|
+
BAD — imperative commands, or the vague "The system":
|
|
17
|
+
|
|
18
|
+
```plain
|
|
19
|
+
***functional specs***
|
|
20
|
+
|
|
21
|
+
- Implement the entry point for :App:.
|
|
22
|
+
|
|
23
|
+
- The system shows :TaskList:.
|
|
24
|
+
|
|
25
|
+
- Add :Task:.
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
GOOD — statements of fact with concrete subjects:
|
|
29
|
+
|
|
30
|
+
```plain
|
|
31
|
+
***functional specs***
|
|
32
|
+
|
|
33
|
+
- :App: has an entry point.
|
|
34
|
+
|
|
35
|
+
- The :TaskList: is displayed.
|
|
36
|
+
|
|
37
|
+
- A :User: can add a :Task:. Only valid :Task: items are added.
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
## Conciseness
|
|
41
|
+
- A functional spec that a human will not read is not reviewed, so every spec must be as short as its content allows — see `concise-specs.md`
|
|
42
|
+
- Cut anything obvious, any rationale, and every uncheckable adjective; keep every interface detail, boundary case, and disambiguation
|
|
43
|
+
- Shorten the wording, never the facts: the rules below always win over brevity
|
|
44
|
+
|
|
45
|
+
## Complexity limit
|
|
46
|
+
- The complexity limit is about breaking software into small, independently understandable behavior slices that people and AI can reason about, implement, and test reliably
|
|
47
|
+
- The current mechanism for enforcing the complexity limit is that each functional spec must imply a **maximum of 200 changed lines of code**.
|
|
48
|
+
- Smaller slices produce focused test failures, reduce hidden coupling and regression surface, and make review and incremental rendering more predictable
|
|
49
|
+
- If a spec is too large, use `break-down-func-spec` to split it into multiple smaller, independent specs
|
|
50
|
+
- Use `analyze-if-func-spec-too-complex` to verify before inserting
|
|
51
|
+
- Use `analyze-func-specs` to check a spec (or a batch of specs) against all relevant existing specs in a single batched call; use `resolve-spec-conflict` for each conflicting pair it reports
|
|
52
|
+
|
|
53
|
+
## First-spec bootstrap cost
|
|
54
|
+
- The first functional spec in a new root module carries implicit implementation cost even when its text describes little behavior
|
|
55
|
+
- The renderer may need to create the project structure, package metadata, framework configuration, entry point, initial source files, and unit-test infrastructure before implementing the behavior
|
|
56
|
+
- Count that bootstrap work toward the 200-changed-line limit; it is not free setup outside the spec
|
|
57
|
+
- Make the first spec the module's simplest useful, independently observable capability
|
|
58
|
+
- The first spec must still imply a testable outcome; setup alone is not a functional spec
|
|
59
|
+
- Prefer one minimal entry point or public operation with a deterministic result that can be invoked and verified
|
|
60
|
+
- Defer persistence, integrations, multiple interfaces, branching workflows, and cross-cutting behavior until later specs can build on the generated baseline
|
|
61
|
+
- This extra bootstrap cost applies when no previous implementation baseline exists; a module that continues from a `requires` module may already inherit the necessary scaffold
|
|
62
|
+
|
|
63
|
+
## Chronological ordering
|
|
64
|
+
- Specs are rendered incrementally, top to bottom
|
|
65
|
+
- The renderer has **no knowledge of future specs** — only previously rendered specs are in context
|
|
66
|
+
- A new spec can reference behavior from earlier specs but cannot assume anything about specs that come after it
|
|
67
|
+
- Functional specs from `requires` modules are treated as previous requirements — transitively, across the whole ancestor chain
|
|
68
|
+
|
|
69
|
+
## No conflicts
|
|
70
|
+
- The new spec must not contradict any existing functional spec
|
|
71
|
+
- Before adding, review all existing specs and verify compatibility
|
|
72
|
+
- If ambiguity exists, add explicit detail to eliminate any conflicting interpretation
|
|
73
|
+
|
|
74
|
+
## Language agnosticism
|
|
75
|
+
- Write in terms of behavior, concepts, and domain logic
|
|
76
|
+
- Avoid language-specific terminology: generics syntax, framework annotations, language-specific collection types, decorator syntax, base-class keywords, framings like "POJO" or "dataclass"
|
|
77
|
+
- General technical terms that are not language-specific are fine: null values, JSON types, HTTP status codes, REST endpoints, etc.
|
|
78
|
+
- **Naming concrete components is encouraged.** Functional specs can and should refer to concrete domain components, services, or entities (e.g., `:PaymentProcessor:`, `:UserRepository:`, `:DataConverter:`) and their operations (e.g., `:ChargeCard:`, `:FindById:`), pinning down their inputs, outputs, and error behavior. Those names are part of the public contract and survive a language switch. What they must **not** do is bake in how the contract is realized (`@staticmethod`, `class Foo extends Bar`, `List<T>`, `async def`, etc.)
|
|
79
|
+
- **Litmus test:** if the project switched from Python to Java (or vice versa), would the functional spec read correctly with only `***implementation reqs***` updated? If yes, the spec is language-agnostic. If the spec itself would need rewording, the construct belongs in implementation reqs.
|
|
80
|
+
|
|
81
|
+
## Disambiguation
|
|
82
|
+
- Each functional spec must be unambiguous — the renderer should have only one reasonable interpretation
|
|
83
|
+
- If a single line is not enough to fully disambiguate the behavior, use **nested sub-bullets** to add detail
|
|
84
|
+
- Nested lines clarify the parent spec — they do not introduce separate functionality
|
|
85
|
+
- Even with nested detail, the spec must still imply ≤ 200 lines of code
|
|
86
|
+
|
|
87
|
+
## Testability
|
|
88
|
+
- Every functional spec must describe behavior that can be verified through an observable interface
|
|
89
|
+
- State the triggering input or action and the deterministic outcome a test can observe
|
|
90
|
+
- Include relevant outputs, state changes, errors, or externally visible side effects
|
|
91
|
+
- Do not rely on generated implementation details as the only way to verify the behavior
|
|
92
|
+
- If a black-box conformance test cannot be derived from the spec, make the behavior more explicit
|
|
93
|
+
- Testability does not require an acceptance test; acceptance tests are reserved for full workflows
|
|
94
|
+
|
|
95
|
+
## Deterministic interface
|
|
96
|
+
- Specs must be detailed enough that a developer can use the built software without reading the generated code
|
|
97
|
+
- All external interfaces must be explicit: REST endpoint paths and HTTP methods, CLI command names and arguments, file formats, message schemas, etc.
|
|
98
|
+
- Never leave interface details up to the renderer's discretion
|
|
99
|
+
|
|
100
|
+
## Encapsulation
|
|
101
|
+
- Functionality must be self-contained in the spec text
|
|
102
|
+
- `requires` modules only pass on functional specs and `exported_concepts` — never implementation reqs, so do not rely on implementation reqs to convey behavior
|
|
103
|
+
- Behavior that downstream modules need must be expressed in functional specs, not elsewhere
|
|
104
|
+
|
|
105
|
+
## Acceptance tests
|
|
106
|
+
- Nest `***acceptance tests***` under a functional spec when verification criteria are needed
|
|
107
|
+
- Each acceptance test must be a **full workflow test** — a specific scenario that exercises the functional spec end-to-end, not a unit-level check of a single field or condition
|
|
108
|
+
- Do not restate the obvious behavior from the functional spec — simple, direct verifications are already auto-generated as conformance tests. Acceptance tests must go beyond that: multi-step workflows, interactions between concepts, edge-case scenarios, or end-to-end sequences that prove the feature works in a realistic context
|
|
109
|
+
- Each acceptance test must be a direct logical consequence of the parent spec — it illustrates, not extends
|
|
110
|
+
- Acceptance tests must describe concrete, verifiable outcomes — not vague qualities
|
|
111
|
+
- Acceptance tests must not contradict, narrow, or extend beyond the parent spec
|
|
112
|
+
|
|
113
|
+
## Format
|
|
114
|
+
|
|
115
|
+
```plain
|
|
116
|
+
***functional specs***
|
|
117
|
+
|
|
118
|
+
- :App: has an entry point.
|
|
119
|
+
|
|
120
|
+
- A :User: can add a :Task:. Only valid :Task: items are added.
|
|
121
|
+
|
|
122
|
+
- A :User: can send a :Message: to a :Conversation:.
|
|
123
|
+
- A :Message: must have non-empty content.
|
|
124
|
+
- The :Message: is appended to the end of the :Conversation:.
|
|
125
|
+
- All :Participant: members of the :Conversation: can see the new :Message:.
|
|
126
|
+
|
|
127
|
+
***acceptance tests***
|
|
128
|
+
- Sending a :Message: to a :Conversation: with three participants should make the message visible to all three.
|
|
129
|
+
```
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Rules for writing ***implementation reqs*** sections in .plain files
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
# Rules for writing `***implementation reqs***`
|
|
6
|
+
|
|
7
|
+
When writing or editing an `***implementation reqs***` section in a `.plain` file, always follow these rules:
|
|
8
|
+
|
|
9
|
+
## HOW, not WHAT
|
|
10
|
+
- Implementation reqs describe **how** the software should be built, not **what** it should do
|
|
11
|
+
- Observable behavior (endpoints, business rules, user-facing features) belongs in `***functional specs***`
|
|
12
|
+
- Internal structure, technology choices, and coding guidance belong here
|
|
13
|
+
|
|
14
|
+
## `:UnitTests:` lives here (hard rule)
|
|
15
|
+
- **Everything** about `:UnitTests:` goes in `***implementation reqs***` — paths, approach, packages, framework, conventions, fixtures, mocking policy, file layout, naming, lint / static-analysis gates
|
|
16
|
+
- `:UnitTests:` are part of the generated codebase (they sit inside `plain_modules/<module>/code/` alongside the implementation), so requirements that shape them are implementation reqs by definition
|
|
17
|
+
- The unit-test generator reads **only** `***implementation reqs***` — anything about `:UnitTests:` placed elsewhere (e.g. `***test reqs***`) is silently ignored
|
|
18
|
+
- Author each `:UnitTests:` requirement via `add-implementation-requirement` and phrase it in terms of `:UnitTests:` so the partition stays visible at a glance
|
|
19
|
+
|
|
20
|
+
## What belongs here
|
|
21
|
+
- Technology choices: language, framework, runtime version
|
|
22
|
+
- Architectural constraints: patterns, layering, dependency rules
|
|
23
|
+
- Coding standards: naming conventions, style guidelines
|
|
24
|
+
- Data formats: serialization, encoding, transformation rules
|
|
25
|
+
- Error handling: strategies, retry logic, exception hierarchies
|
|
26
|
+
- Algorithm descriptions: specific approaches when behavior alone is insufficient
|
|
27
|
+
- Performance guidance: memory constraints, streaming requirements, batching strategies
|
|
28
|
+
- Language-specific constructs: generics, annotations, framework-specific types and idioms
|
|
29
|
+
- **Unit-test guidance: framework, structure, mocking conventions, file layout** — unit tests are part of the generated codebase, so requirements that shape them are implementation reqs
|
|
30
|
+
|
|
31
|
+
## What does NOT belong here
|
|
32
|
+
- Behavior and features → `***functional specs***`
|
|
33
|
+
- Concept definitions → `***definitions***`
|
|
34
|
+
- **Conformance-test guidance** → `***test reqs***`
|
|
35
|
+
- **Acceptance-test scenarios** → `***acceptance tests***` nested under the relevant functional spec
|
|
36
|
+
|
|
37
|
+
## Unit tests vs conformance tests (common mistake)
|
|
38
|
+
- Unit-test guidance (`:UnitTests:` framework, structure, mocking) goes **here**, not in `***test reqs***`
|
|
39
|
+
- `***test reqs***` is exclusively for `:ConformanceTests:` — framework, execution command, mocking policy, environment setup
|
|
40
|
+
- Putting unit-test guidance in `***test reqs***` is one of the most common authoring mistakes; the rendered code will silently miss those requirements because the unit-test generator only reads `***implementation reqs***`
|
|
41
|
+
- Do not link the unit testing script here (or anywhere in the specs; a link to it is ignored). turri finds it in the project root - `unittests-script:` in `config.turri.yaml`, else the one `run_unittests*.sh` in the root, else the one in `test_scripts/` - and shows it to the coding agent itself. Describe the framework and run command here in words
|
|
42
|
+
|
|
43
|
+
## Encapsulation warning
|
|
44
|
+
- `requires` modules only pass on functional specs and `exported_concepts` — never implementation reqs
|
|
45
|
+
- If downstream modules need certain behavior to be visible, express it in functional specs, not here
|
|
46
|
+
|
|
47
|
+
## Conciseness
|
|
48
|
+
- State each req in the fewest plain words that stay exact — see `concise-specs.md`
|
|
49
|
+
- Do not restate the standard behavior of a technology this section already names
|
|
50
|
+
|
|
51
|
+
## No duplication
|
|
52
|
+
- Do not duplicate guidance already present in the file or its imports
|
|
53
|
+
- Check imported modules before adding a new req
|
|
54
|
+
|
|
55
|
+
## Concept references
|
|
56
|
+
- Reference defined `:Concepts:` where they add clarity
|
|
57
|
+
- All referenced concepts must already be defined in `***definitions***`
|
|
58
|
+
- Make the component a req constrains its subject; `:Implementation:` is the whole codebase, so use it only for a fact that holds everywhere — language, dependency manager, stack-wide convention
|
|
59
|
+
|
|
60
|
+
## Format
|
|
61
|
+
|
|
62
|
+
```plain
|
|
63
|
+
***implementation reqs***
|
|
64
|
+
- :Implementation: should be in Python 3.12.
|
|
65
|
+
- :Implementation: should use pip for dependency management.
|
|
66
|
+
- :ReportExporter: writes a :CsvFile: with streaming writes, never holding the full dataset in memory.
|
|
67
|
+
- :UnitTests: should use pytest, run with `pytest tests/`.
|
|
68
|
+
```
|