plain-forge 1.0.19 → 1.0.20

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/README.md CHANGED
@@ -4,7 +4,7 @@
4
4
 
5
5
  # plain-forge
6
6
 
7
- A toolkit for working with [***plain](https://plainlang.org) projects from inside your AI coding agent of choice — Claude Code, Codex, ForgeCode, OpenCode, and any other agent that reads from a standard skills directory. plain-forge ships skills, rules, and docs that turn a conversation into complete `.plain` spec files, then keeps maintaining them across the lifetime of the project. The specs are rendered into production-ready code by the [codeplain](https://codeplain.ai) renderer.
7
+ A toolkit for working with [∗∗∗plain](https://plainlang.org) projects from inside your AI coding agent of choice — Claude Code, Codex, ForgeCode, OpenCode, and any other agent that reads from a standard skills directory. plain-forge ships skills, rules, and docs that turn a conversation into complete `.plain` spec files, then keeps maintaining them across the lifetime of the project. The specs are rendered into production-ready code by the [codeplain](https://codeplain.ai) renderer.
8
8
 
9
9
  ## What plain-forge does
10
10
 
@@ -195,7 +195,7 @@ Hit a bug in the rendered app, a failing test, or behavior that doesn't match wh
195
195
  2. plain-forge applies the fix in the `.plain` file(s) only and summarizes what changed.
196
196
  3. Re-render to regenerate the code (see [Rendering specs](#rendering-specs)).
197
197
 
198
- > **Important:** Never edit generated code under `plain_modules/` or `conformance_tests/` directly — your changes will be overwritten on the next render. Always fix the spec and re-render.
198
+ > **Important:** Never edit generated output under `plain_modules/` directly — neither the code in `plain_modules/<module>/code/` nor the conformance tests in `plain_modules/<module>/tests/`. Your changes will be overwritten on the next render. Always fix the spec and re-render.
199
199
 
200
200
 
201
201
  ## Repository Structure
@@ -13,7 +13,7 @@ When writing or editing an `***implementation reqs***` section in a `.plain` fil
13
13
 
14
14
  ## `:UnitTests:` lives here (hard rule)
15
15
  - **Everything** about `:UnitTests:` goes in `***implementation reqs***` — paths, approach, packages, framework, conventions, fixtures, mocking policy, file layout, naming, lint / static-analysis gates
16
- - `:UnitTests:` are part of the generated codebase (they sit inside `plain_modules/<module>/` alongside the implementation), so requirements that shape them are implementation reqs by definition
16
+ - `:UnitTests:` are part of the generated codebase (they sit inside `plain_modules/<module>/code/` alongside the implementation), so requirements that shape them are implementation reqs by definition
17
17
  - The unit-test generator reads **only** `***implementation reqs***` — anything about `:UnitTests:` placed elsewhere (e.g. `***test reqs***`) is silently ignored
18
18
  - Author each `:UnitTests:` requirement via `add-implementation-requirement` and phrase it in terms of `:UnitTests:` so the partition stays visible at a glance
19
19
 
@@ -75,7 +75,7 @@ mvn test -Dtest='<:UnitTests: package>.**.*Test' checkstyle:check
75
75
 
76
76
  These reqs feed `run_conformance_tests_<lang>`. At minimum, declare:
77
77
 
78
- 1. **`:ConformanceTests:` source location** — where the conformance suite lives in the project (typically a sibling folder, e.g. `conformance_tests/<module>/`); the renderer passes the resolved path as `$2`
78
+ 1. **`:ConformanceTests:` source location** — the generated suite lives under `plain_modules/<module>/tests/`, one folder per functional spec; the renderer passes the resolved path of the folder under test as `$2`
79
79
  2. **`:ConformanceTests:` framework and execution command** — `mvn test --no-transfer-progress`, `pytest`, `npm test`, `go test ./...`, etc., with any flags / profiles the project requires
80
80
  3. **Fully qualified `:ConformanceTests:` package** (or path / pattern) used to scope discovery, if the runner needs one
81
81
  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)
@@ -85,7 +85,7 @@ These reqs feed `run_conformance_tests_<lang>`. At minimum, declare:
85
85
  Author the conformance facts as one or more entries, phrased in terms of `:ConformanceTests:`:
86
86
 
87
87
  ```plain
88
- - :ConformanceTests: of :Implementation: live in `conformance_tests/foo/` and are implemented with JUnit 5 + Maven.
88
+ - :ConformanceTests: of :Implementation: live in `plain_modules/foo/tests/` and are implemented with JUnit 5 + Maven.
89
89
  - The fully qualified package used for :ConformanceTests: discovery is `com.example.integrations.foo.conformance`.
90
90
  - :ConformanceTests: are run via `mvn test --no-transfer-progress`; the host's Surefire plugin must be installed.
91
91
  - :ConformanceTests: run against the live :ProviderName: sandbox — no mocking of provider calls.
@@ -139,7 +139,7 @@ All three scripts are invoked by the `codeplain` renderer with positional argume
139
139
 
140
140
  ## 1. `prepare_environment_<lang>` — copy into the host, then compile
141
141
 
142
- Receives one positional argument: the renderer's build output folder (e.g. `plain_modules/<module>/`).
142
+ Receives one positional argument: the renderer's build output folder (e.g. `plain_modules/<module>/code/`).
143
143
 
144
144
  Purpose: stage the generated code **into the host codebase at the module's package path**, then run the host's install / build so that downstream test projects (specifically the conformance suite) can depend on it from the local dependency cache.
145
145
 
@@ -30,6 +30,9 @@ The embedded integration's `.plain` specs (and the rest of the ***plain project
30
30
  │ ├── resources/
31
31
  │ ├── test_scripts/
32
32
  │ ├── plain_modules/ # generated; gitignored
33
+ │ │ └── <module>/
34
+ │ │ ├── code/ # implementation + unit tests
35
+ │ │ └── tests/ # conformance tests, per functional spec
33
36
  │ └── config.yaml
34
37
  └── src/ # host source tree (Java, Python, etc.)
35
38
  ```
@@ -117,7 +120,7 @@ The renderer reads the directives from the spec and the shapes from the linked s
117
120
 
118
121
  ## Test-script wiring — copy into the host, run tests there
119
122
 
120
- Embedded integrations are tested **inside the host codebase itself**. The prepare and unit-test scripts copy the renderer's output (`$1`, i.e. `plain_modules/<module>/`) into the host's source tree at the module's package path, then compile / test the host project in place. Only the conformance script 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
+ Embedded integrations are tested **inside the host codebase itself**. The prepare and unit-test 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. Only the conformance script 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.
121
124
 
122
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.
123
126
 
@@ -8,7 +8,7 @@ When creating or editing a `.plain` file that uses `requires`, always follow the
8
8
 
9
9
  ## What requires does
10
10
  - `requires` establishes a **build ordering** — the required module is built before the current one
11
- - The required module's generated code (`plain_modules/<required_module>`) is copied as the starting point
11
+ - The required module's generated code (`plain_modules/<required_module>/code`) is copied as the starting point
12
12
  - The required module's `***functional specs***` become visible as **previous functional specs** — this property **is transitive**
13
13
  - Only `exported_concepts` from the required module are available — not its full definitions — and this property **is not transitive**
14
14
 
@@ -14,7 +14,7 @@ When writing or editing a `***test reqs***` section in a `.plain` file, always f
14
14
 
15
15
  ## `:ConformanceTests:` lives here (hard rule)
16
16
  - **Everything** about `:ConformanceTests:` goes in `***test reqs***` — paths, approach, packages, framework, execution command, mocking / network policy, fixtures, pass criteria, environment prerequisites
17
- - `:ConformanceTests:` live outside the generated codebase (typically in a separate project under `conformance_tests/<module>/`), so requirements that shape them belong in test reqs by definition
17
+ - `:ConformanceTests:` live outside the generated codebase, in their own tree under `plain_modules/<module>/tests/` (one folder per functional spec), so requirements that shape them belong in test reqs by definition
18
18
  - The conformance-test generator reads **only** `***test reqs***` — anything about `:ConformanceTests:` placed elsewhere (e.g. `***implementation reqs***`) is silently ignored
19
19
  - Author each `:ConformanceTests:` requirement via `add-test-requirement` and phrase it in terms of `:ConformanceTests:` so the partition stays visible at a glance
20
20
  - Conformance testing run scripts should be linked here as a linked resource
@@ -16,7 +16,7 @@ Always use the skill `load-plain-reference` to retrieve the ***plain syntax rule
16
16
  `requires` establishes a build ordering between modules. The required module is built **before** the current one. This does not necessarily mean the current module extends or depends on the required module's code — it may be completely independent. The `requires` relationship simply ensures the build order is correct.
17
17
 
18
18
  When this module is rendered:
19
- - The required module's generated code (`plain_modules/<required_module>`) is copied as the starting point.
19
+ - The required module's generated code (`plain_modules/<required_module>/code`) is copied as the starting point.
20
20
  - The required module's `***functional specs***` become visible as **previous functional specs**.
21
21
  - Only `exported_concepts` from the required module are available (not its full definitions).
22
22
 
@@ -20,11 +20,11 @@ Always use the skill `load-plain-reference` to retrieve the ***plain syntax rule
20
20
 
21
21
  ## Guiding Principle
22
22
 
23
- Generated code in `plain_modules/` and `conformance_tests/` is **read-only** — it exists solely as evidence for diagnosis. All fixes are applied exclusively to the `.plain` spec files. The workflow is: observe → read generated code → trace to spec → fix the spec.
23
+ Generated code in `plain_modules/<module>/code/` and `plain_modules/<module>/tests/` is **read-only** — it exists solely as evidence for diagnosis. All fixes are applied exclusively to the `.plain` spec files. The workflow is: observe → read generated code → trace to spec → fix the spec.
24
24
 
25
25
  ## Input
26
26
 
27
- 1. **The module name** — identifies the `plain_modules/<module_name>/` directory and the corresponding `.plain` file(s).
27
+ 1. **The module name** — identifies the `plain_modules/<module_name>/` directory (`code/` and `tests/`) and the corresponding `.plain` file(s).
28
28
  2. **The user's observation** — what is wrong? This can be a bug description, a screenshot, a test failure, an error message, or a general "this doesn't work right."
29
29
  3. **Optional: a specific functional spec** — if the user suspects a particular spec, start there. Otherwise, investigate broadly.
30
30
 
@@ -38,7 +38,7 @@ Generated code in `plain_modules/` and `conformance_tests/` is **read-only** —
38
38
 
39
39
  ## Phase 2 — Investigate the Generated Code
40
40
 
41
- Read files in `plain_modules/<module_name>/` to understand what the renderer produced. **Do not modify any generated files.**
41
+ Read files in `plain_modules/<module_name>/code/` to understand what the renderer produced. **Do not modify any generated files.**
42
42
 
43
43
  ### 2a. Narrow the search
44
44
 
@@ -60,7 +60,7 @@ For each relevant file:
60
60
  ### 2c. Check conformance tests and unit tests (if relevant)
61
61
 
62
62
  If the bug manifests as a test failure:
63
- 1. Read the failing test in `conformance_tests/<module_name>/` or `plain_modules/<module_name>/test/`.
63
+ 1. Read the failing test — conformance tests in `plain_modules/<module_name>/tests/<functionality>/`, unit tests inside `plain_modules/<module_name>/code/`.
64
64
  2. Understand what the test expects vs. what the implementation does.
65
65
  3. Determine whether the test expectation is correct (matches the spec) or incorrect (doesn't match the spec).
66
66
 
@@ -164,7 +164,7 @@ revise the new spec
164
164
  ## Common Pitfalls
165
165
 
166
166
  ### Fixing the code instead of the spec
167
- Never modify files in `plain_modules/` or `conformance_tests/`. Even if the fix is obvious in the code, the change must be made in the `.plain` file so it persists across re-renders.
167
+ Never modify files in `plain_modules/<module>/code/` or `plain_modules/<module>/tests/`. Even if the fix is obvious in the code, the change must be made in the `.plain` file so it persists across re-renders.
168
168
 
169
169
  ### Treating symptoms instead of root causes
170
170
  If the user says "the button is in the wrong place," don't just add positioning detail. Investigate why the renderer placed it there — the root cause might be a missing layout spec, an ambiguous screen description, or a conflict with another spec.
@@ -124,4 +124,4 @@ Once the initial specs exist, the user will return with new features. Use the `a
124
124
  - Applicable language rules and operational references: `load-plain-reference`.
125
125
  - Spec-editing skills live in `.claude/skills/`.
126
126
  - Templates go in `template/`, but import paths omit the `template/` prefix. Resources go in `resources/`.
127
- - Generated code lands in `plain_modules/` (read-only, never edit). Test scripts live in `test_scripts/`.
127
+ - Generated code lands in `plain_modules/<module>/code/` and generated conformance tests in `plain_modules/<module>/tests/` (both read-only, never edit). Test scripts live in `test_scripts/`.
@@ -43,7 +43,7 @@ Everything else — toolchain check, build staging, dependency isolation, exit c
43
43
 
44
44
  ### Why this split exists
45
45
 
46
- The conformance runner is invoked **once per functional spec** by the renderer. Each functional spec in a module has its own `conformance_tests/<module>/<spec>/` folder, and after the renderer finishes generating code for a new spec, it runs the conformance tests of **every previous spec** in the same module to detect regressions. For a module with N functional specs, this script is called **on the order of N times per render** — not once per render.
46
+ The conformance runner is invoked **once per functional spec** by the renderer. Each functional spec in a module has its own `plain_modules/<module>/tests/<spec>/` folder, and after the renderer finishes generating code for a new spec, it runs the conformance tests of **every previous spec** in the same module to detect regressions. For a module with N functional specs, this script is called **on the order of N times per render** — not once per render.
47
47
 
48
48
  That per-spec invocation pattern is what makes the install step expensive. A naive runner that does `pip install` / `npm ci` / `mvn install -DskipTests` / `cargo build` on every invocation pays the install cost N times per render. For anything beyond a toy project, that cost dominates wall-clock time.
49
49
 
@@ -123,7 +123,7 @@ A conformance script has **two** read-only inputs: the source build folder (`$1`
123
123
 
124
124
  Why each input is read-only:
125
125
 
126
- - **`$1` (build folder)** is shared with the renderer (`plain_modules/...` by default) and downstream tooling. Writing into it corrupts the renderer's view of "what was generated" and breaks subsequent renders. The whole point of staging into the system temp directory is so the source folder stays a clean, reproducible artifact of the render.
126
+ - **`$1` (build folder)** is shared with the renderer (`plain_modules/<module>/code` by default) and downstream tooling. Writing into it corrupts the renderer's view of "what was generated" and breaks subsequent renders. The whole point of staging into the system temp directory is so the source folder stays a clean, reproducible artifact of the render.
127
127
  - **`$2` (conformance tests folder)** is the user's authored test source — typically checked into version control. Writing into it pollutes the working tree, churns git status, and (with frameworks that auto-discover) can make subsequent runs pick up generated files as if they were tests.
128
128
 
129
129
  If you find yourself about to issue any command whose `cwd` is `$1` or `$2`, or whose target path starts with `$1/` or `$2/`, **stop**. Either move the operation into `<temp>/<lang>_<basename>`, or you're doing something the script must not do.
@@ -27,7 +27,7 @@ When this script exists for a project, the corresponding conformance script's ow
27
27
 
28
28
  ### Why this script exists at all (the structural reason)
29
29
 
30
- The conformance test runner is invoked **once per functional spec** by the renderer — not once per render. Each functional spec in a module has its own `conformance_tests/<module>/<spec>/` folder, and after the renderer finishes generating code for a new spec, it runs the conformance tests of **every previous spec** in the same module to detect regressions. For a module with N functional specs, the conformance script is invoked roughly N times on every render.
30
+ The conformance test runner is invoked **once per functional spec** by the renderer — not once per render. Each functional spec in a module has its own `plain_modules/<module>/tests/<spec>/` folder, and after the renderer finishes generating code for a new spec, it runs the conformance tests of **every previous spec** in the same module to detect regressions. For a module with N functional specs, the conformance script is invoked roughly N times on every render.
31
31
 
32
32
  Without a prepare script, every one of those N invocations does the full dependency install (Python venv + `pip install`, full Maven dependency tree, `npm ci`, `cargo build`, ...) from scratch. That cost — paid N times per render — dominates the wall-clock time of rendering a non-trivial project.
33
33
 
@@ -103,7 +103,7 @@ The source build folder passed in as `$1` is **input only**. Prepare reads from
103
103
  - pre-build into it (every compile output — `target/`, `build/`, `dist/`, native binaries, generated sources — lives inside `<temp>/<lang>_<basename>`),
104
104
  - create logs, caches, or temp files inside it.
105
105
 
106
- The build folder is shared with the renderer (`plain_modules/...` by default) and with the conformance script, which staging-checks the working folder via `if [ ! -d "/tmp/<lang>_$(basename "$1")" ]` and expects `$1` itself to look the same as it did right after rendering. Writing into `$1` corrupts the renderer's view of "what was generated", churns git status if the project commits `$1`, and (if the conformance script ever does an `rm -rf $1` during its own setup) silently destroys work prepare did.
106
+ The build folder is shared with the renderer (`plain_modules/<module>/code` by default) and with the conformance script, which staging-checks the working folder via `if [ ! -d "/tmp/<lang>_$(basename "$1")" ]` and expects `$1` itself to look the same as it did right after rendering. Writing into `$1` corrupts the renderer's view of "what was generated", churns git status if the project commits `$1`, and (if the conformance script ever does an `rm -rf $1` during its own setup) silently destroys work prepare did.
107
107
 
108
108
  The whole point of staging into `<temp>/<lang>_<basename>` is so the source build folder stays a clean, reproducible artifact of the render and no build debris lands inside the user's project. Every dependency, every compiled class, every binary, every cache must land inside the working folder — because that is exactly what the conformance script's activate-only variant attaches to.
109
109
 
@@ -47,7 +47,7 @@ The source build folder passed in as `$1` is **input only**. The script must nev
47
47
  - run the test command from inside it (every test command runs from inside `<temp>/<lang>_<basename>` after the `cd` in step 5),
48
48
  - create logs, caches, build outputs, or temp files inside it.
49
49
 
50
- The build folder is shared with the renderer (`plain_modules/...` by default) and downstream tooling. Writing into it corrupts the renderer's view of "what was generated" and breaks subsequent renders. Every write must go into `<temp>/<lang>_<basename>` — the whole point of staging into the system temp directory is so the source build folder stays a clean, reproducible artifact of the render and no build debris is left inside the user's project.
50
+ The build folder is shared with the renderer (`plain_modules/<module>/code` by default) and downstream tooling. Writing into it corrupts the renderer's view of "what was generated" and breaks subsequent renders. Every write must go into `<temp>/<lang>_<basename>` — the whole point of staging into the system temp directory is so the source build folder stays a clean, reproducible artifact of the render and no build debris is left inside the user's project.
51
51
 
52
52
  If you find yourself about to issue any command whose `cwd` is the source folder, or whose target path starts with `$1/`, **stop**. Either move the operation into `<temp>/<lang>_<basename>`, or you're doing something the script must not do.
53
53
 
@@ -59,8 +59,7 @@ These keys reflect choices made in Phase 3 of `forge-plain` and are the bread an
59
59
  | `test-script-timeout` | int (seconds) | `120` | Include only when the user explicitly raised/lowered the default. |
60
60
  | `template-dir` | path (string) | — | Include whenever the project has an `import` module or a custom template directory (e.g. `template/`). Required for projects with shared templates. |
61
61
  | `logging-config-path` | path (string) | `logging_config.yaml` | Points at a **separate** YAML file consumed by Python's `logging.config.dictConfig`. This is the only knob that lets the user actually change log **levels** for the renderer and its dependencies. See [Logging configuration](#logging-configuration) below. Include the key explicitly whenever the project ships a non-default logging config; leave it out only when the user is happy with the renderer's defaults (`INFO` root, `WARNING` for `git`, `ERROR` for `transitions`). |
62
- | `conformance-tests-folder` | string | `conformance_tests` | Include only when the user picked a non-default folder name. |
63
- | `build-folder` | string | `plain_modules` | Include only when the user picked a non-default folder name. Must differ from `build-dest`. |
62
+ | `build-folder` | string | `plain_modules` | Include only when the user picked a non-default folder name. Must differ from `build-dest`. The renderer writes `<build-folder>/<module>/code` (implementation + unit tests) and `<build-folder>/<module>/tests` (conformance tests, one folder per functional spec). |
64
63
  | `build-dest` | string | `dist` | **Always include with the value `dist`.** This skill pins the copy destination explicitly so every project's `config.yaml` has the same, predictable target folder for the post-render copy. Even though `dist` matches the renderer's default, we still write it out so the choice is visible in the file and protected against future default changes. Must differ from `build-folder`. |
65
64
  | `base-folder` | string | — | Include when the user wants build output rooted somewhere other than the project root. |
66
65
 
@@ -72,7 +71,7 @@ These are useful but the defaults are almost always fine. Only include them when
72
71
  |---|---|---|---|
73
72
  | `copy-build` | bool | `true` | The renderer copies the rendered code to `build-dest` after a successful render. Set to `false` only when the user doesn't want this. |
74
73
  | `copy-conformance-tests` | bool | `false` | Requires `conformance-tests-script` to also be set. |
75
- | `conformance-tests-dest` | string | `dist_conformance_tests` | Target folder for the conformance-test copy. Must differ from `conformance-tests-folder`. |
74
+ | `conformance-tests-dest` | string | `dist_conformance_tests` | Target folder for the post-render copy of `plain_modules/<module>/tests`. Must differ from `build-folder`. |
76
75
  | `log-to-file` | bool | `true` | Disable only when the user explicitly does not want a log file. Controls whether logs are mirrored to disk — it does **not** set the log level (that's `logging-config-path`'s job). |
77
76
  | `log-file-name` | string | `codeplain.log` | If `log-to-file` is `false`, this key must be left out. A relative value set here resolves against the config file's directory; when the key is left out entirely, the default resolves against the `.plain` file's directory. |
78
77
  | `render-machine-graph` | bool | `false` | Include only when the user wants the state-machine graph rendered. |
@@ -13,7 +13,7 @@ description: >-
13
13
 
14
14
  # CODEPLAIN_CLI_REFERENCE.md
15
15
 
16
- `codeplain` is the CLI that renders `***plain` specification files into production-ready code. It reads a `.plain` module (and everything it `import`s / `requires`), calls the codeplain API, and writes generated code under `plain_modules/` and conformance tests under `conformance_tests/`. The `.plain` specs are the source of truth; the generated code is a read-only artifact.
16
+ `codeplain` is the CLI that renders `***plain` specification files into production-ready code. It reads a `.plain` module (and everything it `import`s / `requires`), calls the codeplain API, and writes generated code under `plain_modules/<module>/code/` and conformance tests under `plain_modules/<module>/tests/`. The `.plain` specs are the source of truth; the generated code is a read-only artifact.
17
17
 
18
18
  This reference covers the CLI surface only. For the `***plain` language itself use `load-plain-reference`; to supervise a live render use `run-codeplain`; to assemble or validate `config.yaml` use `init-config-file` / `plain-healthcheck`.
19
19
 
@@ -75,7 +75,7 @@ A path's meaning depends on **where it was written**:
75
75
  ### Folders
76
76
 
77
77
  - **`--base-folder BASE_FOLDER`** — base folder for the build files.
78
- - **`--build-folder BUILD_FOLDER`** — folder for build files (generated code lands under here, per module: `plain_modules/<module>/`).
78
+ - **`--build-folder BUILD_FOLDER`** — folder for build files (generated code lands under here, per module: `plain_modules/<module>/code/` and `plain_modules/<module>/tests/`).
79
79
  - **`--template-dir TEMPLATE_DIR`** — path to a custom template directory. Templates are searched in this order: 1) the directory containing the `.plain` file, 2) this custom template directory, 3) the built-in `standard_template_library` directory. In `config.yaml` the equivalent key is `template_dir` — set it whenever the project has import modules or templates.
80
80
 
81
81
  ### Test-script wiring
@@ -126,8 +126,8 @@ A path's meaning depends on **where it was written**:
126
126
 
127
127
  Generated artifacts (gitignored, read-only — never edit):
128
128
 
129
- - `plain_modules/<module>/` — the generated project for each `.plain` spec (implementation + unit tests).
130
- - `conformance_tests/<module>/<functionality>/` — generated conformance tests, one subfolder per functionality.
129
+ - `plain_modules/<module>/code/` — the generated project for each `.plain` spec (implementation + unit tests).
130
+ - `plain_modules/<module>/tests/<functionality>/` — generated conformance tests, one subfolder per functionality.
131
131
  - `codeplain.log` — the per-run log (overwritten each run).
132
132
 
133
133
  When a run ends, `codeplain` writes a result banner (to the log, and to the terminal outside headless mode):
@@ -15,8 +15,9 @@ those specifications.
15
15
  *.plain # Root specification modules
16
16
  template/*.plain # Reusable import modules
17
17
  resources/ # Linked text artifacts such as schemas and fixtures
18
- plain_modules/ # Generated implementation and unit tests
19
- conformance_tests/ # Generated conformance tests, grouped by module and functionality
18
+ plain_modules/ # Generated output, one directory per module
19
+ <module>/code/ # implementation and unit tests
20
+ <module>/tests/ # conformance tests, one folder per functional spec
20
21
  test_scripts/ # Unit, environment-preparation, and conformance runners
21
22
  config.yaml # codeplain CLI configuration
22
23
  ```
@@ -18,7 +18,7 @@ and debugging skills rather than editing generated output.
18
18
 
19
19
  ## Generated artifacts are read-only
20
20
 
21
- Everything under `plain_modules/` and `conformance_tests/` is generated. It may be read, executed,
21
+ Everything under `plain_modules/` is generated — both `<module>/code/` and `<module>/tests/`. It may be read, executed,
22
22
  and debugged, but never edited directly. Apply fixes as follows:
23
23
 
24
24
  - Behavior or implementation/unit-test guidance: edit definitions, functional specs, or
@@ -37,7 +37,7 @@ Test scripts are renderer entry points as well as developer utilities:
37
37
  - `prepare_environment_<lang>` optionally prepares a reusable system-temporary environment for
38
38
  conformance testing once per render.
39
39
  - `run_conformance_tests_<lang>` receives the generated build folder and one functionality's
40
- conformance-test folder. It may run repeatedly during a render.
40
+ conformance-test folder (`plain_modules/<module_name>/tests/<functionality>`). It may run repeatedly during a render.
41
41
 
42
42
  Invoke the corresponding `implement-*-script` skill when creating or changing a test script. Those
43
43
  skills own exact staging, cleanup, dependency-installation, shell, and exit-code contracts.
@@ -47,10 +47,10 @@ skills own exact staging, cleanup, dependency-installation, shell, and exit-code
47
47
  Run project scripts from the repository root, using the filenames configured for that project:
48
48
 
49
49
  ```bash
50
- ./test_scripts/run_unittests.sh plain_modules/<module_name>
51
- ./test_scripts/prepare_environment.sh plain_modules/<module_name>
50
+ ./test_scripts/run_unittests.sh plain_modules/<module_name>/code
51
+ ./test_scripts/prepare_environment.sh plain_modules/<module_name>/code
52
52
  ./test_scripts/run_conformance_tests.sh \
53
- plain_modules/<module_name> conformance_tests/<module_name>/<functionality>
53
+ plain_modules/<module_name>/code plain_modules/<module_name>/tests/<functionality>
54
54
  ```
55
55
 
56
56
  The renderer resolves script arguments to absolute paths. Test scripts must treat input directories
@@ -90,7 +90,7 @@ Treat the dry-run as a hard gate: the healthcheck **only passes** when every top
90
90
  Iterate until it passes:
91
91
 
92
92
  1. Read the error output. If the first run was not verbose, immediately re-run with `--verbose`. Identify the offending `.plain` file, the line (if reported), and the kind of issue: missing concept, syntax error, cyclic definition, complexity violation (`Functional spec too complex!`), conflicting reqs, missing template, broken `import`/`requires`, missing config field, etc.
93
- 2. Fix only the `.plain` files (or the relevant `config.yaml` / template) using the appropriate edit skill — `add-concept`, `add-functional-spec`, `add-functional-specs`, `add-implementation-requirement`, `resolve-spec-conflict`, `break-down-func-spec`, `consolidate-concepts`, or an inline edit. **Never** modify generated code under `plain_modules/` or `conformance_tests/`.
93
+ 2. Fix only the `.plain` files (or the relevant `config.yaml` / template) using the appropriate edit skill — `add-concept`, `add-functional-spec`, `add-functional-specs`, `add-implementation-requirement`, `resolve-spec-conflict`, `break-down-func-spec`, `consolidate-concepts`, or an inline edit. **Never** modify generated code under `plain_modules/<module>/code/` or `plain_modules/<module>/tests/`.
94
94
  3. If you are uncertain about ***plain syntax for the failing construct, re-load `load-plain-reference` before fixing.
95
95
  4. Re-run the same `codeplain <top_module>.plain --dry-run …` command with the same flags. Repeat until it exits successfully.
96
96
 
@@ -21,7 +21,7 @@ A **supervisor** around a live `codeplain` render. The renderer itself is the co
21
21
  1. Launches `codeplain <module>.plain` (or attaches to an in-flight run).
22
22
  2. Watches three signal sources in a tight loop, in priority order:
23
23
  - **`codeplain.log`** — the primary signal. Everything the renderer is doing, deciding, retrying, or failing at gets written here. This is the file you stare at.
24
- - the generated **code outputs** under `plain_modules/<module>/` and `conformance_tests/<module>/` — a secondary signal, used to corroborate or contradict what the log says.
24
+ - the generated **code outputs** under `plain_modules/<module>/` and `plain_modules/<module>/tests/` — a secondary signal, used to corroborate or contradict what the log says.
25
25
  - the **process / TUI** itself (alive? exited? exit code?) — a tertiary signal, used only to know whether to keep monitoring.
26
26
  3. Surfaces what is happening to the user in plain English, in near-real-time.
27
27
  4. On pathology, stops the renderer (with user approval), hands off to the right edit skill, and resumes from the last completed functionality.
@@ -76,7 +76,7 @@ Before launching anything:
76
76
  - **What "pass" means**, exit-code-wise. Some scripts combine multiple checks (e.g. `unit_testing_typescript.sh` here runs `tsc --noEmit` *and* `vitest`, and its final exit code is the worst of the two — so a type error alone fails the unit-test gate).
77
77
  - **What "fail" means**, including any failures that the script deliberately downgrades to warnings (e.g. `prepare_environment_typescript.sh` here treats a failed `npm run build` as a non-fatal warning). Failures the script swallows are failures the renderer will **never see**, so a spec defect that depends on them will silently slip through.
78
78
  - **Where the scripts materialize their working environment** (current-convention scripts stage into the system temp directory, e.g. `/tmp/typescript_<build_folder_basename>/`; older scripts used project-local folders like `.tmp/typescript_<build_folder>/`, `build/`, `target/`). This is where the renderer's iteration actually runs; if you need to look at what is breaking, this is the directory — not `plain_modules/<module>/` directly.
79
- - **How conformance tests are discovered and staged** (glob, naming convention, alias setup, etc.). Some scripts stage tests into the source tree (this repo flattens `conformance_tests/<module>/<fname>/` into `src/<module>/`); some run them in place; some require a specific file extension. This tells you exactly which conformance test file is in play for a given functionality.
79
+ - **How conformance tests are discovered and staged** (glob, naming convention, alias setup, etc.). Some scripts stage tests into the source tree (this repo flattens `plain_modules/<module>/tests/<fname>/` into `src/<module>/`); some run them in place; some require a specific file extension. This tells you exactly which conformance test file is in play for a given functionality.
80
80
  - **Toolchain prerequisites** the script asserts (Node version, Python version, specific binaries). If any are missing on the user's machine, stop now and tell them — the renderer will burn credits on phantom failures otherwise.
81
81
 
82
82
  Keep this synthesis short (5–10 bullets, project-specific). It is the single most important reference for the classifier in [Spec-deviation classification](#spec-deviation-classification). The healthcheck only checks that the scripts *exist* and are *referenced correctly*; this step is the only place that reads what they actually *do*.
@@ -132,7 +132,7 @@ All patterns below are taken from the actual codeplain runtime. Treat them as th
132
132
  | `Implementing conformance tests...` / `Implementing test requirements:` | Conformance phase starting for the current functionality. | Routine. |
133
133
  | `Running testing environment preparation script .* for build folder` | `prepare-environment-script` is running. | Routine. |
134
134
  | `\[#79FC96\]All Testing Environment Preparation scripts have passed successfully.\[/#79FC96\]` | Env prep green. | Routine. |
135
- | `Running conformance tests script .* for conformance_tests/<m>/<fname> \(functionality <N> in module <m>\)` | Conformance tests starting for functionality `<N>`. | Note `<fname>` — that is the on-disk folder where the latest conformance test lives. |
135
+ | `Running conformance tests script .* for plain_modules/<m>/tests/<fname> \(functionality <N> in module <m>\)` | Conformance tests starting for functionality `<N>`. | Note `<fname>` — that is the on-disk folder where the latest conformance test lives. |
136
136
  | `Running conformance tests attempt <N>.` | Conformance retry loop. **Watch `<N>` carefully.** | Increment `ATTEMPT_COUNTER = <N>`. At `>=5`, trigger Pathology A. |
137
137
  | `Fixing conformance test for functionality <N> in module <m>.` | Renderer is patching its own conformance test between attempts. | On its own, routine. Combined with climbing `<N>`, smoking gun for Pathology A. |
138
138
  | `Functional spec too complex!` | Single spec implies >200 LOC. Renderer aborts. | Pathology B. |
@@ -199,7 +199,7 @@ What to extract from the framework output — stay framework-agnostic, look for
199
199
  - **Individual test names that failed**: lines beginning with `×`, `FAIL`, `✗`, `× should ...`, `FAIL src/.../foo.test.ts > <name>`. The *same* test failing across attempts is the smoking gun for an under-specified spec.
200
200
  - **Assertion bodies**: `expected X, got Y`. The values reveal exactly what the test expects vs what the implementation produced — the cleanest input to the classifier below.
201
201
  - **Compile / type errors** (when the script wraps a type-checker): `error TS<NNNN>`, `cannot find module`, `is not assignable to`. A unit-test gate that keeps re-failing on the same type error means the implementation req or the type-level spec is wrong, not the code.
202
- - **Stack traces**: which file under the prepared working folder threw, and at which line. Map the file back to `plain_modules/<module>/src/...` to find the code the spec is supposed to be governing.
202
+ - **Stack traces**: which file under the prepared working folder threw, and at which line. Map the file back to `plain_modules/<module>/code/...` to find the code the spec is supposed to be governing.
203
203
  - **"No test files found" / "No tests collected"**: the script ran but discovered nothing. Almost always a path/glob mismatch in the staged working folder, not a spec problem. Treat as Pathology F.
204
204
 
205
205
  Always set the logs to be verbose in `config.yaml`.
@@ -232,8 +232,8 @@ The inputs to the classifier are:
232
232
  1. The **spec** that governs the failing functionality — the functional spec and its acceptance test(s) in the `.plain` file.
233
233
  2. The **test-script output** for the most recent attempt, from `codeplain.log` per the section above.
234
234
  3. The renderer's own **`.memory/conformance_test_memory/<id>.json`** for this functionality (when present).
235
- 4. The newest **conformance test file** the renderer just edited under `conformance_tests/<module>/<fname>/`.
236
- 5. The newest **implementation file(s)** the renderer just edited under `plain_modules/<module>/`.
235
+ 4. The newest **conformance test file** the renderer just edited under `plain_modules/<module>/tests/<fname>/`.
236
+ 5. The newest **implementation file(s)** the renderer just edited under `plain_modules/<module>/code/`.
237
237
 
238
238
  With these in hand, place the current iteration into one of four buckets:
239
239
 
@@ -320,15 +320,15 @@ If the process is gone but you haven't yet seen a `✓` or `✗` banner in the l
320
320
 
321
321
  This is a secondary signal — it confirms what the log already said. Don't go on a fishing trip here.
322
322
 
323
- For the current module, the renderer drops generated code into `plain_modules/<module>/` and conformance tests into `conformance_tests/<module>/<functionality_name>/`. Each pass, inspect what is new:
323
+ For the current module, the renderer drops generated code into `plain_modules/<module>/` and conformance tests into `plain_modules/<module>/tests/<functionality_name>/`. Each pass, inspect what is new:
324
324
 
325
- - New files under `plain_modules/<module>/src/` confirm the functionality is being implemented.
326
- - New folders under `conformance_tests/<module>/` confirm the functionality has reached the conformance phase.
325
+ - New files under `plain_modules/<module>/code/` confirm the functionality is being implemented.
326
+ - New folders under `plain_modules/<module>/tests/` confirm the functionality has reached the conformance phase.
327
327
  - Files that were rewritten between passes are the renderer's "fix" attempts — diffing them across iterations is what reveals whether the renderer is converging or thrashing.
328
328
 
329
329
  Read **only the newest** file(s) (the ones that changed since last pass), and read them to understand *what the renderer chose to do*, not just *that* it did something. This is the first line of defense against "the spec is ambiguous and the renderer guessed wrong" — you can often spot the wrong guess in the generated code long before tests fail.
330
330
 
331
- Generated code under `plain_modules/` and `conformance_tests/` is **read-only**. Never edit it. Edits go in the `.plain` files.
331
+ Generated code under `plain_modules/<module>/code/` and `plain_modules/<module>/tests/` is **read-only**. Never edit it. Edits go in the `.plain` files.
332
332
 
333
333
  ### 1d. Cadence
334
334
 
@@ -384,7 +384,7 @@ Symptom: log shows the unit-test or conformance-test **script** itself failing (
384
384
 
385
385
  ```
386
386
  ImportError: Start directory is not importable
387
- /Users/x/project//Users/x/project/conformance_tests/module/spec
387
+ /Users/x/project//Users/x/project/plain_modules/module/tests/spec
388
388
  ```
389
389
 
390
390
  The render then gets stuck "fixing" tests that can never pass. The fix is never a spec edit: stop the render and regenerate the affected scripts via the matching `implement-*-testing-script` skill (current convention: system-temp working folder from `basename "$1"`, `$2` resolved to absolute before any `cd`).
@@ -434,7 +434,7 @@ If the fix required regenerating a module's earlier output (e.g. a definition ch
434
434
  When the renderer exits successfully:
435
435
 
436
436
  1. Read the success banner from the log: render id, generated code folder, functionalities count, used credits, render time. Report it to the user verbatim — these are the numbers they actually care about.
437
- 2. List the top-level files/folders that appeared (or changed) under `plain_modules/<module>/` and `conformance_tests/<module>/` so the user knows where to look.
437
+ 2. List the top-level files/folders that appeared (or changed) under `plain_modules/<module>/` and `plain_modules/<module>/tests/` so the user knows where to look.
438
438
  3. Remind the user of the **side-channel commands** they may want to run themselves, based on the config:
439
439
  - `./test_scripts/<unittests-script>` for unit tests,
440
440
  - `./test_scripts/<prepare-environment-script>` to set up the test env,
@@ -446,7 +446,7 @@ When the renderer exits with a failure that wasn't intercepted by Phase 2 (e.g.
446
446
 
447
447
  ## Anti-patterns
448
448
 
449
- - **Editing generated code to "fix" a render.** Never. Generated files under `plain_modules/` and `conformance_tests/` exist as evidence only; they are overwritten every render. All fixes go in `.plain` files.
449
+ - **Editing generated code to "fix" a render.** Never. Generated files under `plain_modules/<module>/code/` and `plain_modules/<module>/tests/` exist as evidence only; they are overwritten every render. All fixes go in `.plain` files.
450
450
  - **Reading the test scripts' source code to understand what's failing.** Don't. The scripts run real frameworks (vitest, pytest, etc.) and the *output* of those frameworks — captured in `codeplain.log` and in `.memory/conformance_test_memory/*.json` — is what tells you what is failing and why. Reading the script source tells you nothing the log doesn't already say.
451
451
  - **Treating every conformance loop as under-specification.** Run the classifier. The renderer sometimes drifts (Bucket 3) by silently weakening its own tests; that is the opposite problem and the fix is different.
452
452
  - **Re-reading the whole log on every pass.** It grows. Track the byte offset and read only what was appended since the last pass. Full reads are reserved for "I just saw an error and need backstory."
@@ -480,4 +480,4 @@ When the renderer exits with a failure that wasn't intercepted by Phase 2 (e.g.
480
480
  - [ ] Spec fixes were delegated to the right edit skill, not done inside this skill
481
481
  - [ ] Resumes use `--render-from <N+1>` (or `--render-range`) — `--force-render` only with user approval
482
482
  - [ ] On success, final render id / credits / time were reported and side-channel test commands were surfaced
483
- - [ ] No generated code under `plain_modules/` or `conformance_tests/` was modified
483
+ - [ ] No generated code under `plain_modules/<module>/code/` or `plain_modules/<module>/tests/` was modified
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "plain-forge",
3
- "version": "1.0.19",
3
+ "version": "1.0.20",
4
4
  "description": "Conversational spec-writing tool for ***plain specification language",
5
5
  "type": "module",
6
6
  "engines": {
@@ -33,7 +33,7 @@
33
33
  "scripts": {
34
34
  "build": "tsx bin/forge-build.ts",
35
35
  "clean": "tsx bin/forge-build.ts --clean",
36
- "test": "node --test \"test/**/*.test.mjs\""
36
+ "test": "node --test test/*.test.mjs"
37
37
  },
38
38
  "devDependencies": {
39
39
  "@types/node": "^22.10.0",