plain-forge 1.0.6 → 1.0.7

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
@@ -182,7 +182,6 @@ plain-forge keeps a single canonical source of truth under `forge/` and uses tin
182
182
  forge/ # canonical, runtime-neutral content
183
183
  skills/ # all skills used during spec writing
184
184
  rules/ # workspace rules for spec validation
185
- docs/ # shared docs (PLAIN_REFERENCE.md, etc.)
186
185
 
187
186
  runtimes/ # per-runtime adapters
188
187
  claude/
@@ -12,6 +12,12 @@ When writing or editing an `***implementation reqs***` section in a `.plain` fil
12
12
  - Observable behavior (endpoints, business rules, user-facing features) belongs in `***functional specs***`
13
13
  - Internal structure, technology choices, and coding guidance belong here
14
14
 
15
+ ## `:UnitTests:` lives here (hard rule)
16
+ - **Everything** about `:UnitTests:` goes in `***implementation reqs***` — paths, approach, packages, framework, conventions, fixtures, mocking policy, file layout, naming, lint / static-analysis gates
17
+ - `: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
18
+ - The unit-test generator reads **only** `***implementation reqs***` — anything about `:UnitTests:` placed elsewhere (e.g. `***test reqs***`) is silently ignored
19
+ - Author each `:UnitTests:` requirement via `add-implementation-requirement` and phrase it in terms of `:UnitTests:` so the partition stays visible at a glance
20
+
15
21
  ## What belongs here
16
22
  - Technology choices: language, framework, runtime version
17
23
  - Architectural constraints: patterns, layering, dependency rules
@@ -5,7 +5,7 @@ globs: "**/*.plain"
5
5
 
6
6
  # Rules for embedded-integration test scripts
7
7
 
8
- When an embedded integration ships its three `test_scripts/` (prepare-environment, unit, conformance) — whether you author them by hand or via the `implement-*-testing-script` skills — these rules apply on top of the shared testing-script rules in [PLAIN_REFERENCE.md](../docs/PLAIN_REFERENCE.md) (exit-code conventions, the activate-only vs install-inline conformance distinction, `VERBOSE=1`, etc.).
8
+ When an embedded integration ships its three `test_scripts/` (prepare-environment, unit, conformance) — whether you author them by hand or via the `implement-*-testing-script` skills — these rules apply on top of the shared testing-script rules in PLAIN_REFERENCE.md (exit-code conventions, the activate-only vs install-inline conformance distinction, `VERBOSE=1`, etc.).
9
9
 
10
10
  ## Staging model (read this first)
11
11
 
@@ -24,46 +24,97 @@ This deliberately writes into the host's `src/main/...` and `src/test/...` (or t
24
24
 
25
25
  This rule covers the **mechanics each script must obey** regardless of language: argument handling, exit codes, idempotency, path resolution, output parsing, and the "what NOT to put here" guard rails. The language-specific install / test commands come from the skills; the contract below is invariant.
26
26
 
27
- ## What the `.plain` spec must declare in `***implementation reqs***`
27
+ ## What the `.plain` spec must declare
28
28
 
29
- The three scripts are generated from facts in the integration's spec. For an embedded integration, those facts cannot be inferred — they have to be **declared explicitly** in `***implementation reqs***` so the renderer (and the `implement-*-testing-script` skills) can fill them into the three placeholders that every script body needs:
29
+ The three scripts are generated from facts in the integration's spec. For an embedded integration, those facts cannot be inferred — they have to be **declared explicitly** in the right section, partitioned by which predefined concept they describe:
30
+
31
+ - **Everything about `:UnitTests:`** — paths, approach, packages, framework, conventions, fixtures, mocking policy — lives in `***implementation reqs***`. Unit tests are part of the generated codebase, so requirements that shape them are implementation reqs (see [`impl-reqs.md`](impl-reqs.md))
32
+ - **Everything about `:ConformanceTests:`** — paths, approach, packages, framework, execution command, pass criteria, mocking policy — lives in `***test reqs***`. Conformance tests are external to the generated codebase, so requirements that shape them are test reqs (see [`test-reqs.md`](test-reqs.md))
33
+
34
+ Authors use `add-implementation-requirement` for the first group and `add-test-requirement` for the second. The two groups are parallel — each predefined concept owns a complete authoring story in its own section.
35
+
36
+ ### In `***implementation reqs***` — everything about `:UnitTests:`
37
+
38
+ These reqs feed `run_unittests_<lang>`. `prepare_environment_<lang>` is **not** part of the unit-test workflow — it exists for conformance (see the next subsection) and reads its own facts from `***test reqs***`. At minimum, declare:
30
39
 
31
40
  1. **Integration source path inside the host** — where `$1/<source>/*` gets copied to. Example: `src/main/java/com/example/integrations/foo`
32
- 2. **Integration test source path inside the host** — where `$1/<tests>/*` gets copied to, and where `:UnitTests:` are discovered. Example: `src/test/java/com/example/integrations/foo`
33
- 3. **Test package for the test-filter argument** — the fully qualified package the test runner uses to scope discovery. Example: `com.example.integrations.foo`
41
+ 2. **`:UnitTests:` source path inside the host** — where `$1/<tests>/*` gets copied to, and where the test runner discovers unit tests. Example: `src/test/java/com/example/integrations/foo`
42
+ 3. **Fully qualified `:UnitTests:` package** — the package the test runner uses to scope discovery via its filter argument. Example: `com.example.integrations.foo`
43
+ 4. **`:UnitTests:` framework and conventions** — JUnit / pytest / Jest / Go's `testing` / etc., plus naming conventions (`*Test`, `test_*`, `*.test.ts`, …), fixture / mock / assertion style, file layout inside the test package
44
+ 5. **Quality gates that run alongside `:UnitTests:`** — Checkstyle / ESLint / Pylint / Ruff / `go vet` / `cargo clippy` — whatever the host project requires
34
45
 
35
- Author one `***implementation reqs***` entry per fact (use the `add-implementation-requirement` skill). Phrase each one in terms of `:UnitTests:` (the predefined concept) and the integration's other concepts, not as raw paths floating in the spec — that keeps the reqs reviewable and the renderer can resolve them when emitting scripts:
46
+ Author the location facts as one tight group, phrased in terms of `:UnitTests:`:
36
47
 
37
48
  ```plain
38
49
  - :UnitTests: of :Implementation: live in `src/test/java/com/example/integrations/foo` inside the host codebase.
39
50
  - The corresponding integration source code lives in `src/main/java/com/example/integrations/foo`.
40
- - The fully qualified test package used for test discovery is `com.example.integrations.foo`.
51
+ - The fully qualified package used for :UnitTests: discovery is `com.example.integrations.foo`.
52
+ - :UnitTests: use JUnit 5 with the host's Checkstyle profile applied via `mvn checkstyle:check`.
41
53
  ```
42
54
 
43
- These three facts feed directly into the script bodies:
55
+ These facts feed directly into the prepare and unit-test script bodies:
44
56
 
45
57
  ```bash
46
58
  # clean existing code from the host
47
59
  rm -rf $MAIN_PROJECT_FOLDER/<integration source path>/*
48
- rm -rf $MAIN_PROJECT_FOLDER/<integration test source path>/*
60
+ rm -rf $MAIN_PROJECT_FOLDER/<:UnitTests: source path>/*
49
61
 
50
62
  # create destinations and copy generated code into the host
51
63
  mkdir -p $MAIN_PROJECT_FOLDER/<integration source path>
52
- mkdir -p $MAIN_PROJECT_FOLDER/<integration test source path>
64
+ mkdir -p $MAIN_PROJECT_FOLDER/<:UnitTests: source path>
53
65
  cp -R $1/<integration source path>/* $MAIN_PROJECT_FOLDER/<integration source path>
54
- cp -R $1/<integration test source path>/* $MAIN_PROJECT_FOLDER/<integration test source path>
66
+ cp -R $1/<:UnitTests: source path>/* $MAIN_PROJECT_FOLDER/<:UnitTests: source path>
67
+
68
+ # run :UnitTests: scoped to the integration's package
69
+ mvn test -Dtest='<:UnitTests: package>.**.*Test' checkstyle:check
70
+ ```
71
+
72
+ ### In `***test reqs***` — everything about `:ConformanceTests:`
73
+
74
+ These reqs feed `run_conformance_tests_<lang>`. At minimum, declare:
75
+
76
+ 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`
77
+ 2. **`:ConformanceTests:` framework and execution command** — `mvn test --no-transfer-progress`, `pytest`, `npm test`, `go test ./...`, etc., with any flags / profiles the project requires
78
+ 3. **Fully qualified `:ConformanceTests:` package** (or path / pattern) used to scope discovery, if the runner needs one
79
+ 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)
80
+ 5. **`:ConformanceTests:` pass criteria** — the strict criteria from [*Pass criteria (strict)*](#pass-criteria-strict): at least one test ran AND zero failures / errors / skipped. Reaffirm this in the spec so the renderer knows the runner must parse the test tool's stdout
81
+ 6. **`:ConformanceTests:` build / install needs** — anything the conformance project needs before `mvn test` (or equivalent) will work: dependencies, fixtures, schema files, generated stubs
82
+
83
+ Author the conformance facts as one or more entries, phrased in terms of `:ConformanceTests:`:
84
+
85
+ ```plain
86
+ - :ConformanceTests: of :Implementation: live in `conformance_tests/foo/` and are implemented with JUnit 5 + Maven.
87
+ - The fully qualified package used for :ConformanceTests: discovery is `com.example.integrations.foo.conformance`.
88
+ - :ConformanceTests: are run via `mvn test --no-transfer-progress`; the host's Surefire plugin must be installed.
89
+ - :ConformanceTests: run against the live :ProviderName: sandbox — no mocking of provider calls.
90
+ - The conformance script reads `<PROVIDER>_API_KEY` (and any additional secrets) from the shell or from a `.env` file at the project root and fails fast (exit `69`) if any required var is missing after the optional `.env` load.
91
+ - The 429 (rate-limit) and forced-5xx paths use a local mock for that specific endpoint; every other path is live.
92
+ - :ConformanceTests: pass only when the Surefire summary line shows at least one test ran with zero failures, zero errors, and zero skipped.
93
+ ```
94
+
95
+ These facts feed directly into the conformance script body:
96
+
97
+ ```bash
98
+ # stage :ConformanceTests: source into the scratch directory
99
+ find "$DIR" -mindepth 1 -exec rm -rf {} +
100
+ cp -R $2/* $DIR
101
+ cd $DIR
102
+
103
+ # build the conformance project, then run :ConformanceTests:
104
+ mvn clean install -DskipTests
105
+ output=$(mvn test --no-transfer-progress 2>&1)
55
106
 
56
- # run the unit tests scoped to the integration's package
57
- mvn test -Dtest='<test package>.**.*Test' checkstyle:check
107
+ # parse Surefire summary against the declared pass criteria, then exit accordingly
58
108
  ```
59
109
 
60
- Rules that flow from this:
110
+ ### Rules common to both sections
61
111
 
62
- - **The three paths must agree.** The source path, the test-source path, and the test package describe the same module from three angles. A mismatch (e.g. test path `src/test/java/com/example/foo` but test package `com.example.bar`) silently produces a green build with stale or zero tests
63
- - **Paths are host-relative**, not absolute. `MAIN_PROJECT_FOLDER` comes from `HOST_CODEBASE_ROOT` (per the configuration concept); the paths above join onto it
112
+ - **The paths must agree across reqs.** The `:UnitTests:` source path, the `:UnitTests:` package, and the integration source path describe the same module from three angles. Same for the `:ConformanceTests:` location and its package. A mismatch (e.g. test path `src/test/java/com/example/foo` but test package `com.example.bar`) silently produces a green build with stale or zero tests
113
+ - **Paths are host-relative**, not absolute. `MAIN_PROJECT_FOLDER` comes from `HOST_CODEBASE_ROOT` (declared in the configuration concept); the paths above join onto it
64
114
  - **The renderer's output folder `$1` mirrors the same layout.** `$1/<integration source path>/*` exists because the renderer emits the generated code into the same package directories it'll be copied into — the `cp -R` is a straight overlay, not a path translation
65
- - **Each fact lives in one place.** If two `***implementation reqs***` entries declare slightly different test paths, the renderer can't tell which to use. Author the three reqs as a tight group (the example above is one bullet with two sub-bullets) so a future reviewer sees them together
66
- - **For non-Java languages**, the three facts have language-specific equivalents Python: `src/foo/`, `tests/foo/`, `tests.foo`; Node: `src/foo/`, `test/foo/`, `test/foo/**/*.test.ts`. The `implement-*-testing-script` skills know the mapping per language, but the spec still has to declare the host-relative paths so the skill knows where to copy
115
+ - **Each fact lives in one place.** If two `***implementation reqs***` entries declare slightly different `:UnitTests:` paths (or two `***test reqs***` entries declare slightly different `:ConformanceTests:` packages), the renderer can't tell which to use. Author each group as a tight cluster (one bullet with sub-bullets) so a future reviewer sees them together
116
+ - **Never duplicate a fact across sections.** `:UnitTests:` facts belong **only** in `***implementation reqs***`; `:ConformanceTests:` facts belong **only** in `***test reqs***`. The two groups never overlap the script generators read each from its own section
117
+ - **For non-Java languages**, the facts have language-specific equivalents — Python: `src/foo/`, `tests/foo/`, `tests.foo`; Node: `src/foo/`, `test/foo/`, `test/foo/**/*.test.ts`. The `implement-*-testing-script` skills know the mapping per language, but the spec still has to declare the host-relative paths and packages so the skill knows what to put in the script bodies
67
118
 
68
119
  ## Common contract (all three scripts)
69
120
 
@@ -158,6 +209,37 @@ Required steps:
158
209
  7. On the failure path: print the captured output unconditionally so the renderer (and the user) can see what failed
159
210
  8. On the success path: print the output only if `VERBOSE=1`
160
211
 
212
+ ### Secrets and `.env` handling
213
+
214
+ `:ConformanceTests:` run against the live provider (per [`integrations.md`](integrations.md) → *`:ConformanceTests:` always run against the live integration*), so the conformance script needs the user's credentials at runtime.
215
+
216
+ - **Credentials are read from environment variables** named in `***test reqs***` and the auth concept. Never from a literal, never from a file checked into the repo
217
+ - **The script may optionally load a `.env` file** before running the suite. Typical pattern in Bash:
218
+
219
+ ```bash
220
+ ENV_FILE="${ENV_FILE:-$MAIN_PROJECT_FOLDER/.env}"
221
+ if [ -f "$ENV_FILE" ]; then
222
+ echo "Loading env from $ENV_FILE"
223
+ set -a; . "$ENV_FILE"; set +a
224
+ fi
225
+ ```
226
+
227
+ PowerShell uses the equivalent `Get-Content` + `Set-Item Env:` loop. Absence of `.env` is **not** an error — CI provides the same vars directly through the shell
228
+ - **Verify required env vars exist after the optional `.env` load** and fail fast if any are missing:
229
+
230
+ ```bash
231
+ for var in PROVIDER_API_KEY PROVIDER_ACCOUNT_ID; do
232
+ if [ -z "${!var:-}" ]; then
233
+ printf "Error: %s is required for :ConformanceTests:\n" "$var" >&2
234
+ exit 69
235
+ fi
236
+ done
237
+ ```
238
+
239
+ - **Export the resolved vars** (already in scope thanks to `set -a`) so child processes started by the test runner inherit them — Maven, pytest, npm, Go all read env vars from their parent process
240
+ - **Never log credential values.** Echo the env-var **name** when reporting "loaded", not its value. Redact in any error path that dumps captured output
241
+ - **Document the secret names twice** — once in the auth concept (for the runtime), once in `***test reqs***` for `:ConformanceTests:` (for the script). They must be the same names; a divergence means the script reads different credentials than the runtime does, and conformance silently tests the wrong account
242
+
161
243
  ### Pass criteria (strict)
162
244
 
163
245
  These are the **only** valid pass criteria. Anything outside this list is a failure:
@@ -9,6 +9,8 @@ When an integration `.plain` module is **embedded** — meaning the generated co
9
9
 
10
10
  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.
11
11
 
12
+ > **For test-script authoring**, also follow [`integration-embedded-testing.md`](integration-embedded-testing.md). It defines the per-script contract (`prepare_environment_<lang>`, `run_unittests_<lang>`, `run_conformance_tests_<lang>`) — staging into the host vs `.tmp/`, arg validation, exit codes, output parsing, the three `***implementation reqs***` entries the spec must declare so the scripts can be generated, and a Java / Maven reference implementation. This file (`integration-embedded.md`) only summarizes the test-script wiring; the testing rule is the source of truth.
13
+
12
14
  ## The host codebase dictates the tech stack (hard rule)
13
15
 
14
16
  - 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
@@ -129,10 +131,11 @@ See [`integration-embedded-testing.md`](integration-embedded-testing.md) for the
129
131
  ### Invariants the scripts must enforce
130
132
 
131
133
  - **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
132
- - **Target package path is read from the `host-codebase` concept** never inferred from a heuristic. The renderer writes that path into the generated module's location too, so the copy destination is unambiguous
134
+ - **Everything about `:UnitTests:` is declared in `***implementation reqs***`**paths, approach, packages, framework, conventions. The prepare and unit-test scripts derive their copy destinations and test-filter argument from these reqs. See [`integration-embedded-testing.md`](integration-embedded-testing.md) for the exact reqs the spec must author (phrased in terms of `:UnitTests:`)
135
+ - **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:`)
136
+ - **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
133
137
  - **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
