@turri-dev/cli 0.1.27-next.763d091 → 0.1.28-next.d5f2680

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (216) hide show
  1. package/README.md +6 -6
  2. package/dist/advisor-chat.d.ts +63 -0
  3. package/dist/advisor-chat.js +290 -0
  4. package/dist/background-skills.d.ts +53 -0
  5. package/dist/background-skills.js +121 -0
  6. package/dist/bedrock-region.d.ts +66 -0
  7. package/dist/bedrock-region.js +144 -0
  8. package/dist/btw.d.ts +1 -1
  9. package/dist/btw.js +1 -1
  10. package/dist/change-plain-cli.js +20 -13
  11. package/dist/chat-idle.d.ts +7 -0
  12. package/dist/chat-idle.js +20 -0
  13. package/dist/chats.d.ts +10 -0
  14. package/dist/chats.js +23 -0
  15. package/dist/cli-options.d.ts +1 -2
  16. package/dist/cli-options.js +2 -7
  17. package/dist/cli.js +11 -3
  18. package/dist/config-command.d.ts +14 -0
  19. package/dist/config-command.js +35 -15
  20. package/dist/config-edit.d.ts +3 -3
  21. package/dist/config-edit.js +16 -12
  22. package/dist/config.d.ts +54 -34
  23. package/dist/config.js +40 -44
  24. package/dist/conform-cli.js +12 -10
  25. package/dist/conformance-switch.d.ts +34 -0
  26. package/dist/conformance-switch.js +108 -0
  27. package/dist/cultivate-cli.js +15 -5
  28. package/dist/drift-cli.js +2 -0
  29. package/dist/extension.d.ts +15 -0
  30. package/dist/extension.js +260 -83
  31. package/dist/host-session.d.ts +26 -0
  32. package/dist/host-session.js +30 -2
  33. package/dist/host.d.ts +5 -2
  34. package/dist/host.js +7 -1
  35. package/dist/init-config.d.ts +1 -1
  36. package/dist/init-config.js +20 -11
  37. package/dist/loop-cli.d.ts +3 -3
  38. package/dist/loop-cli.js +13 -16
  39. package/dist/loop-tools.js +18 -10
  40. package/dist/model-check.d.ts +8 -3
  41. package/dist/model-check.js +13 -8
  42. package/dist/new-project.js +12 -8
  43. package/dist/{validate-cli.js → parse-cli.js} +11 -5
  44. package/dist/parse-tool.d.ts +9 -0
  45. package/dist/parse-tool.js +33 -0
  46. package/dist/pi-launch.js +1 -1
  47. package/dist/plain/check.d.ts +53 -6
  48. package/dist/plain/check.js +217 -24
  49. package/dist/plain/concepts.d.ts +8 -0
  50. package/dist/plain/concepts.js +66 -23
  51. package/dist/plain/cycles.d.ts +21 -0
  52. package/dist/plain/cycles.js +190 -0
  53. package/dist/plain/errors.d.ts +4 -0
  54. package/dist/plain/errors.js +7 -0
  55. package/dist/plain/files.d.ts +18 -1
  56. package/dist/plain/files.js +39 -2
  57. package/dist/plain/graph.d.ts +33 -4
  58. package/dist/plain/graph.js +60 -12
  59. package/dist/plain/highlight.d.ts +3 -2
  60. package/dist/plain/highlight.js +4 -3
  61. package/dist/plain/index.d.ts +8 -5
  62. package/dist/plain/index.js +8 -5
  63. package/dist/plain/liquid.js +1 -6
  64. package/dist/plain/markdown.d.ts +32 -3
  65. package/dist/plain/markdown.js +146 -24
  66. package/dist/plain/module-concepts.js +2 -2
  67. package/dist/plain/parser.d.ts +83 -28
  68. package/dist/plain/parser.js +381 -260
  69. package/dist/plain/plain.tmLanguage.json +29 -3
  70. package/dist/plain/project.d.ts +48 -0
  71. package/dist/plain/project.js +155 -0
  72. package/dist/plain/spec.d.ts +90 -28
  73. package/dist/plain/spec.js +107 -133
  74. package/dist/plain/test-plain-file.d.ts +59 -0
  75. package/dist/plain/test-plain-file.js +232 -0
  76. package/dist/regenerate/activity.d.ts +20 -9
  77. package/dist/regenerate/activity.js +11 -4
  78. package/dist/regenerate/advisor-records.d.ts +93 -0
  79. package/dist/regenerate/advisor-records.js +110 -0
  80. package/dist/regenerate/advisor-runs.d.ts +87 -0
  81. package/dist/regenerate/advisor-runs.js +327 -0
  82. package/dist/regenerate/base-folder.d.ts +68 -0
  83. package/dist/regenerate/base-folder.js +168 -0
  84. package/dist/regenerate/cases.d.ts +9 -6
  85. package/dist/regenerate/cases.js +71 -15
  86. package/dist/regenerate/change-view.d.ts +8 -3
  87. package/dist/regenerate/change-view.js +71 -22
  88. package/dist/regenerate/chunk-plan.d.ts +36 -15
  89. package/dist/regenerate/chunk-plan.js +165 -59
  90. package/dist/regenerate/conform.d.ts +3 -0
  91. package/dist/regenerate/conform.js +6 -5
  92. package/dist/regenerate/conformance-agents.d.ts +18 -39
  93. package/dist/regenerate/conformance-agents.js +60 -88
  94. package/dist/regenerate/conformance.d.ts +23 -20
  95. package/dist/regenerate/conformance.js +118 -121
  96. package/dist/regenerate/diff-view.d.ts +1 -1
  97. package/dist/regenerate/diff-view.js +20 -4
  98. package/dist/regenerate/drift.d.ts +47 -15
  99. package/dist/regenerate/drift.js +101 -30
  100. package/dist/regenerate/jobs.d.ts +2 -2
  101. package/dist/regenerate/jobs.js +1 -1
  102. package/dist/regenerate/ledger.d.ts +19 -0
  103. package/dist/regenerate/loop.d.ts +61 -49
  104. package/dist/regenerate/loop.js +473 -340
  105. package/dist/regenerate/paths.d.ts +4 -0
  106. package/dist/regenerate/paths.js +7 -1
  107. package/dist/regenerate/plain-palette.js +2 -0
  108. package/dist/regenerate/prompt.d.ts +57 -19
  109. package/dist/regenerate/prompt.js +157 -122
  110. package/dist/regenerate/records.d.ts +43 -12
  111. package/dist/regenerate/records.js +102 -88
  112. package/dist/regenerate/regenerate.js +29 -11
  113. package/dist/regenerate/regression.d.ts +4 -4
  114. package/dist/regenerate/regression.js +14 -10
  115. package/dist/regenerate/renames.d.ts +42 -0
  116. package/dist/regenerate/renames.js +185 -0
  117. package/dist/regenerate/requires.d.ts +1 -1
  118. package/dist/regenerate/requires.js +10 -3
  119. package/dist/regenerate/resources.d.ts +5 -3
  120. package/dist/regenerate/resources.js +4 -4
  121. package/dist/regenerate/scripts-being-written.d.ts +19 -0
  122. package/dist/regenerate/scripts-being-written.js +30 -0
  123. package/dist/regenerate/spec-map-view.d.ts +2 -2
  124. package/dist/regenerate/spec-map-view.js +46 -15
  125. package/dist/regenerate/spec-map.d.ts +27 -5
  126. package/dist/regenerate/spec-map.js +92 -60
  127. package/dist/regenerate/spec-steps.d.ts +48 -5
  128. package/dist/regenerate/spec-steps.js +213 -28
  129. package/dist/regenerate/state.d.ts +4 -3
  130. package/dist/regenerate/stop-report.d.ts +1 -1
  131. package/dist/regenerate/stop-report.js +10 -10
  132. package/dist/regenerate/test-advisor.d.ts +101 -0
  133. package/dist/regenerate/test-advisor.js +379 -0
  134. package/dist/regenerate/test-kinds.d.ts +69 -31
  135. package/dist/regenerate/test-kinds.js +109 -80
  136. package/dist/regenerate/test-scripts.d.ts +2 -0
  137. package/dist/regenerate/test-scripts.js +4 -0
  138. package/dist/regenerate/testing-steps.d.ts +41 -0
  139. package/dist/regenerate/testing-steps.js +110 -0
  140. package/dist/regenerate/testing.d.ts +1 -1
  141. package/dist/regenerate/testing.js +5 -4
  142. package/dist/regenerate/unit-review.d.ts +1 -1
  143. package/dist/regenerate/unit-review.js +3 -3
  144. package/dist/regenerate-cli.js +11 -12
  145. package/dist/release-agent.js +4 -0
  146. package/dist/release-cli.js +2 -0
  147. package/dist/release-tools.js +2 -0
  148. package/dist/run-report.d.ts +2 -2
  149. package/dist/run-report.js +4 -3
  150. package/dist/self-invocation.d.ts +1 -1
  151. package/dist/self-invocation.js +1 -1
  152. package/dist/sentinel.js +1 -1
  153. package/dist/setup.d.ts +10 -0
  154. package/dist/setup.js +52 -4
  155. package/dist/skill.d.ts +10 -0
  156. package/dist/skill.js +6 -0
  157. package/dist/skills.js +3 -1
  158. package/dist/spec-tools.js +21 -31
  159. package/dist/test-plain-files.d.ts +41 -0
  160. package/dist/test-plain-files.js +177 -0
  161. package/docs/README.md +3 -1
  162. package/docs/authoring-plain.md +46 -13
  163. package/docs/chats.md +3 -1
  164. package/docs/config.md +20 -15
  165. package/docs/deploy.md +1 -1
  166. package/docs/development.md +2 -1
  167. package/docs/headless.md +15 -18
  168. package/docs/hosting.md +28 -12
  169. package/docs/installation.md +1 -0
  170. package/docs/parser.md +88 -27
  171. package/docs/regenerating.md +89 -54
  172. package/docs/sentinel.md +1 -1
  173. package/docs/skills-and-gates.md +3 -1
  174. package/docs/test-advisor.md +110 -0
  175. package/docs/theming.md +1 -1
  176. package/docs/using.md +17 -15
  177. package/genome/rules/bullet-continuation.md +8 -8
  178. package/genome/rules/concise-specs-examples.md +111 -80
  179. package/genome/rules/concise-specs-integration-examples.md +56 -41
  180. package/genome/rules/concise-specs.md +17 -16
  181. package/genome/rules/definitions.md +5 -5
  182. package/genome/rules/examples/integration-embedded-testing/run_conformance_tests_java.sh +1 -1
  183. package/genome/rules/func-specs.md +69 -74
  184. package/genome/rules/impl-reqs.md +4 -4
  185. package/genome/rules/integration-embedded-testing.md +20 -20
  186. package/genome/rules/integration-embedded.md +31 -31
  187. package/genome/rules/integration-standalone.md +9 -9
  188. package/genome/rules/integrations.md +18 -18
  189. package/genome/rules/linked-resources.md +19 -16
  190. package/genome/rules/module-structure.md +21 -14
  191. package/genome/rules/project-model.md +18 -17
  192. package/genome/rules/required-concepts.md +1 -1
  193. package/genome/rules/requires-modules.md +6 -5
  194. package/genome/rules/semantic-ids.md +49 -0
  195. package/genome/rules/stdlib.md +4 -5
  196. package/genome/rules/test-plain-files.md +130 -0
  197. package/genome/rules/test-reqs.md +8 -8
  198. package/genome/rules/turri-regeneration.md +24 -8
  199. package/genome/skills/analyze-2-func-specs/SKILL.md +36 -21
  200. package/genome/skills/analyze-func-specs/SKILL.md +46 -32
  201. package/genome/skills/break-down-func-spec/SKILL.md +91 -67
  202. package/genome/skills/consolidate-concepts/SKILL.md +10 -10
  203. package/genome/skills/convert-old-syntax/SKILL.md +68 -0
  204. package/genome/skills/convert-old-syntax/references/rubric.md +11 -0
  205. package/genome/skills/debug-specs/SKILL.md +62 -56
  206. package/genome/skills/implement-conformance-testing-script/SKILL.md +19 -15
  207. package/genome/skills/implement-conformance-testing-script/assets/run_conformance_tests_java.sh +2 -2
  208. package/genome/skills/implement-conformance-testing-script/assets/run_conformance_tests_python.sh +2 -2
  209. package/genome/skills/implement-unit-testing-script/SKILL.md +14 -10
  210. package/genome/skills/refactor-module/SKILL.md +35 -35
  211. package/genome/skills/resolve-section-ownership/SKILL.md +9 -9
  212. package/genome/skills/resolve-spec-conflict/SKILL.md +35 -29
  213. package/package.json +7 -6
  214. package/dist/regenerate/choose-conformance.d.ts +0 -32
  215. package/dist/regenerate/choose-conformance.js +0 -117
  216. /package/dist/{validate-cli.d.ts → parse-cli.d.ts} +0 -0
