plain-forge 1.0.4 → 1.0.5
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 +29 -8
- package/forge/rules/integration-embedded.md +22 -6
- package/forge/skills/add-acceptance-test/SKILL.md +24 -0
- package/forge/skills/add-concept/SKILL.md +23 -0
- package/forge/skills/add-functional-spec/SKILL.md +23 -0
- package/forge/skills/add-functional-specs/SKILL.md +24 -0
- package/forge/skills/add-implementation-requirement/SKILL.md +24 -0
- package/forge/skills/add-test-requirement/SKILL.md +22 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -4,18 +4,39 @@
|
|
|
4
4
|
|
|
5
5
|
# plain-forge
|
|
6
6
|
|
|
7
|
-
A
|
|
7
|
+
A toolkit for working with [***plain](https://plainlang.org) projects from inside your AI coding agent of choice — Claude Code, Codex, ForgeCode, OpenCode, and any other agent that reads from a standard skills directory. plain-forge ships skills, rules, and docs that turn a conversation into complete `.plain` spec files, then keeps maintaining them across the lifetime of the project. The specs are rendered into production-ready code by the [codeplain](https://codeplain.ai) renderer.
|
|
8
8
|
|
|
9
|
-
##
|
|
9
|
+
## What plain-forge does
|
|
10
10
|
|
|
11
|
-
|
|
11
|
+
plain-forge is organized around four kinds of work, each with its own entry-point skill (and a long tail of supporting skills behind it).
|
|
12
12
|
|
|
13
|
-
1.
|
|
14
|
-
2. **What technologies should it use?** — Pick the stack and architecture: language, frameworks, data storage, external services, project structure, and any other stack-wide constraints. Produces the `***implementation reqs***`.
|
|
15
|
-
3. **How should testing be done?** — Decide the testing strategy: framework, test types in scope, conformance/acceptance tests, environment-preparation scripts, layout, and execution. Produces the `***test reqs***`, any `***acceptance tests***`, the runnable scripts under `test_scripts/`, and the `config.yaml`(s) wiring them in. plain-forge then probes your machine to confirm everything those scripts need is actually installed.
|
|
16
|
-
4. **Validate and hand off** — plain-forge identifies the final module in the dependency chain and runs `codeplain <module>.plain --dry-run` itself to catch any static errors (syntax, undefined concepts, broken `import`/`requires` chains, complexity violations, conflicts). It fixes the `.plain` files until the dry-run passes, then hands you the exact `codeplain <module>.plain` command (plus any test scripts) so the real render starts from a clean spec.
|
|
13
|
+
### 1. Bootstrap a new project
|
|
17
14
|
|
|
18
|
-
|
|
15
|
+
Pick whichever entry point matches how much upfront design you want:
|
|
16
|
+
|
|
17
|
+
- **`forge-plain`** — full end-to-end interview. One question at a time, immediate writes to disk, covers product → tech stack → testing → validation in four phases. Produces a complete `.plain` project with `config.yaml`, test scripts, and a successful `codeplain --dry-run` before handing off.
|
|
18
|
+
- **`init-plain-project`** — lightweight scaffold. Asks only about the base technology, project kind, and whether conformance testing is on; emits a template module, a stub top-level module, the testing scripts, and a `config.yaml`. No interview, no specs — pair it with `add-feature` to grow the project feature by feature.
|
|
19
|
+
|
|
20
|
+
### 2. Grow an existing project
|
|
21
|
+
|
|
22
|
+
- **`add-feature`** — takes a feature request in plain English and runs the same one-question-at-a-time loop that `forge-plain` uses, scoped to a single feature against an existing `.plain` file.
|
|
23
|
+
- **Per-section authoring skills** — `add-functional-spec`, `add-functional-specs`, `add-implementation-requirement`, `add-test-requirement`, `add-concept`, `add-acceptance-test`, `add-resource`, `add-template`. Each enforces the relevant ***plain syntax rules (concept uniqueness, complexity limits, line-length, linked-resource constraints, …) so hand-authoring doesn't drift from the language.
|
|
24
|
+
- **Module-management skills** — `create-import-module`, `create-requires-module`, `refactor-module`, `consolidate-concepts` for restructuring as the project grows.
|
|
25
|
+
|
|
26
|
+
### 3. Validate and maintain
|
|
27
|
+
|
|
28
|
+
- **`plain-healthcheck`** — the verification gate. Inventories every `.plain` module, validates every `config.yaml`, and dry-runs every top module with the right config. Returns `PASS` / `FAIL` with a numbered punch-list when something is broken.
|
|
29
|
+
- **`init-config-file`** — assembles the canonical `config.yaml` per part of a project from the decisions made during interviewing.
|
|
30
|
+
- **`check-plain-env`** — probes the host machine for everything the project needs (language toolchains, external services, system binaries, drivers, `codeplain` itself) and emits a `PASS` / `WARN` / `FAIL` report with OS-specific install commands.
|
|
31
|
+
- **`analyze-func-specs`** / **`analyze-if-func-spec-too-complex`** / **`break-down-func-spec`** / **`resolve-spec-conflict`** — the spec-quality toolchain that runs behind `add-feature` and `forge-plain` but can also be invoked directly when reviewing or refactoring.
|
|
32
|
+
|
|
33
|
+
### 4. Debug and render
|
|
34
|
+
|
|
35
|
+
- **`debug-specs`** — when the rendered app misbehaves, this traces the generated code back to the spec that caused it and fixes the spec (never the generated code).
|
|
36
|
+
- **`run-codeplain`** — experimental supervised render. Launches `codeplain` for you, tails the log, watches generated code appear under `plain_modules/`, and detects pathologies (stuck conformance loops, complexity errors, missing concepts, render failures). On approval it stops the renderer, hands off to the right spec-edit skill, and resumes.
|
|
37
|
+
- **`implement-unit-testing-script`** / **`implement-conformance-testing-script`** / **`implement-prepare-environment-script`** — generate the per-language testing scripts that the renderer and you both invoke. New languages can be added by these skills without touching any other part of the project.
|
|
38
|
+
|
|
39
|
+
Each skill operates on the same one-question-at-a-time, write-immediately, refine-through-follow-ups loop. Specs land on disk after every answer; later answers fix earlier writes in place. There is no batched interview, no "I'll gather context first," and no hand-authored functional specs — every new spec goes through the authoring skills so the complexity and conflict checks actually run.
|
|
19
40
|
|
|
20
41
|
## Getting Started
|
|
21
42
|
|
|
@@ -38,12 +38,27 @@ Run host discovery **before** the first Phase 1 question. Treat the results as g
|
|
|
38
38
|
- The renderer is allowed to redefine **only** symbols the host does not provide and the contract schema does not capture
|
|
39
39
|
- Naming the symbol by FQN is not optional decoration — it tells the renderer where the type comes from, which prevents a duplicate definition under `plain_modules/`
|
|
40
40
|
|
|
41
|
-
##
|
|
41
|
+
## Link host files at their original path — never copy them into `resources/`
|
|
42
42
|
|
|
43
|
-
|
|
43
|
+
The integration's `.plain` module lives **inside the host codebase** (per the "adopt the host's `.plain` setup verbatim" step). That means host source files are already reachable as linked resources via their host-relative paths. The integration spec references them **in place**; it never duplicates them under `resources/host/`.
|
|
44
|
+
|
|
45
|
+
- Every host file the integration touches (base classes, configuration modules, registries, exception classes, lifecycle hooks) is referenced from the relevant spec using `***linked resource***` syntax with the **path as it exists in the host codebase** — e.g. `[base.py](host_project/integrations/base.py)` if the `.plain` module sits next to `host_project/`
|
|
46
|
+
- **Do NOT copy host files into `resources/host/`.** A copy creates a second source of truth that drifts the moment the host file is edited; the rendered code will then disagree with whatever the host actually ships
|
|
47
|
+
- **Do NOT add host files via the `add-resource` skill's default copy behavior** when that behavior would duplicate the file — point at the existing host path directly
|
|
44
48
|
- **Never inline a host file's contents** into a spec
|
|
45
|
-
- **Never describe a host symbol's shape from memory** — the renderer reads the linked file's bytes and that is the source of truth
|
|
46
|
-
- This obeys the broader [`linked-resources.md`](linked-resources.md) rules
|
|
49
|
+
- **Never describe a host symbol's shape from memory** — the renderer reads the linked file's bytes at its host path and that is the source of truth
|
|
50
|
+
- This still obeys the broader [`linked-resources.md`](linked-resources.md) rules: a directory is not a valid link, a URL is not a valid link, a binary is not a valid link. Only the *location* changes — host files live where the host put them, not under `resources/`
|
|
51
|
+
|
|
52
|
+
### What still belongs under `resources/`
|
|
53
|
+
|
|
54
|
+
This rule applies to **host source code only**. Other artifacts still live under `resources/` exactly like in a non-embedded project:
|
|
55
|
+
|
|
56
|
+
- **Contract schemas authored by the integration** — `resources/contract/<entry-point>.schema.json`
|
|
57
|
+
- **Configuration schema** — `resources/config.schema.json`
|
|
58
|
+
- **Captured probe responses** (from the live-API cross-check) — `resources/fixtures/<endpoint>.<case>.json`
|
|
59
|
+
- **Static lookup tables** the integration owns — `resources/error-map.yaml`, `resources/retry-policy.yaml`, etc.
|
|
60
|
+
|
|
61
|
+
The rule of thumb: if the host wrote it and ships it, link it where the host put it. If the integration is authoring it for the first time, it goes under `resources/`.
|
|
47
62
|
|
|
48
63
|
## The contract spec declares inheritance, not duplication
|
|
49
64
|
|
|
@@ -125,7 +140,7 @@ Before declaring an embedded integration done, in addition to the shared checkli
|
|
|
125
140
|
- [ ] `host-codebase` concept records host root path, host language + version, host dependency manager + manifest, target package path, host base class import path, target generated-class FQN, and the host conventions the contract follows
|
|
126
141
|
- [ ] Contract spec carries renderer directives (target language, target file path, target class name, host base class to subclass, host-package pins) and **links** to the contract schema; no class body is inlined
|
|
127
142
|
- [ ] Every host symbol referenced in any spec uses its fully qualified import path and is tagged "imported from the host codebase; do not redefine"
|
|
128
|
-
- [ ] Every host file the integration touches has been
|
|
143
|
+
- [ ] Every host file the integration touches is linked at its **original host-relative path** — no host file has been copied into `resources/host/` or anywhere else, and no host file contents are inlined in any spec
|
|
129
144
|
- [ ] `forge-plain` Phase 2's tech-stack decisions are transcribed verbatim from the host (no independent stack choices)
|
|
130
145
|
- [ ] Host-package version pins are copied into `***implementation reqs***`
|
|
131
146
|
- [ ] `prepare_environment` copies the host into `.tmp/<lang>_<arg>/`, overlays `plain_modules/<module>/` at the target package path, installs the merged tree's dependencies, and is the working folder conformance attaches to
|
|
@@ -140,7 +155,8 @@ Before declaring an embedded integration done, in addition to the shared checkli
|
|
|
140
155
|
|
|
141
156
|
- **Choosing a different language, framework, or dependency manager than the host.** The host stack is inherited; cross-stack `requires` chains are forbidden by [`requires-modules.md`](requires-modules.md)
|
|
142
157
|
- **Redefining a host class under `plain_modules/`.** Reference the host symbol by FQN; let the renderer import it
|
|
143
|
-
- **Inlining a host base class body into the contract spec.**
|
|
158
|
+
- **Inlining a host base class body into the contract spec.** Reference the host file as a linked resource **at its original host-relative path** — do not inline its contents and do not copy it into `resources/`
|
|
159
|
+
- **Copying host source files into `resources/host/` (or anywhere under `resources/`).** That creates a second source of truth that silently drifts from the host. Link the host file in place; the integration `.plain` module already lives inside the host codebase, so the path resolves naturally
|
|
144
160
|
- **Hardcoding the host codebase path in any spec or script.** Read it from the env var declared in the configuration concept
|
|
145
161
|
- **Asking the user to design the integration's tech stack.** Read it from the host's manifest files
|
|
146
162
|
- **Authoring an integration spec that contradicts an existing integration in the same host** without first surfacing the conflict and getting explicit user confirmation
|
|
@@ -44,6 +44,30 @@ Key formatting rules:
|
|
|
44
44
|
- Each test bullet is indented under the `***acceptance tests***` header.
|
|
45
45
|
- Multiple acceptance tests can be listed under a single functional spec.
|
|
46
46
|
|
|
47
|
+
### Line syntax (hard rule)
|
|
48
|
+
|
|
49
|
+
**Every line inside `***acceptance tests***` must be its own list item starting with `- `.** ***plain has no concept of bare continuation lines — indented prose without a leading `- ` is **invalid syntax** and the renderer will reject it.
|
|
50
|
+
|
|
51
|
+
- Hard limit: 120 characters per line. If a sentence is too long, **split it at a natural clause boundary into nested `- ` bullets** — never wrap onto an unprefixed line.
|
|
52
|
+
- Nested clarifications are also `- ` items, indented under the parent. The indentation alone is not enough; the leading `- ` is required.
|
|
53
|
+
|
|
54
|
+
BAD — bare continuation lines (invalid ***plain syntax, will not render):
|
|
55
|
+
|
|
56
|
+
```plain
|
|
57
|
+
***acceptance tests***
|
|
58
|
+
- Processing 250 :Task: items should result in 3 batches with the
|
|
59
|
+
last batch containing the remaining 50 items.
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
GOOD — every line starts with `- `:
|
|
63
|
+
|
|
64
|
+
```plain
|
|
65
|
+
***acceptance tests***
|
|
66
|
+
- Processing 250 :Task: items should result in 3 batches.
|
|
67
|
+
- The first two batches each contain 100 items.
|
|
68
|
+
- The last batch contains the remaining 50 items.
|
|
69
|
+
```
|
|
70
|
+
|
|
47
71
|
## Rules
|
|
48
72
|
|
|
49
73
|
### Conformance with the Functional Spec
|
|
@@ -56,6 +56,29 @@ Attributes and constraints are nested sub-bullets:
|
|
|
56
56
|
- Due Date - completion deadline (optional)
|
|
57
57
|
```
|
|
58
58
|
|
|
59
|
+
## Line syntax (hard rule)
|
|
60
|
+
|
|
61
|
+
**Every line inside `***definitions***` must be its own list item starting with `- `.** ***plain has no concept of bare continuation lines — indented prose without a leading `- ` is **invalid syntax** and the renderer will reject it.
|
|
62
|
+
|
|
63
|
+
- Hard limit: 120 characters per line. If a sentence is too long, **split it at a natural clause boundary into nested `- ` bullets** — never wrap onto an unprefixed line.
|
|
64
|
+
- Nested attributes are also `- ` items, indented under the parent. The indentation alone is not enough; the leading `- ` is required.
|
|
65
|
+
|
|
66
|
+
BAD — bare continuation lines (invalid ***plain syntax, will not render):
|
|
67
|
+
|
|
68
|
+
```plain
|
|
69
|
+
- :Task: describes an activity that needs to be done by :User:.
|
|
70
|
+
- Name is a short description that the user provides when creating
|
|
71
|
+
the task and is shown in the task list.
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
GOOD — every line starts with `- `:
|
|
75
|
+
|
|
76
|
+
```plain
|
|
77
|
+
- :Task: describes an activity that needs to be done by :User:.
|
|
78
|
+
- Name is a short description provided when creating the task.
|
|
79
|
+
- The name is shown in the task list.
|
|
80
|
+
```
|
|
81
|
+
|
|
59
82
|
## Validation Checklist
|
|
60
83
|
|
|
61
84
|
- [ ] Name uses `:CamelCase:` notation
|
|
@@ -57,6 +57,29 @@ Each functional spec must be unambiguous — the renderer should have only one r
|
|
|
57
57
|
- All :Participant: members of the :Conversation: can see the new :Message:.
|
|
58
58
|
```
|
|
59
59
|
|
|
60
|
+
### Line syntax (hard rule)
|
|
61
|
+
|
|
62
|
+
**Every line inside `***functional specs***` must be its own list item starting with `- `.** ***plain has no concept of bare continuation lines — indented prose without a leading `- ` is **invalid syntax** and the renderer will reject it.
|
|
63
|
+
|
|
64
|
+
- Hard limit: 120 characters per line. If a sentence is too long, **split it at a natural clause boundary into nested `- ` bullets** — never wrap onto an unprefixed line.
|
|
65
|
+
- Nested clarifications are also `- ` items, indented under the parent. The indentation alone is not enough; the leading `- ` is required.
|
|
66
|
+
|
|
67
|
+
BAD — bare continuation lines (invalid ***plain syntax, will not render):
|
|
68
|
+
|
|
69
|
+
```plain
|
|
70
|
+
- :GatewayWebhook: should hand off :StripeRequest: to :StripeIntegration:.handle(),
|
|
71
|
+
which returns a list of :EventEnvelope: dicts conforming to the gateway's
|
|
72
|
+
contract.
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
GOOD — every line starts with `- `:
|
|
76
|
+
|
|
77
|
+
```plain
|
|
78
|
+
- :GatewayWebhook: should hand off :StripeRequest: to :StripeIntegration:.handle().
|
|
79
|
+
- The method returns a list of :EventEnvelope: dicts.
|
|
80
|
+
- The dicts must conform to the gateway's :EventEnvelope: contract.
|
|
81
|
+
```
|
|
82
|
+
|
|
60
83
|
### Deterministic Interface
|
|
61
84
|
|
|
62
85
|
Specs must be detailed enough that a developer can use the built software without reading the generated code. All external interfaces must be explicit — REST endpoint paths and HTTP methods, CLI command names and arguments, file formats, message schemas, etc. Never leave interface details up to the renderer's discretion.
|
|
@@ -80,6 +80,30 @@ Each functional spec must be unambiguous — the renderer should have only one r
|
|
|
80
80
|
- All :Participant: members of the :Conversation: can see the new :Message:.
|
|
81
81
|
```
|
|
82
82
|
|
|
83
|
+
### Line syntax (hard rule, per spec)
|
|
84
|
+
|
|
85
|
+
**Every line inside `***functional specs***` must be its own list item starting with `- `.** ***plain has no concept of bare continuation lines — indented prose without a leading `- ` is **invalid syntax** and the renderer will reject the whole file.
|
|
86
|
+
|
|
87
|
+
- Hard limit: 120 characters per line. If a sentence is too long, **split it at a natural clause boundary into nested `- ` bullets** — never wrap onto an unprefixed line.
|
|
88
|
+
- Nested clarifications are also `- ` items, indented under the parent. The indentation alone is not enough; the leading `- ` is required.
|
|
89
|
+
- This rule applies to **every** spec in the batch — one bad continuation line invalidates the entire insert.
|
|
90
|
+
|
|
91
|
+
BAD — bare continuation lines (invalid ***plain syntax, will not render):
|
|
92
|
+
|
|
93
|
+
```plain
|
|
94
|
+
- :GatewayWebhook: should hand off :StripeRequest: to :StripeIntegration:.handle(),
|
|
95
|
+
which returns a list of :EventEnvelope: dicts conforming to the gateway's
|
|
96
|
+
contract.
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
GOOD — every line starts with `- `:
|
|
100
|
+
|
|
101
|
+
```plain
|
|
102
|
+
- :GatewayWebhook: should hand off :StripeRequest: to :StripeIntegration:.handle().
|
|
103
|
+
- The method returns a list of :EventEnvelope: dicts.
|
|
104
|
+
- The dicts must conform to the gateway's :EventEnvelope: contract.
|
|
105
|
+
```
|
|
106
|
+
|
|
83
107
|
### Deterministic Interface
|
|
84
108
|
|
|
85
109
|
Specs must be detailed enough that a developer can use the built software without reading the generated code. All external interfaces must be explicit — REST endpoint paths and HTTP methods, CLI command names and arguments, file formats, message schemas, etc. Never leave interface details up to the renderer's discretion.
|
|
@@ -60,6 +60,30 @@ Implementation reqs are bullet points in the `***implementation reqs***` section
|
|
|
60
60
|
|
|
61
61
|
Reference defined `:Concepts:` where they add clarity. Implementation reqs in non-leaf sections apply to all subsections.
|
|
62
62
|
|
|
63
|
+
## Line syntax (hard rule)
|
|
64
|
+
|
|
65
|
+
**Every line inside a section must be its own list item starting with `- `.** ***plain has no concept of bare continuation lines — indented prose without a leading `- ` is **invalid syntax** and the renderer will reject it.
|
|
66
|
+
|
|
67
|
+
- Hard limit: 120 characters per line. If a sentence is too long, **split it at a natural clause boundary into nested `- ` bullets** — never wrap onto an unprefixed line.
|
|
68
|
+
- Nested detail is also a `- ` item, indented under its parent. The indentation alone is not enough; the leading `- ` is required.
|
|
69
|
+
|
|
70
|
+
BAD — bare continuation lines (invalid ***plain syntax, will not render):
|
|
71
|
+
|
|
72
|
+
```plain
|
|
73
|
+
- :Implementation: tech stack will be finalized in Phase 2.
|
|
74
|
+
- Until then, treat this section as a placeholder so the renderer accepts
|
|
75
|
+
the file. Phase 2 will replace this with language, framework, HTTP
|
|
76
|
+
client, packaging, and architecture decisions.
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
GOOD — every line starts with `- `:
|
|
80
|
+
|
|
81
|
+
```plain
|
|
82
|
+
- :Implementation: tech stack will be finalized in Phase 2.
|
|
83
|
+
- Until then, treat this section as a placeholder so the renderer accepts the file.
|
|
84
|
+
- Phase 2 will replace it with language, framework, HTTP client, packaging, and architecture decisions.
|
|
85
|
+
```
|
|
86
|
+
|
|
63
87
|
## Encapsulation Warning
|
|
64
88
|
|
|
65
89
|
`requires` modules only receive functional specs from their dependencies — not implementation reqs. If downstream modules need certain behavior to be visible, that behavior must be expressed in functional specs, not here.
|
|
@@ -59,6 +59,28 @@ Test reqs are bullet points in the `***test reqs***` section:
|
|
|
59
59
|
|
|
60
60
|
Reference predefined concepts like `:ConformanceTests:` and any defined `:Concepts:` where they add clarity.
|
|
61
61
|
|
|
62
|
+
## Line syntax (hard rule)
|
|
63
|
+
|
|
64
|
+
**Every line inside `***test reqs***` must be its own list item starting with `- `.** ***plain has no concept of bare continuation lines — indented prose without a leading `- ` is **invalid syntax** and the renderer will reject it.
|
|
65
|
+
|
|
66
|
+
- Hard limit: 120 characters per line. If a sentence is too long, **split it at a natural clause boundary into nested `- ` bullets** — never wrap onto an unprefixed line.
|
|
67
|
+
- Nested clarifications are also `- ` items, indented under the parent. The indentation alone is not enough; the leading `- ` is required.
|
|
68
|
+
|
|
69
|
+
BAD — bare continuation lines (invalid ***plain syntax, will not render):
|
|
70
|
+
|
|
71
|
+
```plain
|
|
72
|
+
- :ConformanceTests: must mock all external HTTP calls so that the
|
|
73
|
+
test suite remains hermetic and does not depend on network access.
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
GOOD — every line starts with `- `:
|
|
77
|
+
|
|
78
|
+
```plain
|
|
79
|
+
- :ConformanceTests: must mock all external HTTP calls.
|
|
80
|
+
- The test suite must remain hermetic.
|
|
81
|
+
- Tests must not depend on network access.
|
|
82
|
+
```
|
|
83
|
+
|
|
62
84
|
## Validation Checklist
|
|
63
85
|
|
|
64
86
|
- [ ] Describes conformance testing concerns, not unit tests or behavior
|