134
138
  - **Each script is idempotent.** Re-running the same script with the same `$1` yields the same result
135
- - **`***test reqs***` must document the script contract** — name the env var the host root is read from, the target package path inside the host where `$1` is copied, and the language-appropriate install + test commands. The renderer reads this req and emits the right script bodies
136
139
 
137
140
  ## Embedded-specific completion checklist
138
141
 
@@ -147,10 +150,11 @@ Before declaring an embedded integration done, in addition to the shared checkli
147
150
  - [ ] `prepare_environment` copies `$1` into the host's source tree at the module's package path, cleans the host's build-output directory, and runs the host's install / build so the conformance suite can resolve the integration from the local dependency cache
148
151
  - [ ] `run_unittests` runs the same copy-into-host sequence (self-contained — does not depend on `prepare_environment` having run) and invokes the host's test runner scoped to the module's package
149
152
  - [ ] `run_conformance_tests` copies `$2` into `.tmp/<lang>_conformance/`, `cd`s in, builds the conformance project, and runs it against the host build that `prepare_environment` already installed
150
- - [ ] Host codebase root is read from a named env var (default value documented in each script's usage) — never hardcoded
151
- - [ ] Target package path inside the host where `$1` is copied is read from the `host-codebase` conceptnever inferred
153
+ - [ ] Host codebase root is read from a named env var (default value documented in each script's usage) — never hardcoded; the env var name is captured in the integration's configuration concept
154
+ - [ ] `***implementation reqs***` declares **everything about `:UnitTests:`** — integration source path, `:UnitTests:` source path, `:UnitTests:` package, framework + conventions, lint / static-analysis gateper [`integration-embedded-testing.md`](integration-embedded-testing.md)
155
+ - [ ] `***test reqs***` declares **everything about `:ConformanceTests:`** — source location, framework + execution command, package, mocking / network policy, pass criteria, build / install needs — per [`integration-embedded-testing.md`](integration-embedded-testing.md)
156
+ - [ ] Neither group is duplicated across sections: `:UnitTests:` facts never appear in `***test reqs***`, `:ConformanceTests:` facts never appear in `***implementation reqs***`, and neither lives in the `host-codebase` concept
152
157
  - [ ] Every `rm -rf` in the scripts is scoped to the module's own package directory under the host's source tree — never targets the host's `src/main/`, `target/`, `node_modules/`, `build/`, or `dist/` at the project root
153
- - [ ] A `***test reqs***` entry documents the script contract (env var name, target package path inside the host, install + test commands)
154
158
 
155
159
  ## Anti-patterns specific to embedded integrations
156
160
 
@@ -100,15 +100,17 @@ Separate from the provider's API version (which lives in the provider OpenAPI fi
100
100
 
101
101
  Capture as a contract-version concept; pin the version in every published schema.
102
102
 
103
- ## Testing — live vs recorded, sandbox credentials, webhooks
103
+ ## Testing — live conformance, secrets from env, webhooks
104
104
 
105
- Standalone integrations get tested in isolation, so the testing strategy must be explicit (capture each decision as a `***test reqs***` entry):
105
+ `:ConformanceTests:` for a standalone integration **run against the live provider** (see [`integrations.md`](integrations.md) *`:ConformanceTests:` always run against the live integration*). The testing strategy is captured as `***test reqs***` entries authored via `add-test-requirement`:
106
106
 
107
- - **Live vs. recorded conformance tests.** Live tests hit the provider (requires sandbox creds in CI, may be rate-limited). Recorded tests use VCR-style cassettes or prerecorded responses under `resources/fixtures/`. Mock-server tests use a local stub (WireMock, Mockoon, MSW). Each has tradeoffs pick one (or a mix) and document it
108
- - **Sandbox credentials in CI.** If live tests are in scope, name where credentials come from (CI secret store, dedicated test tenant) and the rotation / leak-response policy
109
- - **Webhook tests** (if webhooks are in scope) must cover signature verification end-to-end including invalid signatures and replay attempts
110
- - **Rate-limit tests.** Tests that exercise the 429 path must **not** exhaust the live API's quota use a local mock for those cases
111
- - **Idempotency tests.** Run the same mutating call twice (with the same idempotency key) and assert the same response either against the live sandbox or against a recorded duplicate fixture
107
+ - **Conformance is live by default.** No VCR cassettes, no prerecorded responses for the calls under test, no mock servers. A green conformance run that never touched the provider proves nothing. Recorded responses under `resources/fixtures/` exist for unit tests and for grounding the OpenAPI schemasnot for conformance
108
+ - **Secrets come from environment variables.** Pin every credential as an env-var name (e.g. `<PROVIDER>_API_KEY`, `<PROVIDER>_CLIENT_ID` + `<PROVIDER>_CLIENT_SECRET`) in `***test reqs***` and in the auth concept. Use the names the provider's docs use so users don't have to translate
109
+ - **The user supplies values via `.env` or the shell.** The project ships `.env.example` (gitignored `.env` for real values). CI provides the same env-var names from its secret store. The conformance script may optionally `source` a `.env` from the project root if one exists; it must verify every required var is set after that optional load and fail fast (exit `69`) on missing vars
110
+ - **Sandbox credentials in CI.** Name where credentials come from (CI secret store, dedicated test tenant), the rotation / leak-response policy, and the env-var names CI must set
111
+ - **Webhook tests** (if webhooks are in scope) must cover signature verification end-to-end including invalid signatures and replay attempts. Signing keys are env vars, like every other secret
112
+ - **Rate-limit (429) tests.** The 429 path must **not** exhaust the live API's quota — use a local mock for that specific endpoint, document the exception in `***test reqs***`. Every other path remains live
113
+ - **Idempotency tests.** Run the same mutating call twice (with the same idempotency key) against the live sandbox and assert the same response
112
114
 
113
115
  ## Standalone-specific completion checklist
114
116
 
@@ -51,6 +51,39 @@ Documentation lies — it goes stale, omits undocumented fields, describes a dif
51
51
  - **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.
52
52
  - **Credentials are never written to `.plain` files or summaries.** Reference them by env-var name only.
53
53
 
54
+ ## `:ConformanceTests:` always run against the live integration (hard rule)
55
+
56
+ Integrations exist to talk to a real third-party (or internal) API. Their `:ConformanceTests:` therefore **always run against the live integration** — no VCR cassettes, no recorded fixtures, no `nock` / `WireMock` / `MSW` mocks for the calls under test. A "green" conformance run that never touched the provider proves nothing about the integration.
57
+
58
+ - The integration's `:ConformanceTests:` **must** make real network calls to the provider (typically a sandbox / staging environment, occasionally production for read-only paths). Fixtures under `resources/fixtures/` exist for unit tests and for grounding the schemas in the OpenAPI file — they are **not** a substitute for live conformance
59
+ - The only exceptions are paths that **cannot** be exercised live safely:
60
+ - **Rate-limit (429) tests** — must not exhaust the live quota; use a local mock for that specific endpoint
61
+ - **Deliberately destructive failure modes** (forced 5xx) the provider doesn't let you trigger — same; mock the specific endpoint
62
+ Document each exception explicitly in `***test reqs***`; everything else is live by default
63
+
64
+ ### Secrets come from the environment
65
+
66
+ 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:
67
+
68
+ - **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
69
+ - **The user supplies the values out-of-band**, either:
70
+ - in a `.env` file at the project root (`.env` is gitignored; the project ships `.env.example` with the names but no values), or
71
+ - exported in the shell that invokes the test scripts (CI uses the same names from its secret store)
72
+ - **`run_conformance_tests_<lang>` reads the env vars at runtime.** If a required var is missing, the script must fail fast with `Error: <NAME> is required for :ConformanceTests:` and exit `69` (per the testing-script exit-code conventions). Never default to a placeholder, never use a value baked into the script
73
+ - **The script may optionally load a `.env` file** before running the suite — typical pattern is to look for `.env` in the project root and `source` / `dotenv -e` it if present, but never fail when it's absent (since CI provides the vars directly via the shell). If a `.env` loader is used, the script must verify each required var is set **after** loading, not before
74
+ - **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
75
+ - **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
76
+
77
+ The `.plain` spec should make this discoverable. A minimal `***test reqs***` block for an integration looks like:
78
+
79
+ ```plain
80
+ - :ConformanceTests: run against the live :ProviderName: sandbox — no mocking of provider calls.
81
+ - Credentials are read from the environment, never from a file checked into the repo.
82
+ - The conformance script reads `<PROVIDER>_API_KEY` (and any additional secrets) from the shell or from a `.env` file at the project root.
83
+ - The script must verify every required env var is set after the optional `.env` load and fail fast with a clear error if any is missing.
84
+ - The 429 (rate-limit) and forced-5xx paths use a local mock for that specific endpoint; every other path is live.
85
+ ```
86
+
54
87
  ## Embedded vs standalone — pick the shape early
55
88
 
56
89
  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.
@@ -13,6 +13,12 @@ When writing or editing a `***test reqs***` section in a `.plain` file, always f
13
13
  - Acceptance tests are nested under individual functional specs, not here
14
14
  - Behavioral requirements go in `***functional specs***`, not here
15
15
 
16
+ ## `:ConformanceTests:` lives here (hard rule)
17
+ - **Everything** about `:ConformanceTests:` goes in `***test reqs***` — paths, approach, packages, framework, execution command, mocking / network policy, fixtures, pass criteria, environment prerequisites
18
+ - `: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
19
+ - The conformance-test generator reads **only** `***test reqs***` — anything about `:ConformanceTests:` placed elsewhere (e.g. `***implementation reqs***`) is silently ignored
20
+ - Author each `:ConformanceTests:` requirement via `add-test-requirement` and phrase it in terms of `:ConformanceTests:` so the partition stays visible at a glance
21
+
16
22
  ## What belongs here
17
23
  - Test framework: which framework to use (e.g., pytest, Unittest, xUnit)
18
24
  - Execution method: the command to run the tests
@@ -38,7 +38,15 @@ Implementation reqs are free-form instructions that steer code generation. Commo
38
38
 
39
39
  - **Behavior and features** — those go in `***functional specs***`
40
40
  - **Concept definitions** — those go in `***definitions***`
41
- - **Test instructions** — those go in `***test reqs***`
41
+ - **Conformance-test instructions** — those go in `***test reqs***`. Note: **unit-test instructions belong HERE**, not in `***test reqs***` — see *Unit-test guidance belongs here* below
42
+
43
+ ## Unit-test guidance belongs here
44
+
45
+ **Everything about `:UnitTests:` goes in `***implementation reqs***`** — paths, approach, packages, framework (JUnit / pytest / Jest / Go's `testing` / …), conventions, fixtures, mocking policy, file layout, naming, lint / static-analysis gates. Unit tests are part of the generated codebase, so requirements that shape them are implementation reqs by definition.
46
+
47
+ - The unit-test generator reads **only** `***implementation reqs***`; anything about `:UnitTests:` placed in `***test reqs***` is silently ignored
48
+ - Phrase each `:UnitTests:` requirement in terms of the predefined `:UnitTests:` concept so the partition stays visible at a glance
49
+ - `***test reqs***` is exclusively for `:ConformanceTests:` — see [`add-test-requirement`](../add-test-requirement/SKILL.md)
42
50
 
43
51
  ## Key Principle: HOW vs WHAT
44
52
 
@@ -183,13 +183,19 @@ prepare-environment-script: test_scripts/prepare_environment_<language>.<sh|ps1>
183
183
 
184
184
  Use `.sh` on macOS/Linux and `.ps1` on Windows, matching what testing scripts. Preserve any existing fields in a `config.yaml` you are updating.
185
185
 
186
+ **Hard partition reminder.** Throughout this phase:
187
+
188
+ - **Everything about `:UnitTests:`** (framework, layout, packages, conventions, execution command, coverage, mocking policy — every fact) is authored into `***implementation reqs***` via `add-implementation-requirement`. The unit-test generator reads only that section
189
+ - **Everything about `:ConformanceTests:`** (framework, layout, packages, execution command, mocking policy, environment prereqs — every fact) is authored into `***test reqs***` via `add-test-requirement`. The conformance-test generator reads only that section
190
+ - A topic that mixes both kinds of facts is split: unit facts go to impl reqs, conformance facts go to test reqs. They never share a bullet
191
+
186
192
  Walk through these topics in order, running ask → author → review for each. Skip a topic only if it genuinely doesn't apply, and say so explicitly:
187
193
 
188
- 1. **Testing framework** — e.g. pytest, Jest, JUnit, Go's `testing` package. If the user has no preference, suggest one that fits the language chosen in Phase 2.
189
- - Author: a framework requirement in `***test reqs***` at the appropriate scope (template if shared, otherwise on the module). Generate `run_unittests` (and any framework config files it needs, e.g. `pytest.ini`, `jest.config.js`) via `implement-unit-testing-script`. Add the `unittests-script:` entry to the relevant `config.yaml`(s), creating each file if it doesn't exist yet.
194
+ 1. **Unit-test framework** — e.g. pytest, Jest, JUnit, Go's `testing` package. If the user has no preference, suggest one that fits the language chosen in Phase 2.
195
+ - Author: a `:UnitTests:` framework requirement in `***implementation reqs***` at the appropriate scope (template if shared, otherwise on the module) — e.g. "`:UnitTests:` should use pytest" plus "`:UnitTests:` are run via `pytest tests/`". Generate `run_unittests` (and any framework config files it needs, e.g. `pytest.ini`, `jest.config.js`) via `implement-unit-testing-script`. Add the `unittests-script:` entry to the relevant `config.yaml`(s), creating each file if it doesn't exist yet.
190
196
  - Review: the framework req, the generated script paths, and the new `config.yaml` entry.
191
- 2. **Test types in scope** — unit tests and integration tests. Which combinations does the user want? How do tests map to the architectural layers established in Phase 2 (e.g. one test module per service, repository tests with an in-memory store, etc.)?
192
- - Author: a test-types/scope requirement in `***test reqs***` describing which types are in scope and how they map to the architecture.
197
+ 2. **Unit-test types and architecture mapping** — unit tests and integration tests. Which combinations does the user want? How do tests map to the architectural layers established in Phase 2 (e.g. one test module per service, repository tests with an in-memory store, etc.)?
198
+ - Author: a `:UnitTests:` scope / architecture requirement in `***implementation reqs***` describing which types are in scope and how they map to the architecture — phrased in terms of `:UnitTests:` so the partition is visible.
193
199
  - Review: that requirement.
194
200
  3. **Conformance testing** — explicitly ask whether conformance/end-to-end tests should be part of the project. Conformance testing drives whether `run_conformance_tests` is generated and whether `***acceptance tests***` are authored. If the user is unsure, briefly explain the tradeoff (extra scripts + per-spec acceptance tests vs. lighter setup) and let them choose.
195
201
  - Author (if yes):
@@ -203,11 +209,11 @@ Walk through these topics in order, running ask → author → review for each.
203
209
  - Author (if yes): `prepare_environment` via `implement-prepare-environment-script`; add the `prepare-environment-script:` entry to the relevant `config.yaml`(s); if the script's responsibilities are non-trivial and worth pinning in the spec, also add a brief `***test reqs***` entry describing what `prepare_environment` is responsible for.
204
210
  - Author (if no): record the decision; skip the script and the config entry.
205
211
  - Review: the script (if any), the new config entry (if any), and the test req (if any).
206
- 5. **Test layout & conventions** — directory layout for tests, naming conventions, fixtures/mocks strategy, anything that constrains the *shape* of test code beyond what topics 1 and 2 already established.
207
- - Author: layout/convention requirements in `***test reqs***` at the appropriate scope.
212
+ 5. **Test layout & conventions** — directory layout, naming conventions, fixtures / mocks strategy, anything that constrains the *shape* of test code beyond what topics 1–4 already established. Ask about both kinds of tests where applicable; keep their facts in separate reqs in separate sections.
213
+ - Author: `:UnitTests:` layout / convention requirements in `***implementation reqs***`; `:ConformanceTests:` layout / convention requirements in `***test reqs***` (only when conformance is enabled). Phrase each one with the predefined concept it shapes so the partition is visible.
208
214
  - Review: each requirement snippet.
209
- 6. **Execution & tooling** — how tests are run (commands, runners, options), coverage targets, CI integration, any environment setup tests rely on beyond `prepare_environment`. If the agreed execution command or options differ from what the script generated in topic 1 (or 3, or 4) currently uses, update the affected script(s) now.
210
- - Author: execution requirements in `***test reqs***`; update any affected scripts under `test_scripts/`.
215
+ 6. **Execution & tooling** — how tests are run (commands, runners, options), coverage targets, CI integration, any environment setup tests rely on beyond `prepare_environment`. Split by concept the same way as topic 5. If the agreed execution command or options differ from what the script generated in topic 1 (or 3, or 4) currently uses, update the affected script(s) now.
216
+ - Author: `:UnitTests:` execution requirements in `***implementation reqs***`; `:ConformanceTests:` execution requirements in `***test reqs***`. Update any affected scripts under `test_scripts/`.
211
217
  - Review: each requirement snippet and any modified script.
212
218
  7. **Other testing constraints** — performance/load expectations, deterministic seeds, network isolation, secrets handling, anything stack-wide that constrains *how* tests are written and that hasn't already been covered.
213
219
  - Author: each constraint as its own requirement at the appropriate scope.
@@ -58,15 +58,17 @@ Use **AskUserQuestion** for one tight batch covering:
58
58
 
59
59
  Create the import module with `create-import-module`. It must contain:
60
60
 
61
- - `***implementation reqs***` — the base stack as requirements:
61
+ - `***implementation reqs***` — the base stack as requirements **and everything about `:UnitTests:`**:
62
62
  - Programming language and version.
63
63
  - Primary framework (if any).
64
64
  - Dependency / package manager.
65
65
  - Project kind as a constraint (e.g. ":Implementation: should be a REST API service.").
66
+ - `:UnitTests:` framework (pytest / Jest / JUnit / Go's `testing` / …) and the command used to run them — phrased in terms of `:UnitTests:` so the partition is explicit.
66
67
  - Anything else the user added in the free-form catch-all.
67
- - `***test reqs***` — the base testing rules:
68
- - Unit testing framework and the command used to run it.
69
- - ":ConformanceTests: must be implemented and executed - do not skip tests." — only if conformance testing is enabled. Add framework + command for conformance tests too.
68
+ - `***test reqs***` — the base **conformance**-testing rules (only added if conformance testing is enabled):
69
+ - ":ConformanceTests: must be implemented and executed - do not skip tests."
70
+ - The `:ConformanceTests:` framework and the command used to run them.
71
+ - **Do NOT** put unit-test framework / command here — that lives in `***implementation reqs***` above.
70
72
 
71
73
  Do **not** add a `***definitions***` section to `template/base.plain`. This skill does not author any concepts. Use only the predefined concepts (`:Implementation:`, `:ConformanceTests:`) in the reqs — no project-specific concepts like `:AppName:` or `:App:`. Do not declare `required_concepts` either.
72
74
 
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  name: load-plain-reference
3
3
  description: >-
4
- Loads the full ***plain language reference into context: syntax, section types
4
+ Loads the full ***plain language reference into context (PLAIN_REFERENCE.md): syntax, section types
5
5
  (definitions, implementation reqs, test reqs, functional specs, acceptance tests),
6
6
  concept notation, frontmatter (import/requires/required_concepts/exported_concepts),
7
7
  templates, linked resources, module model, and authoring best practices. Use whenever
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "plain-forge",
3
- "version": "1.0.6",
3
+ "version": "1.0.7",
4
4
  "description": "Conversational spec-writing tool for ***plain specification language",
5
5
  "type": "module",
6
6
  "engines": {
File without changes