@@ -1,25 +1,25 @@
1
1
  ---
2
- description: Rules for authoring ***plain specs for REST API integrations embedded into an existing host codebase
2
+ description: Rules for authoring ***plain for REST API integrations embedded into an existing host codebase
3
3
  ---
4
4
 
5
- # Rules for **embedded** integration specs
5
+ # Rules for **embedded** integrations in ***plain
6
6
 
7
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
8
 
9
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
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.
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 ***plain 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
12
 
13
13
  ## The host codebase dictates the tech stack (hard rule)
14
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
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's ***plain
16
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
17
  - If a stack question seems to push back on a host rule, treat the host as ground truth and rewrite the question
18
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
19
 
20
20
  ## Project layout — the integration lives under `plain/` at the host root
21
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:
22
+ The embedded integration's `.plain` files (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
23
 
24
24
  ```
25
25
  <host repo>/
@@ -36,7 +36,7 @@ The embedded integration's `.plain` specs (and the rest of the ***plain project
36
36
  └── src/ # host source tree (Java, Python, etc.)
37
37
  ```
38
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.
39
+ This keeps the integration self-contained in one directory the user can `cd` into to regenerate, while leaving the host's own source layout untouched.
40
40
 
41
41
  - If the host **already** has a `plain/` directory, adopt it verbatim — see step 1 of *Discover before you ask* below
