@turri-dev/cli 0.0.0-stage → 0.1.2-next.48dacfc
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 +320 -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 +88 -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 +119 -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,48 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Rules for creating and using import modules in .plain files
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
# Rules for import modules
|
|
6
|
+
|
|
7
|
+
When creating or editing a `.plain` file that uses `import` or is intended to be imported by other modules, always follow these rules:
|
|
8
|
+
|
|
9
|
+
## What an import module is
|
|
10
|
+
- An import module contains **only** `***definitions***`, `***implementation reqs***`, and/or `***test reqs***`
|
|
11
|
+
- It must **not** contain `***functional specs***`
|
|
12
|
+
- It must **not** use `requires` in its frontmatter
|
|
13
|
+
- It may optionally `import` other import modules for layered reuse
|
|
14
|
+
|
|
15
|
+
## What import does
|
|
16
|
+
- `import` pulls in `***definitions***`, `***implementation reqs***`, and `***test reqs***` from the target module
|
|
17
|
+
- It does **not** pull in `***functional specs***`
|
|
18
|
+
- Import paths omit the `.plain` extension
|
|
19
|
+
|
|
20
|
+
## Concept visibility
|
|
21
|
+
- All concepts defined in the imported module become available in the importing module — including concepts the imported module itself imports (layered imports are transitive)
|
|
22
|
+
- Check for concept name collisions between imports and local definitions before adding — a collision is rejected as a syntax error ("Concepts were defined multiple times")
|
|
23
|
+
|
|
24
|
+
## Format
|
|
25
|
+
|
|
26
|
+
```plain
|
|
27
|
+
---
|
|
28
|
+
import:
|
|
29
|
+
- airplain
|
|
30
|
+
description: Module that imports shared definitions and reqs
|
|
31
|
+
---
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
A module can import multiple modules:
|
|
35
|
+
|
|
36
|
+
```plain
|
|
37
|
+
---
|
|
38
|
+
import:
|
|
39
|
+
- airplain
|
|
40
|
+
- shared_utils
|
|
41
|
+
---
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
## Combining import with requires
|
|
45
|
+
- A module can use both `import` and `requires` together
|
|
46
|
+
- `import` brings in shared definitions and reqs
|
|
47
|
+
- `requires` attaches the module to the build chain — accumulated generated code and previous functional specs
|
|
48
|
+
- An import module itself must **not** use `requires`
|
|
@@ -0,0 +1,276 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Rules for authoring the two test scripts that an embedded ***plain integration ships
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
# Rules for embedded-integration test scripts
|
|
6
|
+
|
|
7
|
+
When an embedded integration ships its two `test_scripts/` (unit, conformance), these
|
|
8
|
+
rules apply on top of the contracts in the corresponding
|
|
9
|
+
`implement-*-testing-script` skills. `project-model.md` covers the rendering and
|
|
10
|
+
testing workflow.
|
|
11
|
+
|
|
12
|
+
## Staging model (read this first)
|
|
13
|
+
|
|
14
|
+
A module's code and unit tests live in `plain_modules/<module>/code`; its conformance tests live in one folder, `plain_modules/<module>/tests` — the module's whole conformance suite. turri runs both scripts on the person's machine, once per check: `bash run_unittests_<lang>.sh <build_folder>` decides whether the unit tests pass, and `bash run_conformance_tests_<lang>.sh <build_folder> <conformance_tests_folder>` (absolute paths) runs the whole conformance suite at once and decides whether the conformance tests pass. There is no prepare-environment script, so each script is self-contained.
|
|
15
|
+
|
|
16
|
+
Embedded integrations need a runnable host project, so both scripts stage the generated code **inside the host codebase itself**. The conformance script additionally builds / installs the host and then runs the conformance suite from a scratch folder in the system temp directory (`/tmp/<lang>_conformance/`), because the conformance suite lives in a separate project.
|
|
17
|
+
|
|
18
|
+
| Script | Where it operates | What it does to the host source tree |
|
|
19
|
+
|--------|-------------------|---------------------------------------|
|
|
20
|
+
| `run_unittests_<lang>` | Inside the host codebase | Copies `$1` into the module's package path under the host's source tree, then runs the module's unit tests + lint inside the host |
|
|
21
|
+
| `run_conformance_tests_<lang>` | Inside the host codebase, then `/tmp/<lang>_conformance/` (system temp) | Same copy as above, then cleans and builds / installs the host project; copies `$2` (the conformance-tests folder) into the scratch directory, builds the conformance project, and runs the whole suite there against the host build it just installed |
|
|
22
|
+
|
|
23
|
+
This deliberately writes into the host's `src/main/...` and `src/test/...` (or the language equivalent). Two consequences flow from that:
|
|
24
|
+
|
|
25
|
+
- **The host codebase root must come from a configured env var** (`HOST_CODEBASE_ROOT` is the conventional name; declare it in the integration's configuration concept). Scripts never hardcode it
|
|
26
|
+
- **The destructive-op scoping rule below becomes critical.** `rm -rf` must target only the module's own package path under the host's source tree, never the host's `src/main/`, `target/`, `node_modules/`, or `build/` at the project root
|
|
27
|
+
|
|
28
|
+
This rule covers the **mechanics each script must obey** regardless of language: argument handling, exit codes, idempotency, path resolution, output parsing, and the "what NOT to put here" guard rails. The language-specific install / test commands come from the skills; the contract below is invariant.
|
|
29
|
+
|
|
30
|
+
## What the `.plain` spec must declare
|
|
31
|
+
|
|
32
|
+
The two scripts are generated from facts in the integration's spec. For an embedded integration, those facts cannot be inferred — they have to be **declared explicitly** in the right section, partitioned by which predefined concept they describe:
|
|
33
|
+
|
|
34
|
+
- **Everything about `:UnitTests:`** — paths, approach, packages, framework, conventions, fixtures, mocking policy — lives in `***implementation reqs***`. Unit tests are part of the generated codebase, so requirements that shape them are implementation reqs (see [`impl-reqs.md`](impl-reqs.md))
|
|
35
|
+
- **Everything about `:ConformanceTests:`** — paths, approach, packages, framework, execution command, pass criteria, mocking policy — lives in `***test reqs***`. Conformance tests are external to the generated codebase, so requirements that shape them are test reqs (see [`test-reqs.md`](test-reqs.md))
|
|
36
|
+
|
|
37
|
+
Authors use `add-implementation-requirement` for the first group and `add-test-requirement` for the second. The two groups are parallel — each predefined concept owns a complete authoring story in its own section.
|
|
38
|
+
|
|
39
|
+
### In `***implementation reqs***` — everything about `:UnitTests:`
|
|
40
|
+
|
|
41
|
+
These reqs feed `run_unittests_<lang>`, and the copy step that `run_conformance_tests_<lang>` shares with it. At minimum, declare:
|
|
42
|
+
|
|
43
|
+
1. **Integration source path inside the host** — where `$1/<source>/*` gets copied to. Example: `src/main/java/com/example/integrations/foo`
|
|
44
|
+
2. **`:UnitTests:` source path inside the host** — where `$1/<tests>/*` gets copied to, and where the test runner discovers unit tests. Example: `src/test/java/com/example/integrations/foo`
|
|
45
|
+
3. **Fully qualified `:UnitTests:` package** — the package the test runner uses to scope discovery via its filter argument. Example: `com.example.integrations.foo`
|
|
46
|
+
4. **`:UnitTests:` framework and conventions** — JUnit / pytest / Jest / Go's `testing` / etc., plus naming conventions (`*Test`, `test_*`, `*.test.ts`, …), fixture / mock / assertion style, file layout inside the test package
|
|
47
|
+
5. **Quality gates that run alongside `:UnitTests:`** — Checkstyle / ESLint / Pylint / Ruff / `go vet` / `cargo clippy` — whatever the host project requires
|
|
48
|
+
|
|
49
|
+
Author the location facts as one tight group, phrased in terms of `:UnitTests:`:
|
|
50
|
+
|
|
51
|
+
```plain
|
|
52
|
+
- :UnitTests: of :Implementation: live in `src/test/java/com/example/integrations/foo` inside the host codebase.
|
|
53
|
+
- The corresponding integration source code lives in `src/main/java/com/example/integrations/foo`.
|
|
54
|
+
- The fully qualified package used for :UnitTests: discovery is `com.example.integrations.foo`.
|
|
55
|
+
- :UnitTests: use JUnit 5 with the host's Checkstyle profile applied via `mvn checkstyle:check`.
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
These facts feed directly into the unit-test script body, and into the staging step of the conformance script:
|
|
59
|
+
|
|
60
|
+
```bash
|
|
61
|
+
# clean existing code from the host
|
|
62
|
+
rm -rf $MAIN_PROJECT_FOLDER/<integration source path>/*
|
|
63
|
+
rm -rf $MAIN_PROJECT_FOLDER/<:UnitTests: source path>/*
|
|
64
|
+
|
|
65
|
+
# create destinations and copy generated code into the host
|
|
66
|
+
mkdir -p $MAIN_PROJECT_FOLDER/<integration source path>
|
|
67
|
+
mkdir -p $MAIN_PROJECT_FOLDER/<:UnitTests: source path>
|
|
68
|
+
cp -R $1/<integration source path>/* $MAIN_PROJECT_FOLDER/<integration source path>
|
|
69
|
+
cp -R $1/<:UnitTests: source path>/* $MAIN_PROJECT_FOLDER/<:UnitTests: source path>
|
|
70
|
+
|
|
71
|
+
# run :UnitTests: scoped to the integration's package
|
|
72
|
+
mvn test -Dtest='<:UnitTests: package>.**.*Test' checkstyle:check
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
### In `***test reqs***` — everything about `:ConformanceTests:`
|
|
76
|
+
|
|
77
|
+
These reqs feed `run_conformance_tests_<lang>`. At minimum, declare:
|
|
78
|
+
|
|
79
|
+
1. **`:ConformanceTests:` source location** — the generated suite lives in one folder, `plain_modules/<module>/tests/`, which holds the module's whole conformance suite; turri passes its absolute path as `$2`
|
|
80
|
+
2. **`:ConformanceTests:` framework and execution command** — `mvn test --no-transfer-progress`, `pytest`, `npm test`, `go test ./...`, etc., with any flags / profiles the project requires
|
|
81
|
+
3. **Fully qualified `:ConformanceTests:` package** (or path / pattern) used to scope discovery, if the runner needs one
|
|
82
|
+
4. **`:ConformanceTests:` network and secrets policy** — by default the suite runs against the **live provider** (see [`integrations.md`](integrations.md) → *`:ConformanceTests:` always run against the live integration*). Declare the env-var names the script reads (e.g. `<PROVIDER>_API_KEY`), whether the script loads a `.env` file before running, and any specific endpoints that are mocked because they can't be exercised live safely (429, forced 5xx)
|
|
83
|
+
5. **`:ConformanceTests:` pass criteria** — the strict criteria from [*Pass criteria (strict)*](#pass-criteria-strict): at least one test ran AND zero failures / errors / skipped. Reaffirm this in the spec so the renderer knows the runner must parse the test tool's stdout
|
|
84
|
+
6. **`:ConformanceTests:` build / install needs** — anything the conformance project needs before `mvn test` (or equivalent) will work: the host build / install command the script runs first, dependencies, fixtures, schema files, generated stubs
|
|
85
|
+
|
|
86
|
+
Author the conformance facts as one or more entries, phrased in terms of `:ConformanceTests:`:
|
|
87
|
+
|
|
88
|
+
```plain
|
|
89
|
+
- :ConformanceTests: of :Implementation: live in `plain_modules/foo/tests/` and are implemented with JUnit 5 + Maven.
|
|
90
|
+
- The fully qualified package used for :ConformanceTests: discovery is `com.example.integrations.foo.conformance`.
|
|
91
|
+
- :ConformanceTests: are run via `mvn test --no-transfer-progress`; the host's Surefire plugin must be installed.
|
|
92
|
+
- :ConformanceTests: run against the live :ProviderName: sandbox — no mocking of provider calls.
|
|
93
|
+
- The conformance script reads `<PROVIDER>_API_KEY` (and any additional secrets) from the shell or from a `.env` file at the project root and fails fast (exit `69`) if any required var is missing after the optional `.env` load.
|
|
94
|
+
- The 429 (rate-limit) and forced-5xx paths use a local mock for that specific endpoint; every other path is live.
|
|
95
|
+
- :ConformanceTests: pass only when the Surefire summary line shows at least one test ran with zero failures, zero errors, and zero skipped.
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
These facts feed directly into the conformance script body:
|
|
99
|
+
|
|
100
|
+
```bash
|
|
101
|
+
# stage $1 into the host (same copy as run_unittests), then build / install the host
|
|
102
|
+
rm -rf ./target
|
|
103
|
+
mvn clean install -DskipTests
|
|
104
|
+
|
|
105
|
+
# stage :ConformanceTests: source into the scratch directory
|
|
106
|
+
find "$DIR" -mindepth 1 -exec rm -rf {} +
|
|
107
|
+
cp -R $2/* $DIR
|
|
108
|
+
cd $DIR
|
|
109
|
+
|
|
110
|
+
# build the conformance project, then run :ConformanceTests:
|
|
111
|
+
mvn clean install -DskipTests
|
|
112
|
+
output=$(mvn test --no-transfer-progress 2>&1)
|
|
113
|
+
|
|
114
|
+
# parse Surefire summary against the declared pass criteria, then exit accordingly
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
### Rules common to both sections
|
|
118
|
+
|
|
119
|
+
- **The paths must agree across reqs.** The `:UnitTests:` source path, the `:UnitTests:` package, and the integration source path describe the same module from three angles. Same for the `:ConformanceTests:` location and its package. A mismatch (e.g. test path `src/test/java/com/example/foo` but test package `com.example.bar`) silently produces a green build with stale or zero tests
|
|
120
|
+
- **Paths are host-relative**, not absolute. `MAIN_PROJECT_FOLDER` comes from `HOST_CODEBASE_ROOT` (declared in the configuration concept); the paths above join onto it
|
|
121
|
+
- **The renderer's output folder `$1` mirrors the same layout.** `$1/<integration source path>/*` exists because the renderer emits the generated code into the same package directories it'll be copied into — the `cp -R` is a straight overlay, not a path translation
|
|
122
|
+
- **Each fact lives in one place.** If two `***implementation reqs***` entries declare slightly different `:UnitTests:` paths (or two `***test reqs***` entries declare slightly different `:ConformanceTests:` packages), the renderer can't tell which to use. Author each group as a tight cluster (one bullet with sub-bullets) so a future reviewer sees them together
|
|
123
|
+
- **Never duplicate a fact across sections.** `:UnitTests:` facts belong **only** in `***implementation reqs***`; `:ConformanceTests:` facts belong **only** in `***test reqs***`. The two groups never overlap — the script generators read each from its own section
|
|
124
|
+
- **For non-Java languages**, the facts have language-specific equivalents — Python: `src/foo/`, `tests/foo/`, `tests.foo`; Node: `src/foo/`, `test/foo/`, `test/foo/**/*.test.ts`. The `implement-*-testing-script` skills know the mapping per language, but the spec still has to declare the host-relative paths and packages so the skill knows what to put in the script bodies
|
|
125
|
+
|
|
126
|
+
## Common contract (both scripts)
|
|
127
|
+
|
|
128
|
+
Both scripts are invoked by turri with positional arguments, on the person's machine. They MUST:
|
|
129
|
+
|
|
130
|
+
1. **Be POSIX-bash (`.sh`) on macOS / Linux, PowerShell (`.ps1`) on Windows.** Executable, idempotent — re-running with the same inputs produces the same result
|
|
131
|
+
2. **Validate every positional argument up front.** Exit with a clear `Usage:` line on bad args. Conventional exit codes (consistent with the shared testing-script rules):
|
|
132
|
+
- `1` — bad arguments / usage error (and "no tests discovered" for the conformance runner)
|
|
133
|
+
- `2` — missing or inaccessible input folder
|
|
134
|
+
- `69` — unrecoverable environment failure (missing toolchain, cannot enter working folder, install failed)
|
|
135
|
+
- Any other non-zero code — propagated verbatim from the underlying build / test tool
|
|
136
|
+
|
|
137
|
+
turri passes `$1` (and `$2` for the conformance runner) as **absolute paths** (older renderer versions passed relative ones). Never build a derived path by concatenating a raw argument (`.tmp/$1`, `<lang>_$1`, `$current_dir/$2`) — with an absolute argument that produces a doubled path (`/project//abs/path/...`). Use the argument as-is where a path is needed, take its `basename` when naming a scratch folder, and resolve `$2` to absolute (if it isn't already) before any `cd`
|
|
138
|
+
3. **Echo the toolchain home at the top** — the first thing a developer checks when CI breaks. For Java: `JAVA_HOME`. For Python: the active interpreter (`python --version` / `which python`). For Node: `node --version`. For Go: `go env GOROOT`. Pick the variable whose value most often explains "why does it work locally but not on the CI runner?"
|
|
139
|
+
4. **Respect `VERBOSE=1`.** Gate chatty diagnostic prints behind `if [ "${VERBOSE:-}" = "1" ]` (and the PowerShell equivalent). Errors and key step markers print unconditionally
|
|
140
|
+
5. **Resolve paths relative to the script, not `$PWD`.** Use `"$(cd "$(dirname "$0")/<relative-path-to-anchor>" && pwd)"`. Hard-coded `../../` chains break the moment the renderer's `cwd` changes
|
|
141
|
+
6. **Create destination directories before copying.** `mkdir -p` before any `cp -R` / `rsync` / `robocopy` — most copy commands do not create intermediate directories and fail silently in some shells
|
|
142
|
+
7. **Scope destructive operations narrowly.** Any `rm -rf` (or `Remove-Item -Recurse -Force`) targets only the module's own package path inside the working folder — never the host's `src/`, `target/`, `node_modules/`, `build/`, or `dist/` at the project root. The conformance script cleans the host's build-output directory (e.g. `./target`) only as part of its own build / install step, and removes its system-temp scratch folder on exit
|
|
143
|
+
8. **Print where you are before you `cd`.** `echo "Moving to: $DIR"` saves an hour of debugging when paths are wrong. PowerShell: `Write-Host "Moving to: $DIR"`
|
|
144
|
+
|
|
145
|
+
## 1. `run_unittests_<lang>` — copy into the host, then run unit tests there
|
|
146
|
+
|
|
147
|
+
Receives one positional argument: the renderer's build output folder (`plain_modules/<module>/code/`, absolute).
|
|
148
|
+
|
|
149
|
+
Purpose: stage the generated code **into the host codebase at the module's package path** and run the module's unit tests + language-appropriate quality gates against the host project from inside the host root.
|
|
150
|
+
|
|
151
|
+
Required steps:
|
|
152
|
+
|
|
153
|
+
1. Validate `$1`
|
|
154
|
+
2. Resolve the host codebase root from the configured env var
|
|
155
|
+
3. `rm -rf` the module's main + test package directories under the host's source tree — both, because stale tests in the target package cause confusing failures
|
|
156
|
+
4. `mkdir -p` the destination directories and copy `$1/src/...` (or the language-equivalent layout) into the host's source tree at the module's package path
|
|
157
|
+
5. `cd` into the host codebase root, verify with an explicit exit-code check (`if [ $? -ne 0 ]` / `if (! $?) { ... }`)
|
|
158
|
+
6. Run the test command **scoped to the module's package only**:
|
|
159
|
+
- Maven: `mvn test -Dtest='<fully.qualified.package>.**.*Test' <lint-check>:check`
|
|
160
|
+
- pytest: `pytest tests/<module>/` (or whatever path matches the module)
|
|
161
|
+
- Jest: `npm test -- --testPathPattern '<module>/'`
|
|
162
|
+
- Go: `go test ./<module>/...`
|
|
163
|
+
7. Always include the host's lint / static-analysis gate in the same invocation when the build system has one (Checkstyle, ESLint, Pylint / Ruff, `go vet`, `cargo clippy`, …). Running tests without the lint gate produces a false-green when style violations would otherwise block the build
|
|
164
|
+
|
|
165
|
+
Hard rules:
|
|
166
|
+
|
|
167
|
+
- **Self-contained.** The script stages `$1` itself on every run; it never relies on another script having run first
|
|
168
|
+
- **Never run the full test suite** when scoping is possible. A per-module script that runs the entire host's tests wastes minutes on every render iteration
|
|
169
|
+
|
|
170
|
+
## 2. `run_conformance_tests_<lang>` — copy into the host, build it, then run the external suite
|
|
171
|
+
|
|
172
|
+
Receives two positional arguments: the renderer's build output folder (`$1`, `plain_modules/<module>/code/`) and the conformance-tests folder (`$2`, `plain_modules/<module>/tests/` — the module's whole conformance suite), both absolute.
|
|
173
|
+
|
|
174
|
+
Purpose: stage the generated code into the host, build / install the host, and run the whole conformance suite in `$2` against that build. The conformance suite is a separate project that consumes the integration as a dependency from a local cache (`~/.m2`, `node_modules`, the language equivalent), so the script installs the host itself first — otherwise the suite links against whatever was last built, which may not be the renderer's most recent output. turri runs it once per check, and its exit code decides whether the conformance tests pass.
|
|
175
|
+
|
|
176
|
+
Required steps:
|
|
177
|
+
|
|
178
|
+
1. Validate `$1` and `$2`. Resolve `$2` to an absolute path if it isn't one already (newer renderers pass it absolute; older ones passed it relative to the invocation directory)
|
|
179
|
+
2. Resolve the host codebase root from the configured env var, `rm -rf` the module's main + test package directories under the host's source tree, `mkdir -p` the destination directories, and copy `$1/src/...` into the host's source tree at the module's package path — **the same staging as `run_unittests`**
|
|
180
|
+
3. `cd` into the host codebase root, verify with an explicit exit-code check
|
|
181
|
+
4. Clean the host's build-output directory before compiling — stale class / object files from a previous build cause confusing link-time mismatches when the contract has changed. Maven: `rm -rf ./target`. Cargo: `cargo clean -p <pkg>`. Go: `go clean -cache` (usually unnecessary, but mention if applicable). For npm projects with incremental builds disabled, wipe the relevant `dist/` / `build/` folder
|
|
182
|
+
5. Run the host's standard install / build command so the conformance project can resolve the integration — **skip the fat-artifact and skip tests** when the build system supports it, because the tests run afterwards. Exit `69` if it fails:
|
|
183
|
+
- Maven: `mvn clean install -DskipTests` (plus any host-specific repackage-skip flag)
|
|
184
|
+
- npm / pnpm / yarn: `npm ci` (no test script; dependency install only)
|
|
185
|
+
- Python: `pip install -e .` (then any extra integration dependencies)
|
|
186
|
+
- Go: `go mod tidy && go build ./...`
|
|
187
|
+
- Cargo: `cargo fetch && cargo build --tests --no-run`
|
|
188
|
+
6. Stage `$2/*` into a scratch directory in the system temp directory (`/tmp/<lang>_conformance/` in Bash, the `GetTempPath()` equivalent in PowerShell). Wipe it first if it exists, and remove it again on exit (`trap 'rm -rf "$DIR"' EXIT` / `finally`). Use a deletion form that handles hidden files and odd shells safely:
|
|
189
|
+
- Bash: `find "$DIR" -mindepth 1 -exec rm -rf {} +` (safer than `rm -rf "$DIR"/*`)
|
|
190
|
+
- PowerShell: `Remove-Item -Recurse -Force "$DIR\*"` after confirming the path exists
|
|
191
|
+
7. `cd` into the scratch directory
|
|
192
|
+
8. Compile / set up the conformance project — exit early if this fails:
|
|
193
|
+
- Maven: `mvn clean install -DskipTests`
|
|
194
|
+
- npm: `npm ci`
|
|
195
|
+
- Python: `pip install -r requirements.txt`
|
|
196
|
+
- Go / Cargo: standard build
|
|
197
|
+
9. Run the whole conformance suite and **capture stdout to a variable**:
|
|
198
|
+
- Bash: `output=$(mvn test --no-transfer-progress 2>&1)` (or the language equivalent)
|
|
199
|
+
- PowerShell: `$output = & <cmd> 2>&1 | Out-String`
|
|
200
|
+
10. **Parse the captured output strictly** (see *Pass criteria* below) — don't trust the test tool's exit code alone
|
|
201
|
+
11. On the failure path: print the captured output unconditionally so the renderer (and the user) can see what failed
|
|
202
|
+
12. On the success path: print the output only if `VERBOSE=1`
|
|
203
|
+
|
|
204
|
+
### Secrets and `.env` handling
|
|
205
|
+
|
|
206
|
+
`:ConformanceTests:` run against the live provider (per [`integrations.md`](integrations.md) → *`:ConformanceTests:` always run against the live integration*), so the conformance script needs the user's credentials at runtime.
|
|
207
|
+
|
|
208
|
+
- **Credentials are read from environment variables** named in `***test reqs***` and the auth concept. Never from a literal, never from a file checked into the repo
|
|
209
|
+
- **The script may optionally load a `.env` file** before running the suite. Typical pattern in Bash:
|
|
210
|
+
|
|
211
|
+
```bash
|
|
212
|
+
ENV_FILE="${ENV_FILE:-$MAIN_PROJECT_FOLDER/.env}"
|
|
213
|
+
if [ -f "$ENV_FILE" ]; then
|
|
214
|
+
echo "Loading env from $ENV_FILE"
|
|
215
|
+
set -a; . "$ENV_FILE"; set +a
|
|
216
|
+
fi
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
PowerShell uses the equivalent `Get-Content` + `Set-Item Env:` loop. Absence of `.env` is **not** an error — CI provides the same vars directly through the shell
|
|
220
|
+
- **Verify required env vars exist after the optional `.env` load** and fail fast if any are missing:
|
|
221
|
+
|
|
222
|
+
```bash
|
|
223
|
+
for var in PROVIDER_API_KEY PROVIDER_ACCOUNT_ID; do
|
|
224
|
+
if [ -z "${!var:-}" ]; then
|
|
225
|
+
printf "Error: %s is required for :ConformanceTests:\n" "$var" >&2
|
|
226
|
+
exit 69
|
|
227
|
+
fi
|
|
228
|
+
done
|
|
229
|
+
```
|
|
230
|
+
|
|
231
|
+
- **Export the resolved vars** (already in scope thanks to `set -a`) so child processes started by the test runner inherit them — Maven, pytest, npm, Go all read env vars from their parent process
|
|
232
|
+
- **Never log credential values.** Echo the env-var **name** when reporting "loaded", not its value. Redact in any error path that dumps captured output
|
|
233
|
+
- **Document the secret names twice** — once in the auth concept (for the runtime), once in `***test reqs***` for `:ConformanceTests:` (for the script). They must be the same names; a divergence means the script reads different credentials than the runtime does, and conformance silently tests the wrong account
|
|
234
|
+
|
|
235
|
+
### Pass criteria (strict)
|
|
236
|
+
|
|
237
|
+
These are the **only** valid pass criteria. Anything outside this list is a failure:
|
|
238
|
+
|
|
239
|
+
- At least one test ran AND zero failures AND zero errors AND zero skipped. Maven example: `Tests run: [1-9][0-9]*, Failures: 0, Errors: 0, Skipped: 0`. Each language's test runner has an equivalent summary line — match it with a regex, not by substring
|
|
240
|
+
- Exit with the test tool's exit code on the success path (almost always `0`)
|
|
241
|
+
|
|
242
|
+
Failure cases — each must be detected explicitly:
|
|
243
|
+
|
|
244
|
+
- `Tests run: 0` (or the language equivalent) → **failure**. The renderer must never be told "green" when nothing ran. Exit `1` per the shared testing-script rules (the "no tests discovered" exit code)
|
|
245
|
+
- Any non-zero `Failures:`, `Errors:`, or `Skipped:` count → **failure**. Dump the captured output and propagate the test tool's exit code
|
|
246
|
+
- The test tool exited non-zero but the summary line shows `Tests run: 0` → still a `1` (no-tests-discovered), not the tool's own exit code
|
|
247
|
+
|
|
248
|
+
**Why parse stdout instead of trusting the test tool's exit code.** Every major test runner returns `0` when zero test classes match a filter. Maven Surefire, pytest with no collected tests, `go test ./pkg/that/has/no/_test.go/files`, Jest with an empty pattern — all green-by-default. The renderer's contract is "tests must run AND must pass" — the exit code alone cannot express that.
|
|
249
|
+
|
|
250
|
+
## Cross-cutting rules
|
|
251
|
+
|
|
252
|
+
- **No secrets in scripts.** Read credentials from env vars the renderer (or the user's shell) already sets. If a required env var is missing, fail fast with a clear `Error: $FOO is required for ...` and exit `69`. Never hardcode an API key, never read from a checked-in file
|
|
253
|
+
- **Mirror the package / module path exactly.** A typo in the package segment (e.g. one wrong directory in `com/example/foo/...` or `host_pkg/foo/bar/...`) makes `rm -rf` silently do nothing and the subsequent copy put files in the wrong place. Both produce a **green** build with stale code. Derive the package path from the `host-codebase` concept; never retype it inline
|
|
254
|
+
- **Don't add cleverness to the script that belongs in the build.** If you're tempted to `grep` the test output for more than the pass/fail summary, change the test or the test-runner config instead. The script is a thin contract — keep it that way
|
|
255
|
+
- **Don't use shortcut profiles that skip quality gates.** Maven's `-Pfast`, Gradle's `-x check`, npm's `--ignore-scripts`, pytest's `--no-cov`, `-DskipTests` anywhere except the conformance script's two build / install steps (the host install and the conformance project's compile, where the tests run right after) — all defeat the purpose of these scripts. The build's full quality gate (tests + lint + coverage thresholds) must run for unit and conformance
|
|
256
|
+
- **Failure messages are for humans.** `printf "Error: <what failed> at <where>\n"` then exit with the right code. No silent failures, no `|| true` to paper over errors, no `2>/dev/null` on commands whose stderr matters
|
|
257
|
+
- **Use existing scripts as templates.** When you add a new embedded integration, copy from a known-good module, then search-and-replace only the package path and the test-filter argument. Resist the urge to "improve" structure mid-port — pattern-matching across many modules is a feature, not a bug
|
|
258
|
+
|
|
259
|
+
## Reference implementation (Java + Maven)
|
|
260
|
+
|
|
261
|
+
A working two-script set for a Java / Maven embedded integration lives under [`examples/integration-embedded-testing/`](examples/integration-embedded-testing/) next to this file:
|
|
262
|
+
|
|
263
|
+
- [`run_unittests_java.sh`](examples/integration-embedded-testing/run_unittests_java.sh) — stages `$1` into the host and runs `mvn test -Dtest='<module-pkg>.**.*Test' checkstyle:check`
|
|
264
|
+
- [`run_conformance_tests_java.sh`](examples/integration-embedded-testing/run_conformance_tests_java.sh) — stages `$1` into the host the same way, cleans `target/`, runs `mvn clean install -DskipTests` in the host, then copies `$2` into `/tmp/java_conformance/` (system temp, removed again on exit), builds the conformance project, runs the suite, and parses the Surefire summary line per the strict pass criteria above
|
|
265
|
+
|
|
266
|
+
Use them as a template when adding a new embedded integration in Java / Maven — copy, search-and-replace only the package segment and the `-Dtest` filter, then put them in the project's `test_scripts/`. For other languages (Python, Node.js, Go, Rust, …), the `implement-*-testing-script` skills generate the equivalent two-script set following the same contract.
|
|
267
|
+
|
|
268
|
+
## Adding a script for a new module
|
|
269
|
+
|
|
270
|
+
1. Copy the two scripts from a working embedded-integration module into the project's `test_scripts/`
|
|
271
|
+
2. Search-and-replace the package segment everywhere it appears — including the test-filter argument (the most commonly missed spot)
|
|
272
|
+
3. `chmod +x` both (and verify ACLs on Windows for the `.ps1` siblings)
|
|
273
|
+
4. Do not link them from the specs: turri finds them in the project root - `config.turri.yaml` (`unittests-script:`, `conformance-tests-script:`), else the one of each kind in the root, else in `test_scripts/` - and shows them to the agents itself. If `test_scripts/` ends up with two scripts of one kind, name the one to use in `config.turri.yaml`.
|
|
274
|
+
5. Smoke-test by running each script manually with the renderer's output folder as `$1` (and the conformance-tests folder as `$2` for the conformance runner). Confirm exit codes match what the renderer expects
|
|
275
|
+
|
|
276
|
+
For non-Java languages, the skills [`implement-unit-testing-script`](../skills/implement-unit-testing-script/SKILL.md), and [`implement-conformance-testing-script`](../skills/implement-conformance-testing-script/SKILL.md) adapt the contract above to Python, Node.js, Go, Rust, Flutter, and others. Always route script creation through these skills so the cross-cutting rules above are applied uniformly.
|
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Rules for authoring ***plain specs for REST API integrations embedded into an existing host codebase
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
# Rules for **embedded** integration specs
|
|
6
|
+
|
|
7
|
+
When an integration `.plain` module is **embedded** — meaning the generated code in `plain_modules/` is consumed in-process by an existing host codebase as a library / module — these rules apply on top of the shared rules in [`integrations.md`](integrations.md). If anything below contradicts a guess made from memory, the rules here win.
|
|
8
|
+
|
|
9
|
+
Embedded means: the host codebase already exists, has its own language / framework / dependency manager / packaging layout, and the integration must conform to all of that without negotiation.
|
|
10
|
+
|
|
11
|
+
> **For test-script authoring**, also follow [`integration-embedded-testing.md`](integration-embedded-testing.md). It defines the per-script contract (`run_unittests_<lang>`, `run_conformance_tests_<lang>`) — staging into the host and the system temp directory, arg validation, exit codes, output parsing, the `***implementation reqs***` and `***test reqs***` entries the spec must declare so the scripts can be generated. This file (`integration-embedded.md`) only summarizes the test-script wiring; the testing rule is the source of truth.
|
|
12
|
+
|
|
13
|
+
## The host codebase dictates the tech stack (hard rule)
|
|
14
|
+
|
|
15
|
+
- Language, framework, dependency manager, packaging layout, coding standards, error model, logging library, and architecture are **inherited** from the host — they are **never chosen** by the integration spec
|
|
16
|
+
- Do not re-ask the user about any of these in any phase — they are facts to be discovered from the host's manifest files (`pyproject.toml`, `package.json`, `go.mod`, `Cargo.toml`, `pom.xml`, …) and other integrations.
|
|
17
|
+
- If a `brood` stack-phase question seems to push back on a host rule, treat the host as ground truth and rewrite the question
|
|
18
|
+
- Implementation reqs added in the stack phase are **transcribed** from the host stack verbatim — host language and exact version, host framework + version, dependency manager and manifest path, packaging layout, host conventions the contract must follow, and every host-package version the contract pins
|
|
19
|
+
|
|
20
|
+
## Project layout — the integration lives under `plain/` at the host root
|
|
21
|
+
|
|
22
|
+
The embedded integration's `.plain` specs (and the rest of the ***plain project — `template/`, `resources/`, `test_scripts/`, generated `plain_modules/`) live in a single top-level `plain/` folder at the host repository root, alongside the host's own source tree:
|
|
23
|
+
|
|
24
|
+
```
|
|
25
|
+
<host repo>/
|
|
26
|
+
├── docs/
|
|
27
|
+
├── plain/ # ← the embedded ***plain integration lives here
|
|
28
|
+
│ ├── <module>.plain
|
|
29
|
+
│ ├── template/
|
|
30
|
+
│ ├── resources/
|
|
31
|
+
│ ├── test_scripts/
|
|
32
|
+
│ ├── plain_modules/ # generated; gitignored
|
|
33
|
+
│ │ └── <module>/
|
|
34
|
+
│ │ ├── code/ # implementation + unit tests
|
|
35
|
+
│ │ └── tests/ # conformance tests: the module's whole suite
|
|
36
|
+
└── src/ # host source tree (Java, Python, etc.)
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
This keeps the integration self-contained in one directory the user can `cd` into to render, while leaving the host's own source layout untouched.
|
|
40
|
+
|
|
41
|
+
- If the host **already** has a `plain/` directory, adopt it verbatim — see step 1 of *Discover before you ask* below
|
|
42
|
+
- If the host does **not** yet have one, create `plain/` at the repo root; do not invent a different name (`specs/`, `plain_specs/`, …)
|
|
43
|
+
- The two test scripts still `cd` into `$HOST_CODEBASE_ROOT` (the host repo root) to compile and run tests against the host project; the `plain/` folder is the integration's **authoring** root, not its **runtime** root
|
|
44
|
+
|
|
45
|
+
## Discover before you ask
|
|
46
|
+
|
|
47
|
+
Run host discovery **before** the first interview question. Treat the results as ground truth for everything that follows.
|
|
48
|
+
|
|
49
|
+
1. **Locate the host's `.plain` setup (if any).** Look for an existing `.plain` file or directory, a `plain_modules/`, a `resources/`, and a `test_scripts/`. If the host already has a `.plain` setup, **adopt it verbatim**: the new integration module lands inside the existing `plain_source_dir` and `requires` the relevant base module (use `create-requires-module`). Do not create a parallel layout.
|
|
50
|
+
2. **Read every existing integration in the host.** For each one, extract: intent and scope, host base class / interface / protocol the integration subclasses or implements, package path and naming convention, configuration pattern (env vars, settings module, secret manager), error / exception hierarchy, logging / metrics / tracing conventions, testing pattern (live vs recorded vs mocked, fixture location, conformance-test layout). The new integration must follow the same patterns unless the user explicitly opts out.
|
|
51
|
+
3. **Capture findings in the `host-codebase` concept.** Cite the existing integration by file path so the reasoning is auditable. The concept holds, as facts:
|
|
52
|
+
- Host codebase root (absolute or project-relative path)
|
|
53
|
+
- Host language and exact version (from the manifest file)
|
|
54
|
+
- Dependency manager and manifest file path (pip + `requirements.txt`, Poetry, uv, npm, pnpm, yarn, Go modules, Cargo, Maven, Gradle, …)
|
|
55
|
+
- Package / module path inside the host where the integration will be consumed
|
|
56
|
+
- Fully qualified import path of any host class / interface / struct / protocol the integration must conform to (e.g. `host_project.integrations.base.IntegrationContract`)
|
|
57
|
+
- Host conventions (custom base classes, Pydantic major version, sync vs. async style, exception hierarchy, dependency-injection seams, logging library)
|
|
58
|
+
- Target generated-class fully qualified name under `plain_modules/` (e.g. `plain_modules.integrations.<provider>.Client`) and the host base class it should subclass
|
|
59
|
+
4. **Only ask the user what the codebase cannot tell you.** The user's time goes into the third-party API itself (provider, docs, endpoints, edge cases, webhooks) and authentication / credentials. Everything else is a deduction. If a deduction is ambiguous (two existing integrations subclass two different base classes), surface the ambiguity with a single `ask_user` question that quotes both code locations — the question is about resolving a conflict the host already contains, not about asking the user to design the integration.
|
|
60
|
+
|
|
61
|
+
## Reference host symbols by fully qualified import path
|
|
62
|
+
|
|
63
|
+
- Every host class, interface, struct, exception, or type alias that appears in a spec must be written with its full dotted / slashed import path (e.g. `host_project.integrations.base.IntegrationContract`, `@host/integrations#Contract`) and tagged in the spec text as **"imported from the host codebase; do not redefine"**
|
|
64
|
+
- The renderer is allowed to redefine **only** symbols the host does not provide and the contract schema does not capture
|
|
65
|
+
- Naming the symbol by FQN is not optional decoration — it tells the renderer where the type comes from, which prevents a duplicate definition under `plain_modules/`
|
|
66
|
+
|
|
67
|
+
## Link host files at their original path — never copy them into `resources/`
|
|
68
|
+
|
|
69
|
+
The integration's `.plain` module lives **inside the host codebase** (per the "adopt the host's `.plain` setup verbatim" step). That means host source files are already reachable as linked resources via their host-relative paths. The integration spec references them **in place**; it never duplicates them under `resources/host/`.
|
|
70
|
+
|
|
71
|
+
- Every host file the integration touches (base classes, configuration modules, registries, exception classes, lifecycle hooks) is referenced from the relevant spec using `***linked resource***` syntax with the **path as it exists in the host codebase** — e.g. `[base.py](host_project/integrations/base.py)` if the `.plain` module sits next to `host_project/`
|
|
72
|
+
- **Do NOT copy host files into `resources/host/`.** A copy creates a second source of truth that drifts the moment the host file is edited; the rendered code will then disagree with whatever the host actually ships
|
|
73
|
+
- **Do NOT add host files via the `add-resource` skill's default copy behavior** when that behavior would duplicate the file — point at the existing host path directly
|
|
74
|
+
- **Never inline a host file's contents** into a spec
|
|
75
|
+
- **Never describe a host symbol's shape from memory** — the renderer reads the linked file's bytes at its host path and that is the source of truth
|
|
76
|
+
- This still obeys the broader [`linked-resources.md`](linked-resources.md) rules: a directory is not a valid link, a URL is not a valid link, a binary is not a valid link. Only the *location* changes — host files live where the host put them, not under `resources/`
|
|
77
|
+
|
|
78
|
+
### What still belongs under `resources/`
|
|
79
|
+
|
|
80
|
+
This rule applies to **host source code only**. Other artifacts still live under `resources/` exactly like in a non-embedded project:
|
|
81
|
+
|
|
82
|
+
- **Contract schemas authored by the integration** — `resources/contract/<entry-point>.schema.json`
|
|
83
|
+
- **Configuration schema** — `resources/config.schema.json`
|
|
84
|
+
- **Static lookup tables** the integration owns — `resources/error-map.yaml`, `resources/retry-policy.yaml`, etc.
|
|
85
|
+
|
|
86
|
+
The rule of thumb: if the host wrote it and ships it, link it where the host put it. If the integration is authoring it for the first time, it goes under `resources/`.
|
|
87
|
+
|
|
88
|
+
## The contract spec declares inheritance, not duplication
|
|
89
|
+
|
|
90
|
+
- The entry-point class / interface / struct in the contract spec must `subclass` / `implements` / `embeds` the host symbol by its full import path
|
|
91
|
+
- The spec describes only the **integration-specific additions and overrides** — never restates the parent's fields or methods
|
|
92
|
+
- The additions and overrides are themselves expressed in the linked schema under `resources/contract/` (JSON Schema or OpenAPI), with `allOf` / `$ref` extending the host's schema rather than duplicating fields
|
|
93
|
+
- If a host base class adds fields the integration shouldn't redeclare, the contract schema's `allOf` chain captures that explicitly
|
|
94
|
+
|
|
95
|
+
## Renderer directives go in the spec, shapes go in the schema
|
|
96
|
+
|
|
97
|
+
Each contract spec carries the language-specific glue that the schema can't express:
|
|
98
|
+
|
|
99
|
+
- Target generated-class fully qualified name (e.g. `plain_modules.integrations.<provider>.Client`)
|
|
100
|
+
- Target file path under `plain_modules/`
|
|
101
|
+
- Host base class import path to subclass / implement
|
|
102
|
+
- Host-package version pins (e.g. `pydantic ~= 2.5`, `fastapi ^0.110`)
|
|
103
|
+
- Framework-specific decorators or metaclasses (`model_config`, `@Depends`, …)
|
|
104
|
+
|
|
105
|
+
The renderer reads the directives from the spec and the shapes from the linked schema, then emits the host-language class. The spec must **not** also contain a class body or a field list — that creates two sources of truth and they will drift.
|
|
106
|
+
|
|
107
|
+
## Single source of truth for the host root
|
|
108
|
+
|
|
109
|
+
- The `host-codebase` concept holds the host root path as a **fact**
|
|
110
|
+
- Test scripts, configuration-loading specs, and any other spec that needs the host location reads it from **that one fact** (via the env var declared in the configuration concept)
|
|
111
|
+
- Never hardcode the host path in any spec, script, or runtime config
|
|
112
|
+
|
|
113
|
+
## No host-overlapping reqs
|
|
114
|
+
|
|
115
|
+
- Implementation reqs added in any phase must not contradict the host codebase — same language, same dependency manager, same packaging layout, same error hierarchy, same logging library
|
|
116
|
+
- If two reqs are in tension (one from the host, one newly authored), the host wins; rewrite or drop the newly authored req
|
|
117
|
+
- Do not author a req that re-declares something the host already enforces — that's a maintenance burden with no benefit
|
|
118
|
+
|
|
119
|
+
## Test-script wiring — copy into the host, run tests there
|
|
120
|
+
|
|
121
|
+
Embedded integrations are tested **inside the host codebase itself**. A module's code and unit tests live in `plain_modules/<module>/code`; its conformance tests live in one folder, `plain_modules/<module>/tests` — the module's whole conformance suite. turri runs two self-contained scripts on the person's machine, once per check: `run_unittests_<lang>.sh <build_folder>` decides the unit tests, and `run_conformance_tests_<lang>.sh <build_folder> <conformance_tests_folder>` (absolute paths) runs the whole conformance suite at once and its exit code decides the conformance tests. There is no prepare-environment script.
|
|
122
|
+
|
|
123
|
+
Both scripts copy the renderer's output (`$1`, i.e. `plain_modules/<module>/code/`) into the host's source tree at the module's package path, then compile / test the host project in place. The conformance script also uses a scratch folder in the system temp directory (`/tmp/<lang>_conformance/`), because the conformance suite is a separate project that consumes the host build as a dependency.
|
|
124
|
+
|
|
125
|
+
This matters because the integration's generated code references host symbols by their full import path (e.g. `from host_project.integrations.base import IntegrationContract`). Those imports only resolve cleanly when the test process is rooted in the host's package layout — anything else creates path edge cases that bite later in conformance failures.
|
|
126
|
+
|
|
127
|
+
See [`integration-embedded-testing.md`](integration-embedded-testing.md) for the full per-script contract (arg validation, exit codes, idempotency, output parsing, pass criteria, cross-cutting rules). The summary that belongs in *this* file:
|
|
128
|
+
|
|
129
|
+
- **`run_unittests_<lang>`** copies `$1` into the host's source tree at the module's package path, then runs the module's unit tests + lint scoped to the module's package
|
|
130
|
+
- **`run_conformance_tests_<lang>`** makes the same copy of `$1` into the host, cleans the host's build-output directory, and runs the host's install / build (e.g. `mvn clean install -DskipTests`) so the conformance project resolves this build from the local dependency cache; then it copies `$2` (the conformance-tests folder) into `/tmp/<lang>_conformance/` (system temp), `cd`s in, builds the conformance project, and runs the whole suite
|
|
131
|
+
|
|
132
|
+
### Invariants the scripts must enforce
|
|
133
|
+
|
|
134
|
+
- **Host root is a parameter, not a literal.** No script may hardcode an absolute host path. Read the host root from an env var (e.g. `HOST_CODEBASE_ROOT`) with a sensible default matching the user's layout (e.g. `../host_project`). Surface the env var in each script's `--help` / usage banner. Capture this env var in the integration's configuration concept so it has exactly one declared name across specs, scripts, and runtime
|
|
135
|
+
- **Everything about `:UnitTests:` is declared in `***implementation reqs***`** — paths, approach, packages, framework, conventions. Both scripts derive their copy destinations from these reqs, and the unit-test script its test-filter argument. See [`integration-embedded-testing.md`](integration-embedded-testing.md) for the exact reqs the spec must author (phrased in terms of `:UnitTests:`)
|
|
136
|
+
- **Everything about `:ConformanceTests:` is declared in `***test reqs***`** — paths, approach, packages, framework, execution command, pass criteria, mocking policy. The conformance script derives its build and run steps from these reqs. See [`integration-embedded-testing.md`](integration-embedded-testing.md) for the exact reqs (phrased in terms of `:ConformanceTests:`)
|
|
137
|
+
- **The two groups never overlap.** `:UnitTests:` facts belong only in `***implementation reqs***`; `:ConformanceTests:` facts belong only in `***test reqs***`. Neither lives in the `host-codebase` concept
|
|
138
|
+
- **Destructive ops are scoped to the module's own package path** under the host's source tree. `rm -rf` never touches the host's `src/main/`, `target/`, `node_modules/`, `build/`, or `dist/` at the project root. Only the module-specific package directories are wiped
|
|
139
|
+
- **Each script is idempotent.** Re-running the same script with the same `$1` yields the same result
|
|
140
|
+
|
|
141
|
+
## Embedded-specific completion checklist
|
|
142
|
+
|
|
143
|
+
Before declaring an embedded integration done, in addition to the shared checklist in [`integrations.md`](integrations.md):
|
|
144
|
+
|
|
145
|
+
- [ ] `host-codebase` concept records host root path, host language + version, host dependency manager + manifest, target package path, host base class import path, target generated-class FQN, and the host conventions the contract follows
|
|
146
|
+
- [ ] Contract spec carries renderer directives (target language, target file path, target class name, host base class to subclass, host-package pins) and **links** to the contract schema; no class body is inlined
|
|
147
|
+
- [ ] Every host symbol referenced in any spec uses its fully qualified import path and is tagged "imported from the host codebase; do not redefine"
|
|
148
|
+
- [ ] Every host file the integration touches is linked at its **original host-relative path** — no host file has been copied into `resources/host/` or anywhere else, and no host file contents are inlined in any spec
|
|
149
|
+
- [ ] `brood`'s stack-phase decisions are transcribed verbatim from the host (no independent stack choices)
|
|
150
|
+
- [ ] Host-package version pins are copied into `***implementation reqs***`
|
|
151
|
+
- [ ] `run_unittests` copies `$1` into the host's source tree at the module's package path and invokes the host's test runner scoped to the module's package
|
|
152
|
+
- [ ] `run_conformance_tests` makes the same copy of `$1` into the host, cleans the host's build-output directory, runs the host's install / build so the conformance suite resolves the integration from the local dependency cache, then copies `$2` into `/tmp/<lang>_conformance/` (system temp), `cd`s in, builds the conformance project, and runs the whole suite
|
|
153
|
+
- [ ] There is no `prepare_environment_<lang>` script; each test script is self-contained
|
|
154
|
+
- [ ] Host codebase root is read from a named env var (default value documented in each script's usage) — never hardcoded; the env var name is captured in the integration's configuration concept
|
|
155
|
+
- [ ] `***implementation reqs***` declares **everything about `:UnitTests:`** — integration source path, `:UnitTests:` source path, `:UnitTests:` package, framework + conventions, lint / static-analysis gate — per [`integration-embedded-testing.md`](integration-embedded-testing.md)
|
|
156
|
+
- [ ] `***test reqs***` declares **everything about `:ConformanceTests:`** — source location, framework + execution command, package, mocking / network policy, pass criteria, build / install needs — per [`integration-embedded-testing.md`](integration-embedded-testing.md)
|
|
157
|
+
- [ ] Neither group is duplicated across sections: `:UnitTests:` facts never appear in `***test reqs***`, `:ConformanceTests:` facts never appear in `***implementation reqs***`, and neither lives in the `host-codebase` concept
|
|
158
|
+
- [ ] Every `rm -rf` in the scripts is scoped to the module's own package directory under the host's source tree — never targets the host's `src/main/`, `target/`, `node_modules/`, `build/`, or `dist/` at the project root
|
|
159
|
+
|
|
160
|
+
## Anti-patterns specific to embedded integrations
|
|
161
|
+
|
|
162
|
+
- **Choosing a different language, framework, or dependency manager than the host.** The host stack is inherited; cross-stack `requires` chains are forbidden by [`requires-modules.md`](requires-modules.md)
|
|
163
|
+
- **Redefining a host class under `plain_modules/`.** Reference the host symbol by FQN; let the renderer import it
|
|
164
|
+
- **Inlining a host base class body into the contract spec.** Reference the host file as a linked resource **at its original host-relative path** — do not inline its contents and do not copy it into `resources/`
|
|
165
|
+
- **Copying host source files into `resources/host/` (or anywhere under `resources/`).** That creates a second source of truth that silently drifts from the host. Link the host file in place; the integration `.plain` module already lives inside the host codebase, so the path resolves naturally
|
|
166
|
+
- **Hardcoding the host codebase path in any spec or script.** Read it from the env var declared in the configuration concept
|
|
167
|
+
- **Asking the user to design the integration's tech stack.** Read it from the host's manifest files
|
|
168
|
+
- **Authoring an integration spec that contradicts an existing integration in the same host** without first surfacing the conflict and getting explicit user confirmation
|
|
169
|
+
- **Wiring tests with `PYTHONPATH` / `NODE_PATH` / Go `replace` directives instead of physically copying `$1` into the host's package tree.** The import-stitching approach is forbidden — every test script starts by copying the generated module into the host's source tree at the module's package path
|
|
170
|
+
- **Letting an `rm -rf` in a test script reach the host's `src/main/`, `target/`, `node_modules/`, `build/`, or `dist/` at the project root.** Destructive ops are scoped strictly to the module's own package directory. A wrong package path in the script silently does nothing and copies files in the wrong place — both produce a green build with stale code
|
|
171
|
+
- **Running conformance tests against a stale local dependency cache.** The conformance script must stage `$1` and build / install the host itself before it runs the suite, so the conformance project resolves this build from `~/.m2` (or the language equivalent). Without that step the suite tests whichever build last hit the cache — the wrong code
|