plain-forge 1.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +247 -0
- package/bin/cli.mjs +143 -0
- package/forge/docs/.gitkeep +0 -0
- package/forge/rules/definitions.md +57 -0
- package/forge/rules/exported-concepts.md +39 -0
- package/forge/rules/func-specs.md +72 -0
- package/forge/rules/impl-reqs.md +50 -0
- package/forge/rules/import-modules.md +51 -0
- package/forge/rules/required-concepts.md +45 -0
- package/forge/rules/requires-modules.md +59 -0
- package/forge/rules/test-reqs.md +47 -0
- package/forge/skills/add-acceptance-test/SKILL.md +98 -0
- package/forge/skills/add-concept/SKILL.md +67 -0
- package/forge/skills/add-feature/SKILL.md +136 -0
- package/forge/skills/add-functional-spec/SKILL.md +81 -0
- package/forge/skills/add-functional-specs/SKILL.md +115 -0
- package/forge/skills/add-implementation-requirement/SKILL.md +73 -0
- package/forge/skills/add-resource/SKILL.md +108 -0
- package/forge/skills/add-template/SKILL.md +65 -0
- package/forge/skills/add-test-requirement/SKILL.md +68 -0
- package/forge/skills/analyze-2-func-specs/SKILL.md +102 -0
- package/forge/skills/analyze-func-specs/SKILL.md +124 -0
- package/forge/skills/analyze-if-func-spec-too-complex/SKILL.md +152 -0
- package/forge/skills/break-down-func-spec/SKILL.md +156 -0
- package/forge/skills/check-plain-env/SKILL.md +288 -0
- package/forge/skills/consolidate-concepts/SKILL.md +193 -0
- package/forge/skills/create-import-module/SKILL.md +98 -0
- package/forge/skills/create-requires-module/SKILL.md +104 -0
- package/forge/skills/debug-specs/SKILL.md +189 -0
- package/forge/skills/forge-integration/SKILL.md +443 -0
- package/forge/skills/forge-plain/SKILL.md +333 -0
- package/forge/skills/implement-conformance-testing-script/SKILL.md +247 -0
- package/forge/skills/implement-conformance-testing-script/assets/run_conformance_tests_cypress.ps1 +324 -0
- package/forge/skills/implement-conformance-testing-script/assets/run_conformance_tests_golang.ps1 +100 -0
- package/forge/skills/implement-conformance-testing-script/assets/run_conformance_tests_java.sh +102 -0
- package/forge/skills/implement-conformance-testing-script/assets/run_conformance_tests_python.ps1 +92 -0
- package/forge/skills/implement-conformance-testing-script/assets/run_conformance_tests_python.sh +100 -0
- package/forge/skills/implement-prepare-environment-script/SKILL.md +242 -0
- package/forge/skills/implement-prepare-environment-script/assets/prepare_environment_java.sh +42 -0
- package/forge/skills/implement-prepare-environment-script/assets/prepare_environment_python.sh +81 -0
- package/forge/skills/implement-unit-testing-script/SKILL.md +133 -0
- package/forge/skills/implement-unit-testing-script/assets/run_unittests_flutter.ps1 +82 -0
- package/forge/skills/implement-unit-testing-script/assets/run_unittests_golang.ps1 +68 -0
- package/forge/skills/implement-unit-testing-script/assets/run_unittests_java.sh +45 -0
- package/forge/skills/implement-unit-testing-script/assets/run_unittests_python.ps1 +76 -0
- package/forge/skills/implement-unit-testing-script/assets/run_unittests_python.sh +90 -0
- package/forge/skills/implement-unit-testing-script/assets/run_unittests_react.ps1 +83 -0
- package/forge/skills/init-config-file/SKILL.md +261 -0
- package/forge/skills/init-plain-project/SKILL.md +124 -0
- package/forge/skills/load-plain-reference/SKILL.md +646 -0
- package/forge/skills/plain-healthcheck/SKILL.md +132 -0
- package/forge/skills/refactor-module/SKILL.md +197 -0
- package/forge/skills/resolve-spec-conflict/SKILL.md +88 -0
- package/forge/skills/run-codeplain/SKILL.md +540 -0
- package/package.json +42 -0
|
@@ -0,0 +1,540 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: run-codeplain
|
|
3
|
+
description: >-
|
|
4
|
+
Launch a `codeplain` render and supervise it end-to-end. Tails the log file,
|
|
5
|
+
watches generated code appear under `plain_modules/`, and inspects the live
|
|
6
|
+
TUI/process. Detects pathologies (stuck conformance loops, complexity errors,
|
|
7
|
+
missing concepts, render failures), and — with user approval — stops the
|
|
8
|
+
renderer, hands off to a spec-edit skill, and resumes with `--render-from`.
|
|
9
|
+
Use whenever the user wants to actually render specs (not just dry-run) and
|
|
10
|
+
wants supervision of the run.
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
# Run Codeplain
|
|
14
|
+
|
|
15
|
+
Always use the skill `load-plain-reference` to retrieve the ***plain syntax rules — but only if you haven't done so yet.
|
|
16
|
+
|
|
17
|
+
## What this skill is
|
|
18
|
+
|
|
19
|
+
A **supervisor** around a live `codeplain` render. The renderer itself is the codeplain CLI; this skill does not generate code. It:
|
|
20
|
+
|
|
21
|
+
1. Launches `codeplain <module>.plain` (or attaches to an in-flight run).
|
|
22
|
+
2. Watches three signal sources in a tight loop, in priority order:
|
|
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.
|
|
25
|
+
- the **process / TUI** itself (alive? exited? exit code?) — a tertiary signal, used only to know whether to keep monitoring.
|
|
26
|
+
3. Surfaces what is happening to the user in plain English, in near-real-time.
|
|
27
|
+
4. On pathology, stops the renderer (with user approval), hands off to the right edit skill, and resumes from the last completed functionality.
|
|
28
|
+
|
|
29
|
+
This skill is **read-mostly**. It does not modify `.plain` files itself — it delegates to `debug-specs`, `resolve-spec-conflict`, `break-down-func-spec`, `add-implementation-requirement`, etc. The only files it writes are control files: it may `kill` a process, and it shells out to `codeplain` again to resume.
|
|
30
|
+
|
|
31
|
+
**The log is the ground truth.** The TUI is opaque to the agent; the filesystem moves slowly and only on completed steps; but `codeplain.log` updates the moment the renderer takes any action — API call, retry, fix attempt, test run, abort. If the log and the filesystem disagree, trust the log. If the log and the TUI disagree (per the user's description), trust the log.
|
|
32
|
+
|
|
33
|
+
## When to use
|
|
34
|
+
|
|
35
|
+
- The user says "render it", "let's run codeplain", "kick off the render", "render `<module>.plain`", or similar.
|
|
36
|
+
- The user already has a render going in another terminal and wants you to babysit it.
|
|
37
|
+
- After `forge-plain` / `add-feature` / `debug-specs` / `plain-healthcheck` finished and the next move is the real render.
|
|
38
|
+
|
|
39
|
+
Do **not** use this skill for:
|
|
40
|
+
|
|
41
|
+
- A dry-run gate — that is `plain-healthcheck`.
|
|
42
|
+
- Authoring or editing specs — that is `forge-plain`, `add-feature`, `debug-specs`, or the specific `add-*` / `resolve-*` / `break-down-*` skills.
|
|
43
|
+
|
|
44
|
+
## Pre-flight (always do this first)
|
|
45
|
+
|
|
46
|
+
Before launching anything:
|
|
47
|
+
|
|
48
|
+
1. **Confirm the target.** Ask the user which top module they want rendered if it is not unambiguous from the conversation. The argument is the path to a `.plain` file (e.g. `agent_harness.plain`, `backend/api.plain`).
|
|
49
|
+
2. **Run `plain-healthcheck` if it hasn't been run since the last spec edit.** A real render that fails on something a dry-run would have caught burns credits. If healthcheck FAILs, stop here and surface the failures — do not start the render.
|
|
50
|
+
3. **Verify the environment.**
|
|
51
|
+
- `codeplain` is on `PATH` (`command -v codeplain`).
|
|
52
|
+
- `CODEPLAIN_API_KEY` is exported, **or** the user will pass `--api-key`.
|
|
53
|
+
- The governing `config.yaml` for the chosen module exists (use the pairing rule from `plain-healthcheck`: same-directory config wins; root `config.yaml` is the fallback).
|
|
54
|
+
4. **Decide on flags with the user.** Default to a minimal command line; mention these knobs only if they apply:
|
|
55
|
+
- `--config-name <name>` — multi-part project, or non-default config file name.
|
|
56
|
+
- `--render-range <range>` / `--render-from <id>` — partial render, or resume from a previous run.
|
|
57
|
+
- `--force-render` — invalidate cached module renders.
|
|
58
|
+
- `--template-dir <path>` — templates outside `template/` and not pinned in config.
|
|
59
|
+
- `--copy-build` / `--build-dest` — copy generated code somewhere outside `plain_modules/`.
|
|
60
|
+
- `--copy-conformance-tests` / `--conformance-tests-dest` — same for conformance tests.
|
|
61
|
+
- `--test-script-timeout <seconds>` — if test scripts are slow.
|
|
62
|
+
- `--api-key <key>` / `--api <url>` — only if the env var route is not in use.
|
|
63
|
+
- `--render-machine-graph` — only on explicit request.
|
|
64
|
+
- `--headless` — see "Launch mode" below.
|
|
65
|
+
|
|
66
|
+
Do **not** prompt for flags the user did not signal interest in; the empty command line is the right default.
|
|
67
|
+
|
|
68
|
+
5. **Locate and reset the log file.** It is `codeplain.log` in the same directory as the `.plain` file unless `--log-file-name` was overridden. **The file is overwritten** at the start of each run (per the `--log-file-name` help text). So:
|
|
69
|
+
- Before launch, note the **absolute path** of the log file and its current size (`wc -c <path>`) — this lets you detect the moment the new run truncates and starts writing.
|
|
70
|
+
- After launch, every log read must be against this same path. Re-derive it if the user passes `--log-file-name` or runs from a different working directory.
|
|
71
|
+
- The log is **per-run**: every byte you read during this run was written by this run. You don't have to worry about stale lines from prior runs.
|
|
72
|
+
|
|
73
|
+
6. **Read every test script referenced by the governing `config.yaml`.** This is non-negotiable. The scripts are the contract between the renderer and the project; you cannot judge what the renderer is "fixing" without knowing what `pass` and `fail` actually mean in this project. For each of `unittests-script`, `prepare-environment-script`, and `conformance-tests-script` (those that are declared), read the script end-to-end **before** launching, and write down for yourself:
|
|
74
|
+
|
|
75
|
+
- **Which framework is invoked** (e.g. `vitest`, `jest`, `pytest`, `go test`, `cargo test`, `phpunit`, etc.) and any framework flags (`--noEmit`, `--reporter=verbose`, custom configs).
|
|
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
|
+
- **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
|
+
- **Where the scripts materialize their working environment** (e.g. `.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.
|
|
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
|
+
|
|
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*.
|
|
83
|
+
|
|
84
|
+
## Launch mode — TUI vs `--headless`
|
|
85
|
+
|
|
86
|
+
Pick a launch mode with the user before starting.
|
|
87
|
+
|
|
88
|
+
### Mode A — `--headless`, agent-launched (default; preferred for supervised runs)
|
|
89
|
+
|
|
90
|
+
The agent launches the renderer in the background and supervises via the log file + filesystem. The terminal tool **cannot** drive an interactive TUI, so TUI mode is incompatible with agent-launched rendering. Use `--headless`:
|
|
91
|
+
|
|
92
|
+
```bash
|
|
93
|
+
nohup codeplain <module>.plain --headless <other-flags> > /dev/null 2>&1 &
|
|
94
|
+
echo $! # capture the PID
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
Record the PID. Confirm `codeplain.log` starts growing within a few seconds; if it doesn't, the launch failed — check the user's shell / API key and re-try.
|
|
98
|
+
|
|
99
|
+
### Mode B — user runs the TUI in another terminal; agent attaches
|
|
100
|
+
|
|
101
|
+
The user has, or wants, the TUI running in a separate terminal of their own. The agent does not launch anything — it only monitors. Confirm the PID with the user (`pgrep -fl codeplain` is the easy way) and confirm the log path. Everything else in the workflow is identical.
|
|
102
|
+
|
|
103
|
+
Never try to "screenshot" or "read" the TUI directly — you can't see it. Treat the TUI as opaque; rely on log + filesystem signals.
|
|
104
|
+
|
|
105
|
+
## Log monitoring contract
|
|
106
|
+
|
|
107
|
+
The monitor loop in Phase 1 leans almost entirely on `codeplain.log`. Before describing the loop, here is exactly how to read this file efficiently and what to look for. Treat this section as the reference the loop calls into.
|
|
108
|
+
|
|
109
|
+
### How to read the log incrementally
|
|
110
|
+
|
|
111
|
+
The log grows monotonically during a run — only ever appended to, never rewritten — and can reach thousands of lines on a long render. Never re-read it whole on each pass. Track the **last byte offset** you read and read only the new bytes since then.
|
|
112
|
+
|
|
113
|
+
Keep two pieces of state across iterations:
|
|
114
|
+
|
|
115
|
+
- `LOG_PATH` — absolute path to `codeplain.log` (captured during pre-flight).
|
|
116
|
+
- `LOG_OFFSET` — number of bytes you have already consumed. Starts at `0` for a fresh run.
|
|
117
|
+
|
|
118
|
+
Each monitor pass:
|
|
119
|
+
|
|
120
|
+
```bash
|
|
121
|
+
SIZE=$(wc -c < "$LOG_PATH")
|
|
122
|
+
if [ "$SIZE" -gt "$LOG_OFFSET" ]; then
|
|
123
|
+
tail -c +$((LOG_OFFSET + 1)) "$LOG_PATH" # new bytes only
|
|
124
|
+
LOG_OFFSET=$SIZE
|
|
125
|
+
fi
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
If `SIZE < LOG_OFFSET`, the file was truncated — a new run has started or somebody rotated the log. Reset `LOG_OFFSET=0` and re-read from the top.
|
|
129
|
+
|
|
130
|
+
If you cannot keep shell state between tool calls, the equivalent is: `wc -c` the file each pass to get the new size, remember the previous size in your own conversation state, and pass the byte range to `tail -c +<offset+1>` or `dd skip=<offset> bs=1`. Either way, **never** read the whole file on a routine pass — only when you need backstory (e.g. you just detected an error and want context from earlier).
|
|
131
|
+
|
|
132
|
+
### Catalog of log patterns to recognize
|
|
133
|
+
|
|
134
|
+
All patterns below are taken from the actual codeplain runtime. Treat them as the canonical signal vocabulary.
|
|
135
|
+
|
|
136
|
+
| Pattern (regex / substring) | Meaning | Loop action |
|
|
137
|
+
| --------------------------- | ------- | ----------- |
|
|
138
|
+
| `^INFO:codeplain:[-]+$` followed by `Module: <name>` and `Rendering functionality <N>:` | A new functionality is starting. The block ends at the next `-----` line. | Update `CURRENT_MODULE` and `CURRENT_FUNCTIONALITY`. Reset `ATTEMPT_COUNTER` to 0. Report to the user. |
|
|
139
|
+
| `Running unit tests script .* \(attempt: <N>\)` | Unit tests starting, possibly a retry. | If `<N>` climbs past 1, flag as warning; past 3, treat as Pathology G. |
|
|
140
|
+
| `\[#79FC96\]All Unit Tests scripts have passed successfully.\[/#79FC96\]` | Unit tests green. | Routine. Note the functionality is past the unit-test gate. |
|
|
141
|
+
| `Refactoring the generated code...` | Refactor pass between unit tests and conformance. | Routine. |
|
|
142
|
+
| `Implementing conformance tests...` / `Implementing test requirements:` | Conformance phase starting for the current functionality. | Routine. |
|
|
143
|
+
| `Running testing environment preparation script .* for build folder` | `prepare-environment-script` is running. | Routine. |
|
|
144
|
+
| `\[#79FC96\]All Testing Environment Preparation scripts have passed successfully.\[/#79FC96\]` | Env prep green. | Routine. |
|
|
145
|
+
| `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. |
|
|
146
|
+
| `Running conformance tests attempt <N>.` | Conformance retry loop. **Watch `<N>` carefully.** | Increment `ATTEMPT_COUNTER = <N>`. At `>=5`, trigger Pathology A. |
|
|
147
|
+
| `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. |
|
|
148
|
+
| `Functional spec too complex!` | Single spec implies >200 LOC. Renderer aborts. | Pathology B. |
|
|
149
|
+
| Substrings: `missing concept`, `unknown definition`, `cyclic`, `not defined`, `cannot resolve` | Spec graph error. Renderer aborts. | Pathology C. |
|
|
150
|
+
| Substrings: `conflict`, `conflicting`, `contradicts` | Spec conflict surfaced. | Pathology D. |
|
|
151
|
+
| `Traceback (most recent call last):` followed by a Python stack | Renderer itself crashed (not a spec error). | Stop monitoring; surface the stack to the user verbatim; this is a bug report case, not a spec edit. |
|
|
152
|
+
| `\[#FF6B6B\]✗ rendering failed` | Renderer aborted. Followed by `render id`, `input file`, `generated code folder`, `functionalities`, `used credits`, `render time`. | Capture the whole failure block. Run is over — go to Phase 4 (failure branch). |
|
|
153
|
+
| `\[#79FC96\]✓ rendering succeeded` (or `rendering complete`) followed by the same metadata block | Renderer finished successfully. | Capture the success block. Run is over — go to Phase 4 (success branch). |
|
|
154
|
+
| `429` / `rate limit` / `quota` / `unauthorized` / `403` / `401` | API problem, not a spec problem. | Stop monitoring; tell the user the API rejected the request and surface the line. |
|
|
155
|
+
| **No new bytes for > 2 min while the process is alive** | API stall or wedged renderer. | Pathology E. |
|
|
156
|
+
|
|
157
|
+
Where a column says "Pathology X", see Phase 2 for the response.
|
|
158
|
+
|
|
159
|
+
### Three pieces of state to maintain across the loop
|
|
160
|
+
|
|
161
|
+
The pathologies in Phase 2 can only be detected if you carry state between iterations. Keep these three variables in your working memory across passes:
|
|
162
|
+
|
|
163
|
+
1. **`CURRENT_FUNCTIONALITY`** — the integer ID of the functionality currently being rendered. Updated whenever you see `Rendering functionality <N>:`.
|
|
164
|
+
2. **`LAST_COMPLETED_FUNCTIONALITY`** — the highest functionality ID whose conformance tests passed cleanly (i.e. no `Fixing conformance test` immediately after, and either the next `Rendering functionality <N+1>:` line appeared or the run succeeded). This is the value you'll pass to `--render-from <N+1>` on resume.
|
|
165
|
+
3. **`ATTEMPT_COUNTER`** — the most recent `Running conformance tests attempt <N>` value for `CURRENT_FUNCTIONALITY`. Reset to 0 when `CURRENT_FUNCTIONALITY` changes.
|
|
166
|
+
|
|
167
|
+
A single-line summary of these three values is what you'll show the user on each pass.
|
|
168
|
+
|
|
169
|
+
### Useful one-liners
|
|
170
|
+
|
|
171
|
+
Use these as cheap, idempotent ways to interrogate the log. Each is safe to run on any pass.
|
|
172
|
+
|
|
173
|
+
- Latest functionality the renderer started:
|
|
174
|
+
```bash
|
|
175
|
+
grep -E "Rendering functionality [0-9]+:" codeplain.log | tail -n 1
|
|
176
|
+
```
|
|
177
|
+
- Current conformance attempt counter:
|
|
178
|
+
```bash
|
|
179
|
+
grep -E "Running conformance tests attempt [0-9]+\." codeplain.log | tail -n 1
|
|
180
|
+
```
|
|
181
|
+
- All errors so far:
|
|
182
|
+
```bash
|
|
183
|
+
grep -E "^(ERROR|CRITICAL|WARNING):codeplain:|✗ rendering failed|Functional spec too complex|Traceback" codeplain.log
|
|
184
|
+
```
|
|
185
|
+
- Time the last log line was written (proxy for liveness):
|
|
186
|
+
```bash
|
|
187
|
+
stat -f %m codeplain.log # macOS — epoch mtime
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
## Reading test-script output
|
|
191
|
+
|
|
192
|
+
The renderer drives three external shell scripts during a run — `unittests-script`, `prepare-environment-script`, and `conformance-tests-script` (whichever are declared in the project's `config.yaml`). Each script wraps a real test framework (vitest, jest, pytest, tsc, etc.) and writes verbose output. **This skill never reads the script source code.** It reads the output those scripts produce while the renderer is iterating. Three places to look, in priority order:
|
|
193
|
+
|
|
194
|
+
### 1. Inline in `codeplain.log` (the primary source)
|
|
195
|
+
|
|
196
|
+
With verbose logging on (the default — `--verbose` is `enabled` by default per the help), codeplain captures each script's stdout/stderr and writes it into `codeplain.log` between its own wrapper lines:
|
|
197
|
+
|
|
198
|
+
```
|
|
199
|
+
INFO:codeplain:Running unit tests script .../unit_testing_typescript.sh. (attempt: 1)
|
|
200
|
+
... script output here: vitest summaries, FAIL lines, tsc diagnostics ...
|
|
201
|
+
INFO:codeplain:[#79FC96]All Unit Tests scripts have passed successfully.[/#79FC96]
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
So when you do the incremental log read in Phase 1, you are simultaneously reading the **renderer wrapper lines** ("running", "passed", "fixing") *and* the **raw framework output** sandwiched between them. The wrapper says *whether* the script passed; the framework output says *why* it failed and *what* the renderer is reacting to. Both are needed.
|
|
205
|
+
|
|
206
|
+
What to extract from the framework output — stay framework-agnostic, look for these shapes:
|
|
207
|
+
|
|
208
|
+
- **Counts**: `<N> passed`, `<M> failed`, `<K> skipped`. Climbing failure count between attempts means the renderer is making things worse.
|
|
209
|
+
- **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.
|
|
210
|
+
- **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.
|
|
211
|
+
- **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.
|
|
212
|
+
- **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.
|
|
213
|
+
- **"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.
|
|
214
|
+
|
|
215
|
+
Always set the logs to be verbose in `config.yaml` and make sure the following logging_config.yaml exists and is set:
|
|
216
|
+
|
|
217
|
+
```yaml
|
|
218
|
+
version: 1
|
|
219
|
+
disable_existing_loggers: false
|
|
220
|
+
|
|
221
|
+
root:
|
|
222
|
+
level: DEBUG
|
|
223
|
+
|
|
224
|
+
loggers:
|
|
225
|
+
codeplain:
|
|
226
|
+
level: DEBUG
|
|
227
|
+
propagate: true
|
|
228
|
+
git:
|
|
229
|
+
level: DEBUG
|
|
230
|
+
propagate: true
|
|
231
|
+
transitions:
|
|
232
|
+
level: DEBUG
|
|
233
|
+
propagate: true
|
|
234
|
+
transitions.extensions.diagrams:
|
|
235
|
+
level: DEBUG
|
|
236
|
+
propagate: true
|
|
237
|
+
anthropic:
|
|
238
|
+
level: DEBUG
|
|
239
|
+
propagate: true
|
|
240
|
+
openai:
|
|
241
|
+
level: DEBUG
|
|
242
|
+
propagate: true
|
|
243
|
+
google:
|
|
244
|
+
level: DEBUG
|
|
245
|
+
propagate: true
|
|
246
|
+
google_genai:
|
|
247
|
+
level: DEBUG
|
|
248
|
+
propagate: true
|
|
249
|
+
httpx:
|
|
250
|
+
level: DEBUG
|
|
251
|
+
propagate: true
|
|
252
|
+
httpcore:
|
|
253
|
+
level: DEBUG
|
|
254
|
+
propagate: true
|
|
255
|
+
urllib3:
|
|
256
|
+
level: DEBUG
|
|
257
|
+
propagate: true
|
|
258
|
+
requests:
|
|
259
|
+
level: DEBUG
|
|
260
|
+
propagate: true
|
|
261
|
+
asyncio:
|
|
262
|
+
level: DEBUG
|
|
263
|
+
propagate: true
|
|
264
|
+
```
|
|
265
|
+
|
|
266
|
+
### 2. Renderer memory under `plain_modules/<module>/.memory/`
|
|
267
|
+
|
|
268
|
+
The renderer writes structured JSON notes about its own iteration there. The most useful is `conformance_test_memory/<issue_id>.json`, with fields like:
|
|
269
|
+
|
|
270
|
+
```json
|
|
271
|
+
{
|
|
272
|
+
"functionality": "Agent CLI",
|
|
273
|
+
"initial_issue_summary": "...what was failing at the start...",
|
|
274
|
+
"fix_attempt_summary": "...what the renderer changed...",
|
|
275
|
+
"current_issue_summary": "...what is still wrong now...",
|
|
276
|
+
"resolution_status": "UNRESOLVED",
|
|
277
|
+
"key_learnings": "...the renderer's own diagnosis of why..."
|
|
278
|
+
}
|
|
279
|
+
```
|
|
280
|
+
|
|
281
|
+
This is the renderer telling you, in its own words, what it tried and what is stuck. A `resolution_status: "UNRESOLVED"` is a hard signal that the loop is not converging — read the `key_learnings` field; it usually pinpoints the ambiguity in the spec or the limit of what the renderer can infer.
|
|
282
|
+
|
|
283
|
+
Refresh these files on each pass while a retry loop is active — the renderer rewrites them between attempts.
|
|
284
|
+
|
|
285
|
+
### 3. The TUI (last resort, agent can't see it)
|
|
286
|
+
|
|
287
|
+
If the user is running in Mode B (TUI visible to them, not to you), the script output is also painted on the TUI live. The agent cannot read it directly. When you need it and it isn't in `codeplain.log`, **ask the user to paste the latest test-output panel of the TUI**, or to relaunch in `--headless` so the log captures everything.
|
|
288
|
+
|
|
289
|
+
## Spec-deviation classification
|
|
290
|
+
|
|
291
|
+
This is the most important active judgment the skill makes. Once a functionality enters a retry loop (unit-test or conformance), the renderer iterates — changing source code, and sometimes changing its own conformance tests — trying to satisfy the test-script output. **Not every iteration is honest.** Each iteration must be classified before deciding whether to let the renderer continue, stop and fix the spec, or stop and fix the test script.
|
|
292
|
+
|
|
293
|
+
The inputs to the classifier are:
|
|
294
|
+
|
|
295
|
+
1. The **spec** that governs the failing functionality — the functional spec and its acceptance test(s) in the `.plain` file.
|
|
296
|
+
2. The **test-script output** for the most recent attempt, from `codeplain.log` per the section above.
|
|
297
|
+
3. The renderer's own **`.memory/conformance_test_memory/<id>.json`** for this functionality (when present).
|
|
298
|
+
4. The newest **conformance test file** the renderer just edited under `conformance_tests/<module>/<fname>/`.
|
|
299
|
+
5. The newest **implementation file(s)** the renderer just edited under `plain_modules/<module>/src/`.
|
|
300
|
+
|
|
301
|
+
With these in hand, place the current iteration into one of four buckets:
|
|
302
|
+
|
|
303
|
+
### Bucket 1 — Honest convergence (let it continue)
|
|
304
|
+
|
|
305
|
+
Signals:
|
|
306
|
+
|
|
307
|
+
- Failure count is **going down** across attempts (e.g. 5 failed → 3 failed → 1 failed).
|
|
308
|
+
- The set of failing test names is shrinking and is a *subset* of the previous attempt's failures.
|
|
309
|
+
- The conformance test file is **unchanged** between attempts — only the implementation is changing.
|
|
310
|
+
- `key_learnings` in memory (if present) reads as legitimate technical reasoning about the framework / language / library, not about reinterpreting the spec.
|
|
311
|
+
|
|
312
|
+
Action: let the renderer keep going, up to the threshold (5 conformance attempts / 3 unit-test attempts). Report progress to the user with the per-attempt failure count.
|
|
313
|
+
|
|
314
|
+
### Bucket 2 — Spec under-specification (stop and fix the spec)
|
|
315
|
+
|
|
316
|
+
Signals — any one of these is enough:
|
|
317
|
+
|
|
318
|
+
- The **same test** fails every attempt with the **same assertion**, and the assertion checks something the functional spec **does not explicitly say** (e.g. exact exit code, exact error wording, specific status field).
|
|
319
|
+
- `current_issue_summary` in memory describes a question the spec does not answer (e.g. "unclear whether `--help` should exit 0 or 2"). The `agent_cli_commander_exit_handling_issue_v2.json` example in this repo — "exit codes were 1 instead of the expected 0 or 2" — is exactly this shape when the spec didn't pin the exit code.
|
|
320
|
+
- The renderer's `fix_attempt_summary` keeps oscillating between two opposite interpretations of the same requirement (regex variant A this attempt, variant B next attempt, A again the attempt after).
|
|
321
|
+
- The conformance test file is being rewritten between attempts in a way that **changes which behavior is being asserted**, not just how it is asserted.
|
|
322
|
+
|
|
323
|
+
Action: stop the renderer (SIGINT). Hand off to `debug-specs` or directly to an inline spec edit / `add-implementation-requirement`. The spec must be tightened so there is only one valid behavior to converge on. Then resume with `--render-from <N>` for the offending functionality.
|
|
324
|
+
|
|
325
|
+
### Bucket 3 — Renderer is drifting away from the spec (stop immediately)
|
|
326
|
+
|
|
327
|
+
The dangerous case. Signals:
|
|
328
|
+
|
|
329
|
+
- The renderer **rewrote a conformance test to weaken the assertion** — the test now accepts behavior the original spec does not allow. Compare the latest conformance test file to the previous attempt's; an assertion that loosened a `===` to a `>=`, removed an `expect`, or wrapped behavior in a `try/catch` to swallow failures is a drift signal.
|
|
330
|
+
- The renderer **deleted code that implemented a requirement** to make a test pass (a `try/catch` that now silently returns 0, a feature flag short-circuit, a hard-coded return value matching the test). Visible by diffing `plain_modules/<module>/src/...` across attempts.
|
|
331
|
+
- The renderer's `key_learnings` reads like rationalization rather than diagnosis (e.g. "adjusted assertion to match observed behavior", "removed strict check that was causing failures").
|
|
332
|
+
- Failure count is going **up** between attempts.
|
|
333
|
+
|
|
334
|
+
Action: stop the renderer **immediately**, don't wait for the threshold. Two parallel fixes are needed:
|
|
335
|
+
|
|
336
|
+
1. **Spec side** — the spec was probably specific enough; the renderer chose the path of least resistance. Either tighten the acceptance test so the loophole is closed (`add-functional-spec` / inline edit), or add an implementation req that names the requirement explicitly (`add-implementation-requirement`).
|
|
337
|
+
2. **Generated-code side** — do nothing; the broken code under `plain_modules/` will be overwritten on resume. **Never** hand-edit it.
|
|
338
|
+
|
|
339
|
+
Then resume with `--render-from <N>` (or `--force-render` if the drift contaminated earlier functionalities). Re-running `plain-healthcheck` first is mandatory.
|
|
340
|
+
|
|
341
|
+
### Bucket 4 — The test script itself is the source of the failure (Pathology F)
|
|
342
|
+
|
|
343
|
+
Signals:
|
|
344
|
+
|
|
345
|
+
- Test-script output shows a **toolchain** error (`vitest: command not found`, `python: No module named pytest`, Node version too old).
|
|
346
|
+
- Output is `No test files found` / `no tests collected` while the conformance tests do exist on disk — a glob/path/staging bug in the script.
|
|
347
|
+
- The script is failing **before** running any framework command at all (`die: prepared environment missing`, missing argument, etc.).
|
|
348
|
+
- The failure mode is identical across functionalities, not specific to the spec being rendered.
|
|
349
|
+
|
|
350
|
+
Action: this is **not** a spec problem. Stop the renderer and hand off to the matching `implement-*-testing-script` skill, or have the user repair the script directly. The spec stays as-is.
|
|
351
|
+
|
|
352
|
+
### Classifier discipline
|
|
353
|
+
|
|
354
|
+
- Run the classifier **only** when a retry loop is actually in progress (`ATTEMPT_COUNTER >= 2`). For a first attempt that just failed, there isn't enough data yet — the renderer hasn't done a fix pass.
|
|
355
|
+
- Show your classification reasoning to the user before acting on Bucket 2 or Bucket 3. State the bucket, the concrete evidence ("test `should exit 0 on --help` has failed 3 attempts; spec text does not specify exit code"), and the proposed hand-off skill. Get their go-ahead before SIGINT.
|
|
356
|
+
- If two buckets seem to apply, prefer Bucket 4 → Bucket 3 → Bucket 2 → Bucket 1 in that order. A script bug shadows everything; a drift is worse than under-specification; under-specification is worse than honest iteration.
|
|
357
|
+
|
|
358
|
+
## Phase 1 — Monitor loop
|
|
359
|
+
|
|
360
|
+
Run the loop until one of these terminal conditions holds: the process exits successfully, the process exits with failure, or you (with the user's approval) kill it.
|
|
361
|
+
|
|
362
|
+
On each iteration, in this order:
|
|
363
|
+
|
|
364
|
+
### 1a. Read the new log bytes (primary)
|
|
365
|
+
|
|
366
|
+
Using the incremental read recipe above, pull only the bytes appended since the last pass. Walk the new text against the pattern catalog and update `CURRENT_FUNCTIONALITY`, `LAST_COMPLETED_FUNCTIONALITY`, and `ATTEMPT_COUNTER`. Because verbose mode is on by default, this chunk also contains the **raw test-script output** (vitest summaries, tsc errors, stack traces) sandwiched between the renderer's wrapper lines — capture failure counts, failing test names, and key assertion text. Note any pathology hits, but don't act on them yet; finish the pass first so the report is consistent.
|
|
367
|
+
|
|
368
|
+
If there are **no new bytes**, note the elapsed time since the last log activity (`stat` mtime works). Hitting 2 minutes of silence with the process alive triggers Pathology E.
|
|
369
|
+
|
|
370
|
+
### 1b. Is the process alive?
|
|
371
|
+
|
|
372
|
+
- Mode A: `ps -p <pid>` (or `kill -0 <pid>`). If gone, capture the exit code via `wait`, or fall back to the log's final block (success / failure banner).
|
|
373
|
+
- Mode B: `pgrep -fl codeplain` should still return the run. If not, the user already stopped it (or it finished).
|
|
374
|
+
|
|
375
|
+
If the process is gone but you haven't yet seen a `✓` or `✗` banner in the log, do one more incremental read — the banner is the very last thing codeplain writes, and the process exits immediately after.
|
|
376
|
+
|
|
377
|
+
### 1c. Inspect the filesystem (corroboration only)
|
|
378
|
+
|
|
379
|
+
This is a secondary signal — it confirms what the log already said. Don't go on a fishing trip here.
|
|
380
|
+
|
|
381
|
+
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:
|
|
382
|
+
|
|
383
|
+
- New files under `plain_modules/<module>/src/` confirm the functionality is being implemented.
|
|
384
|
+
- New folders under `conformance_tests/<module>/` confirm the functionality has reached the conformance phase.
|
|
385
|
+
- 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.
|
|
386
|
+
|
|
387
|
+
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.
|
|
388
|
+
|
|
389
|
+
Generated code under `plain_modules/` and `conformance_tests/` is **read-only**. Never edit it. Edits go in the `.plain` files.
|
|
390
|
+
|
|
391
|
+
### 1d. Cadence
|
|
392
|
+
|
|
393
|
+
Poll every 10–30 seconds. Faster than 10s is noisy and wastes tool calls; slower than 30s makes the user feel ignored. Tighten the cadence (down to 5s) when something looks off — climbing `ATTEMPT_COUNTER`, a new error keyword in the latest log chunk, an unexpectedly quiet log. Loosen the cadence (out to 60s) during long, healthy "rendering functionality N" stretches where the log is moving steadily and nothing alarming has appeared.
|
|
394
|
+
|
|
395
|
+
The log read is cheap (incremental), so the bottleneck is the user-facing report, not the I/O. Prefer to err on the tighter side.
|
|
396
|
+
|
|
397
|
+
### 1e. Report to the user
|
|
398
|
+
|
|
399
|
+
After each pass, give the user a **short** status line — one or two sentences, built from the three state variables. Examples:
|
|
400
|
+
|
|
401
|
+
- `Functionality 3/? in flight; unit tests just passed, conformance prep starting.` (after a `Implementing conformance tests...` line)
|
|
402
|
+
- `Functionality 4 is on conformance attempt 4 of an open loop; unit tests still green. One more attempt and I'll flag it.` (`ATTEMPT_COUNTER = 4`)
|
|
403
|
+
- `No new log lines in 95s; PID 12345 still alive. Likely an API call in flight.` (silent log, alive process)
|
|
404
|
+
- `Last completed: functionality 2. Currently rendering functionality 3.` (resume-readiness summary)
|
|
405
|
+
|
|
406
|
+
Do not dump the raw log unless the user asks for it. When you do dump it, paste only the relevant block (e.g. the failure banner with the surrounding 10 lines), not the entire file.
|
|
407
|
+
|
|
408
|
+
## Phase 2 — Pathologies and intervention
|
|
409
|
+
|
|
410
|
+
These are the patterns that justify stopping the renderer. For each, the workflow is the same: **detect → confirm with the user → stop → diagnose → fix the specs → resume**.
|
|
411
|
+
|
|
412
|
+
### Pathology A — Conformance loop that won't converge
|
|
413
|
+
|
|
414
|
+
Symptom: `Running conformance tests attempt <N>` keeps climbing for the **same** functionality, with `Fixing conformance test for functionality <N>` between attempts. Unit tests stay green; only the conformance step fails.
|
|
415
|
+
|
|
416
|
+
Threshold: **5 attempts on the same functionality** is the heuristic. The renderer will keep going (the real `codeplain.log` in this repo shows it climbing to 10), but each attempt costs credits and rarely converges if it hasn't by 5.
|
|
417
|
+
|
|
418
|
+
Do **not** treat hitting the threshold as the diagnosis. From `ATTEMPT_COUNTER >= 2` onwards, run the [Spec-deviation classification](#spec-deviation-classification) classifier each pass. The bucket it returns is the diagnosis:
|
|
419
|
+
|
|
420
|
+
- **Bucket 1** — keep going until the threshold; report the per-attempt failure count.
|
|
421
|
+
- **Bucket 2** — stop now (regardless of how close to the threshold you are); hand off to `debug-specs` / `add-implementation-requirement` / inline spec edit. The spec is under-specified.
|
|
422
|
+
- **Bucket 3** — stop **immediately**, do not wait for the threshold; the renderer is drifting. Tighten the spec or acceptance test before resuming.
|
|
423
|
+
- **Bucket 4** — the test script is the problem; route to Pathology F instead.
|
|
424
|
+
|
|
425
|
+
### Pathology B — `Functional spec too complex!`
|
|
426
|
+
|
|
427
|
+
The renderer rejected a spec because it would imply >200 LOC. Stop is automatic — the renderer aborts. Hand off to `break-down-func-spec`.
|
|
428
|
+
|
|
429
|
+
### Pathology C — Missing concept / unknown definition / import error
|
|
430
|
+
|
|
431
|
+
The renderer can't resolve a reference. Stop is automatic. Hand off to `add-concept` (or fix the `import` / `requires` chain). Re-run `plain-healthcheck` before resuming — a dry-run will catch this faster next time.
|
|
432
|
+
|
|
433
|
+
### Pathology D — Conflicting specs
|
|
434
|
+
|
|
435
|
+
Renderer surfaces a conflict, or you can see two specs in the same module that contradict each other in the generated code. Hand off to `resolve-spec-conflict`.
|
|
436
|
+
|
|
437
|
+
### Pathology E — Renderer stalled with no log activity
|
|
438
|
+
|
|
439
|
+
No new bytes appended to `codeplain.log` for **>2 minutes** *and* the process is still alive. Detect it via `stat -f %m codeplain.log` (mtime) and `wc -c` (size unchanged across passes). Usually the API is slow; occasionally the renderer is wedged. Action:
|
|
440
|
+
|
|
441
|
+
1. Confirm the process is still alive (`ps -p <pid>`).
|
|
442
|
+
2. Confirm the API is reachable (`curl -sSI https://api.codeplain.ai` or whatever `--api` points at).
|
|
443
|
+
3. Wait one more cadence cycle. If the log is still frozen, ask the user whether to stop.
|
|
444
|
+
|
|
445
|
+
### Pathology F — Test scripts themselves are broken
|
|
446
|
+
|
|
447
|
+
Symptom: log shows the unit-test or conformance-test **script** itself failing (non-zero exit, syntax error, missing dependency) on every attempt, rather than the renderer's code failing the tests. The fix is **not** a spec change — it is a `test_scripts/` change. Stop and hand off to the matching `implement-*-testing-script` skill, or have the user repair the script directly.
|
|
448
|
+
|
|
449
|
+
### Pathology G — Unit-test retry loop
|
|
450
|
+
|
|
451
|
+
Less common than the conformance loop, but the same shape: `Running unit tests script ... (attempt: <N>)` with `<N>` climbing for the same functionality. Threshold: **3 attempts**. From `ATTEMPT_COUNTER >= 2` onwards, run the same [Spec-deviation classification](#spec-deviation-classification) classifier and act on the bucket it returns. The most common root causes for unit-test loops are an implementation req that doesn't match the unit-test scaffolding (Bucket 2), or test reqs / framework usage that the renderer is satisfying by weakening assertions (Bucket 3). Hand-off is typically `add-implementation-requirement` or `debug-specs`.
|
|
452
|
+
|
|
453
|
+
### How to actually stop the renderer
|
|
454
|
+
|
|
455
|
+
Always **SIGINT first**, never SIGKILL unless SIGINT fails:
|
|
456
|
+
|
|
457
|
+
```bash
|
|
458
|
+
kill -INT <pid>
|
|
459
|
+
```
|
|
460
|
+
|
|
461
|
+
SIGINT lets codeplain flush the log and close out the run cleanly. Wait ~5s, then check `ps -p <pid>`. If still alive, repeat once. Only after a second SIGINT has failed should you escalate to `kill -TERM <pid>` and, last resort, `kill -9 <pid>`.
|
|
462
|
+
|
|
463
|
+
After the process is gone, do **one** final incremental read of the log — the renderer often writes a closing block on shutdown (`✗ rendering failed` with render id, input file, used credits, render time). Capture it; it's the most useful single artifact for the user.
|
|
464
|
+
|
|
465
|
+
The value of `LAST_COMPLETED_FUNCTIONALITY` you've been tracking is what you'll feed `--render-from` on resume (as `<N+1>`). If you weren't tracking it carefully, recover it now with:
|
|
466
|
+
|
|
467
|
+
```bash
|
|
468
|
+
grep -E "Rendering functionality [0-9]+:" codeplain.log | tail -n 1
|
|
469
|
+
```
|
|
470
|
+
|
|
471
|
+
and subtract 1 if the last functionality didn't reach a clean conformance pass.
|
|
472
|
+
|
|
473
|
+
## Phase 3 — Fix + resume
|
|
474
|
+
|
|
475
|
+
1. **Hand off to the right edit skill.** Don't do the spec edit inside this skill — invoke the dedicated one (`debug-specs`, `resolve-spec-conflict`, `break-down-func-spec`, `add-implementation-requirement`, `add-concept`, `add-functional-spec`, etc.). Pass it concrete evidence: the offending log lines, the file paths under `plain_modules/` you read, and the spec the user agreed is wrong.
|
|
476
|
+
2. **Re-run `plain-healthcheck`** after the fix. If it FAILs, do not resume — fix the next thing first.
|
|
477
|
+
3. **Resume with `--render-from`.** If the last fully-completed functionality was `<N>`, resume with the next one:
|
|
478
|
+
|
|
479
|
+
```bash
|
|
480
|
+
codeplain <module>.plain --headless --render-from <N+1> <same-other-flags-as-before>
|
|
481
|
+
```
|
|
482
|
+
|
|
483
|
+
`--render-from` includes its argument, so the value passed is the **first functionality you want re-rendered**, not the last one that already passed. If nothing completed, omit `--render-from` and start over from the beginning. If only a single functionality needs re-rendering and the rest are fine, prefer `--render-range <N>` instead.
|
|
484
|
+
|
|
485
|
+
4. **Re-enter the monitor loop** in Phase 1.
|
|
486
|
+
|
|
487
|
+
If the fix required regenerating a module's earlier output (e.g. a definition change that ripples backwards), use `--force-render` instead of `--render-from`. Confirm this trade-off with the user — it costs more credits.
|
|
488
|
+
|
|
489
|
+
## Phase 4 — Wrap up
|
|
490
|
+
|
|
491
|
+
When the renderer exits successfully:
|
|
492
|
+
|
|
493
|
+
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.
|
|
494
|
+
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.
|
|
495
|
+
3. Remind the user of the **side-channel commands** they may want to run themselves, based on the config:
|
|
496
|
+
- `./test_scripts/<unittests-script>` for unit tests,
|
|
497
|
+
- `./test_scripts/<prepare-environment-script>` to set up the test env,
|
|
498
|
+
- `./test_scripts/<conformance-tests-script>` for conformance tests.
|
|
499
|
+
Only mention the scripts the project's `config.yaml` actually declares.
|
|
500
|
+
4. If `--copy-build` was set, confirm the build was copied to `--build-dest`. If `--copy-conformance-tests` was set, same for `--conformance-tests-dest`.
|
|
501
|
+
|
|
502
|
+
When the renderer exits with a failure that wasn't intercepted by Phase 2 (e.g. transient API error, auth failure), report the failure block from the log and ask the user whether to retry (`--render-from` from the last completed ID) or to investigate.
|
|
503
|
+
|
|
504
|
+
## Anti-patterns
|
|
505
|
+
|
|
506
|
+
- **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.
|
|
507
|
+
- **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.
|
|
508
|
+
- **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.
|
|
509
|
+
- **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."
|
|
510
|
+
- **Trusting the filesystem over the log.** The filesystem only updates when a step completes; the log updates the moment any step *starts*, retries, or fails. When they disagree, the log is right.
|
|
511
|
+
- **Letting a conformance loop run indefinitely.** Each attempt costs credits and the renderer will keep going past any reasonable point (this repo's `codeplain.log` climbed to attempt 10 before bailing). The threshold is 5; surface it to the user immediately.
|
|
512
|
+
- **Re-running the whole module after a small fix.** Use `--render-from <N+1>` or `--render-range <N>`. Only fall back to `--force-render` when a backward dependency genuinely changed.
|
|
513
|
+
- **Trying to read the TUI.** You can't see it. Use the log file and filesystem; ask the user to paste the TUI's test panel if the log is missing verbose output.
|
|
514
|
+
- **Dumping raw log into chat on every pass.** Summarize from the three state variables (`CURRENT_FUNCTIONALITY`, `LAST_COMPLETED_FUNCTIONALITY`, `ATTEMPT_COUNTER`) plus a classifier verdict when a loop is in progress. The user wants signal, not transcript.
|
|
515
|
+
- **Skipping `plain-healthcheck` because "dry-run passed earlier".** Specs drift; configs drift. Re-run it before every real render and after every fix.
|
|
516
|
+
- **`kill -9` as a first move.** Always SIGINT first — codeplain needs to flush its log and clean up.
|
|
517
|
+
|
|
518
|
+
## Validation checklist
|
|
519
|
+
|
|
520
|
+
- [ ] Target `.plain` file and governing `config.yaml` were confirmed before launch
|
|
521
|
+
- [ ] `plain-healthcheck` is PASS as of the latest spec state
|
|
522
|
+
- [ ] `CODEPLAIN_API_KEY` is set, or `--api-key` was supplied
|
|
523
|
+
- [ ] Launch mode (`--headless` agent-launched vs. user-launched TUI) was chosen with the user
|
|
524
|
+
- [ ] Renderer PID is known (or, in Mode B, confirmed alive via `pgrep`)
|
|
525
|
+
- [ ] Log file path is known and is observed to be growing
|
|
526
|
+
- [ ] Log path was captured during pre-flight and a byte offset is being tracked across passes
|
|
527
|
+
- [ ] Monitor loop reads only the new bytes of `codeplain.log` each pass (no full re-reads on routine passes)
|
|
528
|
+
- [ ] `CURRENT_FUNCTIONALITY`, `LAST_COMPLETED_FUNCTIONALITY`, and `ATTEMPT_COUNTER` are maintained across passes
|
|
529
|
+
- [ ] Filesystem inspection is used only to corroborate log signals, not as the primary signal
|
|
530
|
+
- [ ] Status reports to the user are short (≤2 sentences) and signal-driven, not transcript dumps
|
|
531
|
+
- [ ] Test-script output (failure counts, failing test names, assertion bodies, stack traces) is being extracted from the verbose log chunks each pass
|
|
532
|
+
- [ ] When `ATTEMPT_COUNTER >= 2`, the spec-deviation classifier ran and returned a bucket (1–4) before any stop/continue decision
|
|
533
|
+
- [ ] If a classifier verdict caused a stop, the bucket and concrete evidence (failing test name + spec gap, or the diff that showed drift) was shown to the user before SIGINT
|
|
534
|
+
- [ ] `.memory/conformance_test_memory/<id>.json` was read (if present) to capture `current_issue_summary` and `resolution_status`
|
|
535
|
+
- [ ] Pathologies (loop / complexity / missing concept / conflict / stall / broken test script / drift) are recognized and acted on
|
|
536
|
+
- [ ] Renderer was stopped with SIGINT (escalating only on failure) — not SIGKILL by default
|
|
537
|
+
- [ ] Spec fixes were delegated to the right edit skill, not done inside this skill
|
|
538
|
+
- [ ] Resumes use `--render-from <N+1>` (or `--render-range`) — `--force-render` only with user approval
|
|
539
|
+
- [ ] On success, final render id / credits / time were reported and side-channel test commands were surfaced
|
|
540
|
+
- [ ] No generated code under `plain_modules/` or `conformance_tests/` was modified
|
package/package.json
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "plain-forge",
|
|
3
|
+
"version": "1.0.1",
|
|
4
|
+
"description": "Conversational spec-writing tool for ***plain specification language",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"engines": {
|
|
7
|
+
"node": ">=18"
|
|
8
|
+
},
|
|
9
|
+
"repository": {
|
|
10
|
+
"type": "git",
|
|
11
|
+
"url": "git+https://github.com/Codeplain-ai/plain-forge.git"
|
|
12
|
+
},
|
|
13
|
+
"homepage": "https://github.com/Codeplain-ai/plain-forge",
|
|
14
|
+
"bugs": {
|
|
15
|
+
"url": "https://github.com/Codeplain-ai/plain-forge/issues"
|
|
16
|
+
},
|
|
17
|
+
"license": "MIT",
|
|
18
|
+
"keywords": [
|
|
19
|
+
"plain",
|
|
20
|
+
"codeplain",
|
|
21
|
+
"claude-code",
|
|
22
|
+
"skills",
|
|
23
|
+
"spec",
|
|
24
|
+
"ai"
|
|
25
|
+
],
|
|
26
|
+
"bin": {
|
|
27
|
+
"plain-forge": "bin/cli.mjs"
|
|
28
|
+
},
|
|
29
|
+
"files": [
|
|
30
|
+
"bin/cli.mjs",
|
|
31
|
+
"forge"
|
|
32
|
+
],
|
|
33
|
+
"scripts": {
|
|
34
|
+
"build": "tsx bin/forge-build.ts",
|
|
35
|
+
"clean": "tsx bin/forge-build.ts --clean"
|
|
36
|
+
},
|
|
37
|
+
"devDependencies": {
|
|
38
|
+
"@types/node": "^22.10.0",
|
|
39
|
+
"tsx": "^4.19.2",
|
|
40
|
+
"typescript": "^5.7.2"
|
|
41
|
+
}
|
|
42
|
+
}
|