42
42
  - If the host does **not** yet have one, create `plain/` at the repo root; do not invent a different name (`specs/`, `plain_specs/`, …)
@@ -60,19 +60,19 @@ Run host discovery **before** the first interview question. Treat the results as
60
60
 
61
61
  ## Reference host symbols by fully qualified import path
62
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/`
63
+ - Every host class, interface, struct, exception, or type alias that appears in the ***plain must be written with its full dotted / slashed import path (e.g. `host_project.integrations.base.IntegrationContract`, `@host/integrations#Contract`) and tagged in the ***plain's text as **"imported from the host codebase; do not redefine"**
64
+ - The coding agent 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 coding agent where the type comes from, which prevents a duplicate definition under `plain_modules/`
66
66
 
67
67
  ## Link host files at their original path — never copy them into `resources/`
68
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/`.
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's ***plain references them **in place**; it never duplicates them under `resources/host/`.
70
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
71
+ - Every host file the integration touches (base classes, configuration modules, registries, exception classes, lifecycle hooks) is referenced from the relevant ***plain 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 regenerated code will then disagree with whatever the host actually ships
73
73
  - **Do NOT copy host files into `resources/`** when that would duplicate them — link 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
74
+ - **Never inline a host file's contents** into the ***plain
75
+ - **Never describe a host symbol's shape from memory** — the coding agent reads the linked file's bytes at its host path and that is the source of truth
76
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
77
 
78
78
  ### What still belongs under `resources/`
@@ -85,16 +85,16 @@ This rule applies to **host source code only**. Other artifacts still live under
85
85
 
86
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
87
 
88
- ## The contract spec declares inheritance, not duplication
88
+ ## The contract's ***plain declares inheritance, not duplication
89
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
90
+ - The entry-point class / interface / struct in the contract's ***plain must `subclass` / `implements` / `embeds` the host symbol by its full import path
91
+ - The ***plain describes only the **integration-specific additions and overrides** — never restates the parent's fields or methods
92
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
93
  - If a host base class adds fields the integration shouldn't redeclare, the contract schema's `allOf` chain captures that explicitly
94
94
 
95
- ## Renderer directives go in the spec, shapes go in the schema
95
+ ## Coding-agent directives go in the ***plain, shapes go in the schema
96
96
 
97
- Each contract spec carries the language-specific glue that the schema can't express:
97
+ The contract's ***plain carries the language-specific glue that the schema can't express:
98
98
 
99
99
  - Target generated-class fully qualified name (e.g. `plain_modules.integrations.<provider>.Client`)
100
100
  - Target file path under `plain_modules/`
@@ -102,13 +102,13 @@ Each contract spec carries the language-specific glue that the schema can't expr
102
102
  - Host-package version pins (e.g. `pydantic ~= 2.5`, `fastapi ^0.110`)
103
103
  - Framework-specific decorators or metaclasses (`model_config`, `@Depends`, …)
104
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.
105
+ The coding agent reads the directives from the ***plain and the shapes from the linked schema, then emits the host-language class. The ***plain must **not** also contain a class body or a field list — that creates two sources of truth and they will drift.
106
106
 
107
107
  ## Single source of truth for the host root
108
108
 
109
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
110
+ - Test scripts, configuration-loading functional specs, and any other ***plain 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 ***plain, script, or runtime config
112
112
 
113
113
  ## No host-overlapping reqs
114
114
 
@@ -120,7 +120,7 @@ The renderer reads the directives from the spec and the shapes from the linked s
120
120
 
121
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
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.
123
+ Both scripts copy the coding agent'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
124
 
125
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
126
 
@@ -131,8 +131,8 @@ See [`integration-embedded-testing.md`](integration-embedded-testing.md) for the
131
131
 
132
132
  ### Invariants the scripts must enforce
133
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:`)
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 the ***plain, 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 ***plain must author (phrased in terms of `:UnitTests:`)
136
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
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
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
@@ -143,9 +143,9 @@ See [`integration-embedded-testing.md`](integration-embedded-testing.md) for the
143
143
  Before declaring an embedded integration done, in addition to the shared checklist in [`integrations.md`](integrations.md):
144
144
 
145
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
146
+ - [ ] The contract's ***plain carries coding-agent 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 anywhere in the ***plain 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 the ***plain
149
149
  - [ ] The stack decisions are transcribed verbatim from the host (no independent stack choices)
150
150
  - [ ] Host-package version pins are copied into `***implementation reqs***`
151
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
@@ -160,12 +160,12 @@ Before declaring an embedded integration done, in addition to the shared checkli
160
160
  ## Anti-patterns specific to embedded integrations
161
161
 
162
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
163
+ - **Redefining a host class under `plain_modules/`.** Reference the host symbol by FQN; let the coding agent import it
164
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
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
166
+ - **Hardcoding the host codebase path in any ***plain or script.** Read it from the env var declared in the configuration concept
167
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
168
+ - **Authoring an integration's ***plain that contradicts an existing integration in the same host** without first surfacing the conflict and getting explicit user confirmation
169
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
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
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
@@ -1,8 +1,8 @@
1
1
  ---
2
- description: Rules for authoring ***plain specs for REST API integrations deployed standalone
2
+ description: Rules for authoring ***plain for REST API integrations deployed standalone
3
3
  ---
4
4
 
5
- # Rules for **standalone** integration specs
5
+ # Rules for **standalone** integrations in ***plain
6
6
 
7
7
  When an integration `.plain` module is **standalone** — meaning the generated code in `plain_modules/` is the deployable artifact itself (a service, daemon, CLI, scheduled job, container, or library published to a registry) — 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
8
 
@@ -23,7 +23,7 @@ Standalone integrations may freely import any third-party library that isn't con
23
23
 
24
24
  ## The contract schema is the public artifact
25
25
 
26
- In an embedded integration, the schema feeds the renderer's code generation; the host codebase consumes the *generated class*. In a standalone integration, the schema is **shipped** to external consumers and the generated implementation is internal to `plain_modules/`.
26
+ In an embedded integration, the schema feeds the coding agent's code generation; the host codebase consumes the *generated class*. In a standalone integration, the schema is **shipped** to external consumers and the generated implementation is internal to `plain_modules/`.
27
27
 
28
28
  - **Pick the publication surface and write it into the spec.** Examples (one functional spec per surface):
29
29
  - HTTP service: "served at `/openapi.json` (and a Swagger UI at `/docs`)"
@@ -33,16 +33,16 @@ In an embedded integration, the schema feeds the renderer's code generation; the
33
33
  - Queue worker: "published as a JSON Schema in the team's schema registry under `<topic>.v<version>.json`"
34
34
  - Scheduled job: "published as JSON Schema in `resources/contract/job-config.schema.json` and linked from the runbook"
35
35
  - **Schema versioning is part of the contract.** Pin the version in the schema's `$id` / OpenAPI `info.version` and follow semver. Backwards-incompatible changes ship under a new file path, never as mutations to the published version
36
- - **The renderer ships the schema verbatim** — the spec must not restate fields from the schema, and the generated implementation must consume the same file as the published artifact (so the two cannot drift)
36
+ - **The coding agent ships the schema verbatim** — the ***plain must not restate fields from the schema, and the generated implementation must consume the same file as the published artifact (so the two cannot drift)
37
37
 
38
38
  ## All lifecycle stages must be explicit
39
39
 
40
40
  A standalone integration has no host lifecycle to delegate to. Every stage that an embedded integration could skip is mandatory here, each one captured as its own functional spec:
41
41
 
42
- - **Initialization** — one-time setup at startup. Read configuration, validate credentials proactively (eager) or defer to first call (lazy) — pick one and write the spec accordingly. Open connection pools, register webhook handlers, initialize observability
42
+ - **Initialization** — one-time setup at startup. Read configuration, validate credentials proactively (eager) or defer to first call (lazy) — pick one and write the ***plain accordingly. Open connection pools, register webhook handlers, initialize observability
43
43
  - **Credential refresh** — proactive (background timer / pre-expiry refresh) vs. reactive (refresh-on-401). Use a refresh lock to prevent thundering herd. Specify the lock primitive (in-process mutex, distributed lock in Redis, …) when the integration runs as more than one replica
44
44
  - **Graceful shutdown** — drain in-flight requests with a deadline; what happens to retries still queued (abandoned, persisted, returned to caller as a specific error); what signals trigger shutdown (`SIGTERM`, container-stop hook, …)
45
- - **Health checks** — define what "healthy" means for **this** integration: last successful provider call within a window, valid credentials, provider's own `/health` returning 2xx, dependent storage reachable. Spec the response shape (status code, body) for the health endpoint when one is exposed
45
+ - **Health checks** — define what "healthy" means for **this** integration: last successful provider call within a window, valid credentials, provider's own `/health` returning 2xx, dependent storage reachable. State the response shape (status code, body) for the health endpoint when one is exposed
46
46
  - **Background loops** — cron jobs, polling workers, queue consumers: each gets its own functional spec covering trigger, interval, leader-election (if applicable), failure handling, and observability
47
47
 
48
48
  ## Entry-point types — pick deliberately, enumerate fully
@@ -71,7 +71,7 @@ A standalone integration usually mutates state outside the provider call itself.
71
71
 
72
72
  ## Concurrency and backpressure
73
73
 
74
- Standalone integrations face concurrent load that an embedded library doesn't. The spec must pin:
74
+ Standalone integrations face concurrent load that an embedded library doesn't. The ***plain must pin:
75
75
 
76
76
  - **Sync vs. async** in the host-language sense (`async def`, `Promise`, goroutines, threads)
77
77
  - **Expected peak throughput** (RPS) the consumer will issue
@@ -128,8 +128,8 @@ Before declaring a standalone integration done, in addition to the shared checkl
128
128
  ## Anti-patterns specific to standalone integrations
129
129
 
130
130
  - **Skipping any lifecycle stage** because it "feels small". Standalone integrations have no host to fall back on; an implicit shutdown is an undefined shutdown
131
- - **Inlining fields from `resources/contract/<entry-point>.schema.json` into a functional spec.** The schema is the source of truth and is **published** — duplicating fields in spec text guarantees drift
131
+ - **Inlining fields from `resources/contract/<entry-point>.schema.json` into a functional spec.** The schema is the source of truth and is **published** — duplicating fields in ***plain guarantees drift
132
132
  - **Mutating a published schema's `$id` / version path** instead of shipping a new file. Consumers may already be pinning the old version
133
133
  - **Treating configuration as "whatever env vars seem useful at runtime".** Every key lives in `resources/config.schema.json` with type, default, and validation; the loader fails fast on missing required values
134
- - **Picking a tech stack incrementally while writing functional specs.** Decide it once, when the stack is chosen, and transcribe into `***implementation reqs***`; don't accrete a stack across several specs
134
+ - **Picking a tech stack incrementally while writing functional specs.** Decide it once, when the stack is chosen, and transcribe into `***implementation reqs***`; don't accrete a stack across several functional specs
135
135
  - **Hitting the live provider in rate-limit (429) or destructive-error (500) tests.** Use a local mock; live calls are for the happy path and for sandbox-safe error paths
@@ -1,12 +1,12 @@
1
1
  ---
2
- description: Rules for authoring ***plain specs for REST API integrations
2
+ description: Rules for authoring ***plain for REST API integrations
3
3
  ---
4
4
 
5
- # Rules for writing integration specs in `***plain`
5
+ # Rules for writing integrations in `***plain`
6
6
 
7
7
  When writing or editing a `.plain` file that describes an **integration** against a REST API (synchronous JSON/HTTP request-response plus webhook callbacks), always follow these rules. Non-REST integrations (gRPC, GraphQL, SOAP, message brokers, raw TCP, file drops) are out of scope of these rules.
8
8
 
9
- ## Scope of "integration" specs
9
+ ## Scope of "integration" ***plain
10
10
  - An integration `.plain` module describes how the project talks to a **third-party or internal REST API**
11
11
  - The integration may be **embedded** (lives as a library/module inside an existing host codebase) — see [`integration-embedded.md`](integration-embedded.md) for the additional rules that apply
12
12
  - Or **standalone** (a service, daemon, CLI, scheduled job, or container) — see [`integration-standalone.md`](integration-standalone.md) for the additional rules that apply
@@ -34,23 +34,23 @@ Rules that flow from this:
34
34
 
35
35
  - **Provider behavior is recorded, not specified.** How the provider itself works — its quota accounting, consistency guarantees, documented quirks, and any place the live API diverges from its docs — is true and worth capturing, but it is not a requirement on the software being built. Whatever has a machine-readable home in the table above goes there; the rest goes in `resources/<provider>-notes.md`. A functional spec never states provider behavior; it states what the integration does in the face of it.
36
36
  - **Concepts carry references, not data.** An endpoint concept names the endpoint and points at its OpenAPI `paths.<path>.<method>` entry; it does not duplicate the request/response shape in concept attributes. Same for webhook concepts, error-model concepts, pagination concepts, etc.
37
- - **Functional specs consume linked resources.** A spec describes **behavior** ("call endpoint X, parse the response, classify errors, retry on 5xx") and links to the resource that supplies the **shape**. Field names, types, and validation rules live in the resource file.
37
+ - **Functional specs consume linked resources.** A functional spec describes **behavior** ("call endpoint X, parse the response, classify errors, retry on 5xx") and links to the resource that supplies the **shape**. Field names, types, and validation rules live in the resource file.
38
38
  - **Schemas are versioned by file path.** When the provider releases a new API version, copy the new OpenAPI file to a new path (e.g. `resources/<provider>.v2.openapi.yaml`); never mutate the v1 file in place.
39
- - **The renderer generates language-native types from the resources.** For embedded integrations the renderer reads JSON Schema / OpenAPI components and emits Pydantic / TypeScript / Go types in `plain_modules/`. The spec declares **which** schema to generate from, **where** the generated type should land, and **what host base class** it must subclass — but never restates the schema's fields.
39
+ - **The coding agent generates language-native types from the resources.** For embedded integrations the coding agent reads JSON Schema / OpenAPI components and emits Pydantic / TypeScript / Go types in `plain_modules/`. The ***plain declares **which** schema to generate from, **where** the generated type should land, and **what host base class** it must subclass — but never restates the schema's fields.
40
40
 
41
41
  A single `.plain` module can (and typically will) reference many resources. That is the intended pattern; do not try to collapse them into one mega-file.
42
42
 
43
43
  ## Live API must be cross-checked against the documentation
44
44
 
45
- Documentation lies — it goes stale, omits undocumented fields, describes a different API version, papers over breaking changes. Every integration spec must be grounded in what the API really returns, not what the docs claim it returns.
45
+ Documentation lies — it goes stale, omits undocumented fields, describes a different API version, papers over breaking changes. Every integration's ***plain must be grounded in what the API really returns, not what the docs claim it returns.
46
46
 
47
47
  - **Discover the documentation links with web search first, then `fetch` all of them.** Documentation moves, gets reorganized, and is versioned — so never assume a doc URL from memory. Begin every topic by issuing **web searches with human-readable queries** to find the canonical pages, then `fetch` **every** relevant link the search surfaces — the endpoint reference, the auth guide, the webhook catalog, the error reference, the pagination/rate-limit docs, the changelog. The search step finds *which* URLs are authoritative and current; the `fetch` step retrieves their actual content. Do not stop at the first hit — fetch the full set so a topic is grounded in all of its sources, not a single page.
48
48
  - If the environment has **no web-search tool** (only URL-based `fetch` is available), say so explicitly, construct the URL from the provider's well-known documentation root and crawl outward by `fetch`-ing that root and following its links, and ask the user for the canonical URL whenever you cannot reach the right page that way. Never substitute memory for the search-then-fetch step.
49
- - **Always `fetch` the provider's documentation — even if you already "know" the API.** The only acceptable source of truth for what the API looks like *today* is the provider's own live documentation, retrieved with `fetch` at spec-authoring time. This applies without exception — there is no API well-known enough to skip this step, and a spec authored from memory is a spec authored against the wrong contract. Concretely:
49
+ - **Always `fetch` the provider's documentation — even if you already "know" the API.** The only acceptable source of truth for what the API looks like *today* is the provider's own live documentation, retrieved with `fetch` at authoring time. This applies without exception — there is no API well-known enough to skip this step, and ***plain authored from memory is authored against the wrong contract. Concretely:
50
50
  - Before authoring **any** endpoint, auth, error, pagination, or webhook concept, **web-search to locate the relevant documentation page(s), then `fetch` each one** and quote concrete details (status codes, field names, header names, error formats) directly from the fetched content into the resources under `resources/`. Never paraphrase from memory.
51
51
  - If a documentation page is unreachable (paywall, login wall, JS-only render that `fetch` can't see), say so explicitly and ask the user for the canonical content rather than filling the gap from memory.
52
52
  - **The fetched documentation is then cross-checked against the live API** — see the rest of this section.
53
- - **Validate credentials against the live API** before authoring downstream specs. A 2xx on a low-risk read-only endpoint (`/v1/me`, `/account`, `/whoami`, `/health`) is the gate. On 401/403, stop and resolve before continuing. The credentials should be validated against the live API before any downstream specs are authored.
53
+ - **Validate credentials against the live API** before authoring the ***plain that depends on them. A 2xx on a low-risk read-only endpoint (`/v1/me`, `/account`, `/whoami`, `/health`) is the gate. On 401/403, stop and resolve before continuing. The credentials should be validated against the live API before any ***plain that depends on them is authored.
54
54
  - **Issue the minimum cross-check coverage** with `fetch`: one discovery / schema endpoint if available, one list endpoint per primary entity in scope, one single-object retrieval per primary entity, one empty/boundary response, one 404, one 400/422, and one deliberate 401.
55
55
  - **Where the docs and the live API disagree, the live API wins** — the resource records what the API actually returns, and nothing records the disagreement itself.
56
56
  - **Only `GET` / `HEAD` / `OPTIONS` on the cross-check.** Mutating calls (`POST`, `PATCH`, `PUT`, `DELETE`) require explicit per-call user confirmation and must target a sandbox account.
@@ -67,7 +67,7 @@ Integrations exist to talk to a real third-party (or internal) API. Their `:Conf
67
67
 
68
68
  ### Secrets come from the environment
69
69
 
70
- Live conformance needs credentials. The integration spec must pin every credential as **an env var name, never a literal value**, and the conformance script must read those env vars at runtime:
70
+ Live conformance needs credentials. The integration's ***plain must pin every credential as **an env var name, never a literal value**, and the conformance script must read those env vars at runtime:
71
71
 
72
72
  - **Author the env-var names in `***test reqs***`** (using `:ConformanceTests:`) and again in the auth concept. Examples: `STRIPE_API_KEY`, `GITHUB_TOKEN`, `SALESFORCE_CLIENT_ID` + `SALESFORCE_CLIENT_SECRET`. Use names that match what the provider's own docs use, so a user copying values from the provider console doesn't need to translate
73
73
  - **The user supplies the values out-of-band**, either:
@@ -78,7 +78,7 @@ Live conformance needs credentials. The integration spec must pin every credenti
78
78
  - **The conformance suite reads the same env-var names** the integration's runtime reads — so a credential that works at runtime is the same credential that exercises the suite
79
79
  - **Credentials never appear in `.plain` files, commits, summaries, logs, or fixtures.** The cross-check (see *Live API must be cross-checked*) already requires redacting credentials from saved fixtures; the same rule applies to conformance logs
80
80
 
81
- The `.plain` spec should make this discoverable. A minimal `***test reqs***` block for an integration looks like:
81
+ The `.plain` file should make this discoverable. A minimal `***test reqs***` block for an integration looks like:
82
82
 
83
83
  ```plain
84
84
  - :ConformanceTests: run against the live :ProviderName: sandbox — no mocking of provider calls.
@@ -90,16 +90,16 @@ The `.plain` spec should make this discoverable. A minimal `***test reqs***` blo
90
90
 
91
91
  ## Embedded vs standalone — pick the shape early
92
92
 
93
- Every integration is either **embedded** (lives as a library/module inside an existing host codebase) or **standalone** (a service, daemon, CLI, scheduled job, or container). The choice is captured as a concept (`integration-shape: embedded | standalone`) so later specs can reference it.
93
+ Every integration is either **embedded** (lives as a library/module inside an existing host codebase) or **standalone** (a service, daemon, CLI, scheduled job, or container). The choice is captured as a concept (`integration-shape: embedded | standalone`) so later functional specs can reference it.
94
94
 
95
- The contract artifact itself is **identical** across both shapes: a JSON Schema (or OpenAPI) file under `resources/contract/`. What changes is **what the renderer emits from it** and **what extra context the spec carries**:
95
+ The contract artifact itself is **identical** across both shapes: a JSON Schema (or OpenAPI) file under `resources/contract/`. What changes is **what the coding agent emits from it** and **what extra context the ***plain carries**:
96
96
 
97
- - **Embedded** → the renderer generates a host-language class from the schema and wires it into the host's import path. The host codebase dictates the tech stack — see [`integration-embedded.md`](integration-embedded.md) for the full ruleset
98
- - **Standalone** → the renderer treats the schema as the public artifact: it generates an internal implementation in `plain_modules/` and ships the schema verbatim for external consumers. The integration owns its stack — see [`integration-standalone.md`](integration-standalone.md) for the full ruleset
97
+ - **Embedded** → the coding agent generates a host-language class from the schema and wires it into the host's import path. The host codebase dictates the tech stack — see [`integration-embedded.md`](integration-embedded.md) for the full ruleset
98
+ - **Standalone** → the coding agent treats the schema as the public artifact: it generates an internal implementation in `plain_modules/` and ships the schema verbatim for external consumers. The integration owns its stack — see [`integration-standalone.md`](integration-standalone.md) for the full ruleset
99
99
 
100
100
  ## Edge-case coverage is a hard floor, not a stretch goal
101
101
 
102
- A production-ready integration spec captures every corner case the API can throw at the integration. Each of the following must be in the specs (or explicitly recorded as "not applicable" / "not in scope" with the user's acknowledgement) before the integration is considered complete:
102
+ A production-ready integration's ***plain captures every corner case the API can throw at the integration. Each of the following must be in the ***plain (or explicitly recorded as "not applicable" / "not in scope" with the user's acknowledgement) before the integration is considered complete:
103
103
 
104
104
  - **Provider, purpose, canonical documentation URL(s)** as concepts
105
105
  - **Endpoints in scope** in `resources/<provider>.openapi.yaml`, one `paths.<path>.<method>` entry per endpoint; endpoint concepts link to the OpenAPI entry
@@ -115,7 +115,7 @@ A production-ready integration spec captures every corner case the API can throw
115
115
  - **Transient by default**: network-level errors (`ConnectionError` / equivalent, DNS failure, socket timeout, TLS handshake failure, connection reset), HTTP 408, 425, 429, 500, 502, 503, 504, and any provider-specific application-level error codes the docs / live API mark as retryable (e.g. Stripe's `lock_timeout`, AWS `ThrottlingException`)
116
116
  - **Permanent by default**: every other 4xx (400, 401, 403, 404, 405, 409, 410, 415, 422, …), schema-validation failures on otherwise-2xx responses, signature-verification failures on webhooks, and any provider error code the docs / live API mark as non-retryable
117
117
  - **Integration exception hierarchy**: the integration exposes a base exception class (e.g. `<Provider>Error`) with two direct subclasses, `<Provider>TransientError` (raised after the retry policy gives up on a transient condition) and `<Provider>PermanentError` (raised immediately on a permanent condition). Every concrete exception inherits from exactly one of those two — callers branch on the base class, never on individual subclasses
118
- - **The classification is data-driven**, not code-driven — `resources/error-map.yaml` is the source of truth, the renderer reads it to generate the exception hierarchy and the `is_transient(error) -> bool` predicate. Spec text never enumerates status codes inline
118
+ - **The classification is data-driven**, not code-driven — `resources/error-map.yaml` is the source of truth, the coding agent reads it to generate the exception hierarchy and the `is_transient(error) -> bool` predicate. ***plain never enumerates status codes inline
119
119
  - **Discrepancies found during the live-API cross-check override the docs** — if the docs say a code is transient but the live API never recovers from it (or vice versa), the live behavior wins and the divergence is recorded in `error-map.yaml` plus a note in the error-model concept ("docs claim X retryable, live API returns Y permanently; we follow the live API")
120
120
  - **Idempotency strategy** in `resources/idempotency.yaml` + idempotency header in `components.parameters`
121
121
  - **Webhook contracts** as `resources/webhooks/<event>.schema.json` per event type + `resources/webhook-signing.yaml`
@@ -125,12 +125,12 @@ A production-ready integration spec captures every corner case the API can throw
125
125
 
126
126
  ## Anti-patterns (do not do these)
127
127
 
128
- - **Restating an OpenAPI / JSON Schema field list in a concept or functional spec.** The schema lives in `resources/`; the spec links to it
128
+ - **Restating an OpenAPI / JSON Schema field list in a concept or functional spec.** The schema lives in `resources/`; the ***plain links to it
129
129
  - **Pasting a webhook payload, error envelope, or list-endpoint response inline.** Describe its shape in the applicable schema under `resources/` and link that schema
130
130
  - **Inlining a host base class body into the contract spec.** Add the host file as a linked resource under `resources/host/` and reference it by FQN
131
131
  - **Embedding credentials, tokens, or signing keys in a `.plain` file or in a summary** — credentials are referenced by env-var name only
132
132
  - **Authoring against unverified credentials.** Validate first; if the user has no credentials yet, flag it in the module's frontmatter description and re-validate once credentials arrive
133
133
  - **`requires`-ing a separate-stack module** (a Python backend `requires`-ing a React frontend, or vice versa) — see [`requires-modules.md`](requires-modules.md). Use a shared API schema in `resources/` instead
134
134
  - **Authoring the integration's functional specs from the docs first and "reconciling" with the live API later.** Probe the API as you reach each topic; the live response is the source of truth from the moment it's captured
135
- - **Writing any integration spec from memory of the provider's API instead of web-searching for its documentation and `fetch`-ing every relevant page first.** No matter how well-known the API (Stripe, GitHub, Slack, Salesforce, AWS, OpenAI, …), the canonical pages must be located with web search and then retrieved with `fetch` at spec-authoring time — see *Live API must be cross-checked against the documentation*. Authoring from memory bakes in whatever version of the API was current during training, which is always older than the version the integration will actually call
135
+ - **Writing any integration's ***plain from memory of the provider's API instead of web-searching for its documentation and `fetch`-ing every relevant page first.** No matter how well-known the API (Stripe, GitHub, Slack, Salesforce, AWS, OpenAI, …), the canonical pages must be located with web search and then retrieved with `fetch` at authoring time — see *Live API must be cross-checked against the documentation*. Authoring from memory bakes in whatever version of the API was current during training, which is always older than the version the integration will actually call
136
136
  - **Guessing a documentation URL from memory and `fetch`-ing it without searching first.** A remembered URL may 404, redirect to a stale version, or miss the page that actually documents the topic. Search to find the authoritative, current links, then fetch the full set
@@ -1,35 +1,35 @@
1
1
  ---
2
- description: Rules for linking external resources from .plain specs
2
+ description: Rules for linking external resources from .plain files
3
3
  ---
4
4
 
5
5
  # Rules for linked resources in `.plain` files
6
6
 
7
- Specifications can reference external files using markdown link syntax. The renderer reads the linked file's bytes verbatim and feeds them to the model alongside the spec. That mechanism only works for a specific shape of target — violating any of the rules below is one of the most common and disruptive mistakes in `.plain` authoring.
7
+ ***plain can reference external files using markdown link syntax. turri reads the linked file's bytes verbatim and feeds them to the coding agent alongside the ***plain. That mechanism only works for a specific shape of target — violating any of the rules below is one of the most common and disruptive mistakes in `.plain` authoring.
8
8
 
9
9
  ## Hard constraint: a linked resource is always a single file on disk - text, or a PNG or JPEG image
10
10
 
11
11
  A linked resource **must not** be any of the following:
12
12
 
13
- 1. **A folder / directory.** `[integrations](src/integrations/)`, `[host project](../host_project/)` are invalid — the renderer cannot ingest a directory. Pick the single most representative file inside (a `README.md`, an exemplar source file, a manifest) and link **that**.
13
+ 1. **A folder / directory.** `[integrations](src/integrations/)`, `[host project](../host_project/)` are invalid — turri cannot ingest a directory. Pick the single most representative file inside (a `README.md`, an exemplar source file, a manifest) and link **that**.
14
14
  2. **A URL / external location.** `[Stripe docs](https://stripe.com/docs/api)`, any `http://` / `https://` / `ftp://` / `git://` / `s3://` / `gs://` target. Linked resources are local-file only. If a URL's content is essential, fetch it once, save the response to a text file under `resources/`, and link the saved file.
15
- 3. **A binary file other than a PNG or JPEG image.** GIF, WEBP, SVG, PDF, DOCX, XLSX, ZIP, MP3, MP4, compiled binaries (`.exe`, `.so`, `.class`, `.wasm`), and anything else that isn't human-readable text in its raw form. Binary content cannot be meaningfully consumed by the renderer — transcribe it into a text-based form first (a PDF spec becomes a Markdown extract or the underlying JSON Schema / OpenAPI, an architecture diagram becomes a Mermaid block).
16
- - **PNG and JPEG images (`.png`, `.jpg`, `.jpeg`) are the exception**: a UI mockup, a screenshot of the look to match, a diagram. The coding agents are shown the image itself, so the coding model must be one that reads images - the render stops, saying so, when it is not. Each image is at most 5 MB.
17
- - An image says what something looks like, not what it does: the behaviour still belongs in the spec's text. Link a mockup from a concept (`:CheckoutPage:` looks as [checkout mockup](resources/checkout.png) shows) and keep the rules - what each button does, which fields are required - in the functional specs.
15
+ 3. **A binary file other than a PNG or JPEG image.** GIF, WEBP, SVG, PDF, DOCX, XLSX, ZIP, MP3, MP4, compiled binaries (`.exe`, `.so`, `.class`, `.wasm`), and anything else that isn't human-readable text in its raw form. Binary content cannot be meaningfully consumed by the coding agent — transcribe it into a text-based form first (a PDF spec becomes a Markdown extract or the underlying JSON Schema / OpenAPI, an architecture diagram becomes a Mermaid block).
16
+ - **PNG and JPEG images (`.png`, `.jpg`, `.jpeg`) are the exception**: a UI mockup, a screenshot of the look to match, a diagram. The coding agents are shown the image itself, so the coding model must be one that reads images - the regeneration stops, saying so, when it is not. Each image is at most 5 MB.
17
+ - An image says what something looks like, not what it does: the behaviour still belongs in the ***plain's text. Link a mockup from a concept (`:CheckoutPage:` looks as [checkout mockup](resources/checkout.png) shows) and keep the rules - what each button does, which fields are required - in the functional specs.
18
18
  - Where the model may not read images, or a detail must be exact (a colour code, a spacing, a label), say it in text as well; the image is the reference, not the only source.
19
- 4. **Another `.plain` file.** `[Auth Module](auth.plain)` is invalid. To reference another module, use the `require` or `import` frontmatter directives. Do not link it as a markdown resource; the renderer will flag this as a syntax error.
19
+ 4. **Another `.plain` file.** `[Auth Module](auth.plain)` is invalid. To reference another module, use the `require` or `import` frontmatter directives. Do not link it as a markdown resource; turri will flag this as a syntax error.
20
20
 
21
21
  ## Never use a URL or folder path as a *reference* in prose
22
- - The hard constraint above is enforced only on markdown links. A bare URL or folder path in prose (concept body, functional-spec text, implementation reqs, test reqs) passes validation and is fed to the renderer verbatim as part of the spec text
23
- - That is exactly why it is dangerous as a *reference*: the renderer cannot fetch the URL or open the folder, so the model sees only the address, not the content behind it. To a human reader the line looks grounded in a source; to the renderer it is a *ghost dependency*, and the spec silently drifts from whatever the URL actually says
22
+ - The hard constraint above is enforced only on markdown links. A bare URL or folder path in prose (concept body, functional-spec text, implementation reqs, test reqs) passes validation and is fed to the coding agent verbatim as part of the ***plain's text
23
+ - That is exactly why it is dangerous as a *reference*: the coding agent cannot fetch the URL or open the folder, so it sees only the address, not the content behind it. To a human reader the line looks grounded in a source; to the coding agent it is a *ghost dependency*, and the ***plain silently drifts from whatever the URL actually says
24
24
  - A URL or path in prose is fine when the *text itself* is the complete fact:
25
25
  - **Runtime values** the produced software uses — the base URL an integration calls, a database connection path, a CLI argument default, an output directory layout
26
26
  - **Provenance notes** for a snapshotted resource — recording the canonical URL a file saved under `resources/` was fetched from
27
- - Litmus test: "Would the renderer benefit from reading the bytes at this URL / folder?" If yes, save it to a file and link the file. If no (the address itself is the fact), it can stay as plain text
27
+ - Litmus test: "Would the coding agent benefit from reading the bytes at this URL / folder?" If yes, save it to a file and link the file. If no (the address itself is the fact), it can stay as plain text
28
28
 
29
29
  ## Structured protocol artifacts must be linked, never transcribed
30
30
  - JSON Schema, OpenAPI / Swagger, GraphQL SDL, Protobuf `.proto`, Avro / Thrift schemas, XML XSDs, AsyncAPI specs, JSON-RPC method definitions, wire-protocol descriptions, payload examples — anything with a formal machine-readable shape — belongs in a file under `resources/` (or another directory within the project)
31
- - The spec line should describe the *role* of the artifact ("the request body conforms to ...", "the public API surface is defined in ...") rather than its contents
32
- - Reasons: one source of truth (no drift between prose and schema); the renderer and the generated code can both consume the file directly; schema changes show up cleanly as diffs
31
+ - The ***plain line should describe the *role* of the artifact ("the request body conforms to ...", "the public API surface is defined in ...") rather than its contents
32
+ - Reasons: one source of truth (no drift between prose and schema); the coding agent and the generated code can both consume the file directly; schema changes show up cleanly as diffs
33
33
 
34
34
  ```plain
35
35
  ***definitions***
@@ -41,8 +41,9 @@ A linked resource **must not** be any of the following:
41
41
 
42
42
  ***functional specs***
43
43
 
44
- - A :User: can add a :Task: by POSTing :TaskCreateRequest: to the `POST /tasks` endpoint of :TasksAPI:.
45
- - The endpoint responds per :TasksAPI:.
44
+ # add-task
45
+ A :User: can add a :Task: by POSTing :TaskCreateRequest: to the `POST /tasks` endpoint of :TasksAPI:.
46
+ - The endpoint responds per :TasksAPI:.
46
47
  ```
47
48
 
48
49
  ## Each linked resource is referenced from exactly one place
@@ -58,9 +59,11 @@ A linked resource **must not** be any of the following:
58
59
 
59
60
  ***functional specs***
60
61
 
61
- - A :User: can add a :Task: using :TaskModalSpec:.
62
+ # add-task
63
+ A :User: can add a :Task: using :TaskModalSpec:.
62
64
 
63
- - A :User: can edit a :Task: using :TaskModalSpec:.
65
+ # edit-task
66
+ A :User: can edit a :Task: using :TaskModalSpec:.
64
67
  ```
65
68
 
66
69
  ## When to link
@@ -9,7 +9,8 @@ When creating a new `.plain` file or reviewing the structure of a whole file, al
9
9
  ## File skeleton
10
10
  - A `.plain` file is one **module**: optional YAML frontmatter between `---` markers, followed by sections marked with `***section name***` headers
11
11
  - The frontmatter, when present, must be the first thing in the file
12
- - Every content line inside a section must be a `- ` list item — see `bullet-continuation.md`
12
+ - Every content line inside `***definitions***`, `***implementation reqs***`, and `***test reqs***` is a `- ` list item - see `bullet-continuation.md`
13
+ - Every functional spec in `***functional specs***` is a `# <func-id>` heading followed by its body - see `func-specs.md` and `semantic-ids.md`
13
14
  - Every line must be as short and plain as its content allows — see `concise-specs.md`
14
15
 
15
16
  ## Frontmatter fields
@@ -23,21 +24,22 @@ When creating a new `.plain` file or reviewing the structure of a whole file, al
23
24
  | `required_concepts` | Declare concepts an import module expects the importing module to define | `required-concepts.md` |
24
25
 
25
26
  ## Sections
26
- - There are exactly four top-level section markers: `***definitions***`, `***implementation reqs***`, `***test reqs***`, and `***functional specs***`
27
+ - A module has exactly four top-level section markers: `***definitions***`, `***implementation reqs***`, `***test reqs***`, and `***functional specs***`
27
28
  - Each section appears **at most once** per file; all are optional (which ones are allowed depends on the module kind below)
28
- - `***acceptance tests***` is **never a top-level section** — it appears only nested under a single functional spec (see `func-specs.md`)
29
+ - `***conformance tests***` and `***acceptance tests***` never appear in a module, not even nested under a functional spec; they are the two sections of a test plain file (see `test-plain-files.md`)
29
30
  - Write sections in canonical order: `***definitions***` → `***implementation reqs***` → `***test reqs***` → `***functional specs***`
30
31
  - Definitions come first because every concept must be defined before it is referenced
31
- - Functional specs come last because they are rendered incrementally and their nested acceptance tests close the file
32
+ - Functional specs come last because each one builds on those before it
32
33
 
33
34
  ## Module kinds
34
35
  - A **root module** lives at the repository root and carries behavior: it contains `***functional specs***`
35
- - To be renderable, a root module needs at least one functional spec plus the implementation reqs to build it — its own or from an `import`ed module (implementation reqs are not inherited through `requires`). A frontmatter-only file is a valid scaffold, but it cannot be rendered until a functional spec is added
36
+ - To be regenerated, a root module needs at least one functional spec plus the implementation reqs to build it — its own or from an `import`ed module (implementation reqs are not inherited through `requires`). A frontmatter-only file is a valid scaffold, but it cannot be regenerated until a functional spec is added
36
37
  - A **requires module** is a root module that is used by another root module via `requires` directive (see `requires-modules.md`)
37
38
  - An **import module** provides shared content that other modules pull in via `import`: it contains only `***definitions***`, `***implementation reqs***`, and/or `***test reqs***` — never `***functional specs***`, never `requires` (see `import-modules.md`)
39
+ - A **test plain file** is not a module: it holds the scenarios of one functional spec of a root module, its frontmatter `module:` and `func_id:` bind it to that functional spec, it lives in `tests/<module>/`, and it has none of a module's sections (see `test-plain-files.md`)
38
40
 
39
41
  ## Section ownership (where a fact lives)
40
- - The renderer reads each kind of fact **only from its owning section** — a fact placed in the wrong section is silently ignored, not flagged
42
+ - turri reads each kind of fact **only from its owning section** — a fact placed in the wrong section is silently ignored, not flagged
41
43
  - Before writing any requirement, place it by content:
42
44
  - To move facts that are already misplaced, or to split a bullet carrying several kinds of fact, use the `resolve-section-ownership` skill
43
45
 
@@ -47,29 +49,32 @@ When creating a new `.plain` file or reviewing the structure of a whole file, al
47
49
  | HOW the software is built — tech stack, architecture, coding standards — and **everything about `:UnitTests:`** | `***implementation reqs***` | `impl-reqs.md` |
48
50
  | **Everything about `:ConformanceTests:`** — framework, run command, mocking and network policy | `***test reqs***` | `test-reqs.md` |
49
51
  | WHAT the software does — observable, language-agnostic behavior | `***functional specs***` | `func-specs.md` |
50
- | End-to-end workflow verification of one functional spec | nested `***acceptance tests***` | `func-specs.md` |
52
+ | Test scenarios of one functional spec, conformance and acceptance | its test plain file, `tests/<module>/<func-id>.plain` | `test-plain-files.md` |
51
53
 
52
54
  ## Comments
53
- - Lines beginning with `>` are ignored when rendering
55
+ - Lines beginning with `>` are ignored when regenerating
54
56
 
55
57
  ```plain
56
58
  > This is a comment in ***plain.
57
59
  ```
58
60
 
59
- - Comments explain the specification to human authors — never use a comment to carry a requirement the renderer must implement
61
+ - Comments explain the ***plain to human authors — never use a comment to carry a requirement the coding agent must implement
60
62
 
61
63
  ## Liquid templates
62
64
  - `.plain` files are processed with [Liquid](https://shopify.github.io/liquid/) before parsing — the full Liquid syntax (variables, filters, conditionals, loops) is available in any section
63
- - Expansion is **textual** and happens before the specification is interpreted — unlike `import`, which includes another module semantically (see `import-modules.md`)
65
+ - Expansion is **textual** and happens before the ***plain is interpreted — unlike `import`, which includes another module semantically (see `import-modules.md`)
64
66
 
65
67
  ```plain
66
68
  {% assign max_tags = 3 %}
67
69
 
68
70
  ***functional specs***
69
71
 
70
- - A :Widget: can be created with up to {{ max_tags }} tags.
72
+ # create-widget
73
+ A :Widget: can be created with up to {{ max_tags }} tags.
71
74
  {% if max_tags > 1 %}
72
- - A :Widget: can have its tags listed.
75
+
76
+ # list-widget-tags
77
+ A :Widget: can have its tags listed.
73
78
  {% endif %}
74
79
  ```
75
80
 
@@ -99,7 +104,9 @@ import:
99
104
 
100
105
  ***functional specs***
101
106
 
102
- - A :Task: can be added to the :TaskList:.
107
+ # add-task
108
+ A :Task: can be added to the :TaskList:.
103
109
 
104
- - The :TaskList: is displayed.
110
+ # show-task-list
111
+ The :TaskList: is displayed.
105
112
  ```