plain-forge 1.0.20 → 1.0.21-next.55f13d7
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 +1 -0
- package/bin/cli.mjs +1 -1
- package/forge/rules/bullet-continuation.md +16 -6
- package/forge/rules/concise-specs-examples.md +431 -0
- package/forge/rules/concise-specs-integration-examples.md +272 -0
- package/forge/rules/concise-specs.md +80 -0
- package/forge/rules/definitions.md +7 -4
- package/forge/rules/exported-concepts.md +3 -2
- package/forge/rules/func-specs.md +10 -3
- package/forge/rules/impl-reqs.md +8 -4
- package/forge/rules/import-modules.md +4 -6
- package/forge/rules/integration-embedded.md +0 -1
- package/forge/rules/integration-standalone.md +1 -1
- package/forge/rules/integrations.md +6 -7
- package/forge/rules/linked-resources.md +9 -8
- package/forge/rules/module-structure.md +105 -0
- package/forge/rules/required-concepts.md +5 -5
- package/forge/rules/requires-modules.md +37 -21
- package/forge/rules/test-reqs.md +11 -7
- package/forge/skills/add-acceptance-test/SKILL.md +1 -0
- package/forge/skills/add-concept/SKILL.md +2 -1
- package/forge/skills/add-feature/SKILL.md +2 -2
- package/forge/skills/add-functional-spec/SKILL.md +2 -1
- package/forge/skills/add-functional-specs/SKILL.md +2 -1
- package/forge/skills/add-implementation-requirement/SKILL.md +2 -1
- package/forge/skills/add-test-requirement/SKILL.md +1 -0
- package/forge/skills/break-down-func-spec/SKILL.md +1 -0
- package/forge/skills/check-forge-version/SKILL.md +154 -0
- package/forge/skills/check-forge-version/scripts/check-forge-version.mjs +368 -0
- package/forge/skills/create-import-module/SKILL.md +3 -3
- package/forge/skills/create-requires-module/SKILL.md +24 -19
- package/forge/skills/forge-plain/SKILL.md +1 -1
- package/forge/skills/forge-plain/references/checklist.md +2 -1
- package/forge/skills/forge-plain/references/phase-1-product.md +3 -2
- package/forge/skills/forge-plain/references/phase-2-tech.md +1 -1
- package/forge/skills/load-plain-reference/SKILL.md +20 -2
- package/forge/skills/load-plain-reference/references/project-model.md +0 -11
- package/forge/skills/plain-healthcheck/SKILL.md +1 -1
- package/forge/skills/refactor-module/SKILL.md +3 -3
- package/forge/skills/resolve-section-ownership/SKILL.md +65 -0
- package/forge/skills/resolve-spec-conflict/SKILL.md +1 -0
- package/package.json +5 -5
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: File-level structure of .plain modules — frontmatter, sections, ordering, and section ownership
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
# Rules for `.plain` module structure
|
|
6
|
+
|
|
7
|
+
When creating a new `.plain` file or reviewing the structure of a whole file, always follow these rules. Section *content* rules live in the per-section rule files linked below — this file covers only file-level structure.
|
|
8
|
+
|
|
9
|
+
## File skeleton
|
|
10
|
+
- A `.plain` file is one **module**: optional YAML frontmatter between `---` markers, followed by sections marked with `***section name***` headers
|
|
11
|
+
- The frontmatter, when present, must be the first thing in the file
|
|
12
|
+
- Every content line inside a section must be a `- ` list item — see `bullet-continuation.md`
|
|
13
|
+
- Every line must be as short and plain as its content allows — see `concise-specs.md`
|
|
14
|
+
|
|
15
|
+
## Frontmatter fields
|
|
16
|
+
|
|
17
|
+
| Field | Purpose | Rule file |
|
|
18
|
+
|---|---|---|
|
|
19
|
+
| `description` | One-line summary of the module; recommended on every module | — |
|
|
20
|
+
| `import` | Pull definitions, implementation reqs, and test reqs from import modules | `import-modules.md` |
|
|
21
|
+
| `requires` | Attach the module to the build chain of another root module | `requires-modules.md` |
|
|
22
|
+
| `exported_concepts` | Declare which of this module's concepts are visible to modules that `require` it | `exported-concepts.md` |
|
|
23
|
+
| `required_concepts` | Declare concepts an import module expects the importing module to define | `required-concepts.md` |
|
|
24
|
+
|
|
25
|
+
## Sections
|
|
26
|
+
- There are exactly four top-level section markers: `***definitions***`, `***implementation reqs***`, `***test reqs***`, and `***functional specs***`
|
|
27
|
+
- Each section appears **at most once** per file; all are optional (which ones are allowed depends on the module kind below)
|
|
28
|
+
- `***acceptance tests***` is **never a top-level section** — it appears only nested under a single functional spec (see `func-specs.md`)
|
|
29
|
+
- Write sections in canonical order: `***definitions***` → `***implementation reqs***` → `***test reqs***` → `***functional specs***`
|
|
30
|
+
- Definitions come first because every concept must be defined before it is referenced
|
|
31
|
+
- Functional specs come last because they are rendered incrementally and their nested acceptance tests close the file
|
|
32
|
+
|
|
33
|
+
## Module kinds
|
|
34
|
+
- A **root module** lives at the repository root and carries behavior: it contains `***functional specs***`
|
|
35
|
+
- To be renderable, a root module needs at least one functional spec plus the implementation reqs to build it — its own or from an `import`ed module (implementation reqs are not inherited through `requires`). A frontmatter-only file is a valid scaffold, but it cannot be rendered until a functional spec is added
|
|
36
|
+
- A **requires module** is a root module that is used by another root module via `requires` directive (see `requires-modules.md`)
|
|
37
|
+
- An **import module** provides shared content that other modules pull in via `import`: it contains only `***definitions***`, `***implementation reqs***`, and/or `***test reqs***` — never `***functional specs***`, never `requires` (see `import-modules.md`)
|
|
38
|
+
|
|
39
|
+
## Section ownership (where a fact lives)
|
|
40
|
+
- The renderer reads each kind of fact **only from its owning section** — a fact placed in the wrong section is silently ignored, not flagged
|
|
41
|
+
- Before writing any requirement, place it by content:
|
|
42
|
+
- To move facts that are already misplaced, or to split a bullet carrying several kinds of fact, use the `resolve-section-ownership` skill
|
|
43
|
+
|
|
44
|
+
| Content | Owning section | Rule file |
|
|
45
|
+
|---|---|---|
|
|
46
|
+
| Concepts (`:CamelCaseToken:`) | `***definitions***` | `definitions.md` |
|
|
47
|
+
| HOW the software is built — tech stack, architecture, coding standards — and **everything about `:UnitTests:`** | `***implementation reqs***` | `impl-reqs.md` |
|
|
48
|
+
| **Everything about `:ConformanceTests:`** — framework, run command, mocking and network policy | `***test reqs***` | `test-reqs.md` |
|
|
49
|
+
| WHAT the software does — observable, language-agnostic behavior | `***functional specs***` | `func-specs.md` |
|
|
50
|
+
| End-to-end workflow verification of one functional spec | nested `***acceptance tests***` | `func-specs.md` |
|
|
51
|
+
|
|
52
|
+
## Comments
|
|
53
|
+
- Lines beginning with `>` are ignored when rendering
|
|
54
|
+
|
|
55
|
+
```plain
|
|
56
|
+
> This is a comment in ***plain.
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
- Comments explain the specification to human authors — never use a comment to carry a requirement the renderer must implement
|
|
60
|
+
|
|
61
|
+
## Liquid templates
|
|
62
|
+
- `.plain` files are processed with [Liquid](https://shopify.github.io/liquid/) before parsing — the full Liquid syntax (variables, filters, conditionals, loops) is available in any section
|
|
63
|
+
- Expansion is **textual** and happens before the specification is interpreted — unlike `import`, which includes another module semantically (see `import-modules.md`)
|
|
64
|
+
|
|
65
|
+
```plain
|
|
66
|
+
{% assign max_tags = 3 %}
|
|
67
|
+
|
|
68
|
+
***functional specs***
|
|
69
|
+
|
|
70
|
+
- A :Widget: can be created with up to {{ max_tags }} tags.
|
|
71
|
+
{% if max_tags > 1 %}
|
|
72
|
+
- A :Widget: can have its tags listed.
|
|
73
|
+
{% endif %}
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
## Format
|
|
77
|
+
|
|
78
|
+
```plain
|
|
79
|
+
---
|
|
80
|
+
description: Command-line task manager.
|
|
81
|
+
import:
|
|
82
|
+
- airplain
|
|
83
|
+
---
|
|
84
|
+
|
|
85
|
+
***definitions***
|
|
86
|
+
|
|
87
|
+
- :Task: is an activity tracked by the application.
|
|
88
|
+
- Name (required)
|
|
89
|
+
|
|
90
|
+
- :TaskList: is the collection of all :Task: items. Initially empty.
|
|
91
|
+
|
|
92
|
+
***implementation reqs***
|
|
93
|
+
|
|
94
|
+
- :Implementation: keeps :TaskList: in memory.
|
|
95
|
+
|
|
96
|
+
***test reqs***
|
|
97
|
+
|
|
98
|
+
- :ConformanceTests: must not access the network.
|
|
99
|
+
|
|
100
|
+
***functional specs***
|
|
101
|
+
|
|
102
|
+
- A :Task: can be added to the :TaskList:.
|
|
103
|
+
|
|
104
|
+
- The :TaskList: is displayed.
|
|
105
|
+
```
|
|
@@ -9,14 +9,14 @@ When adding or editing `required_concepts` in a `.plain` file's frontmatter, alw
|
|
|
9
9
|
## What required_concepts does
|
|
10
10
|
- `required_concepts` declares concepts that any module importing this file **must** define
|
|
11
11
|
- It creates a contract: the import module references these concepts but does not define them — the importing module is responsible for providing definitions
|
|
12
|
-
- This is used exclusively on import modules
|
|
12
|
+
- This is used exclusively on import modules
|
|
13
13
|
|
|
14
14
|
## When to use it
|
|
15
|
-
- Use `required_concepts` when an import module
|
|
15
|
+
- Use `required_concepts` when an import module references concepts that vary per project or per importing module
|
|
16
16
|
- The import module can reference these concepts in its definitions, implementation reqs, or test reqs — but their actual definitions come from whoever imports the file
|
|
17
17
|
|
|
18
18
|
## Importing module must satisfy the contract
|
|
19
|
-
- Every concept listed in `required_concepts` must be defined
|
|
19
|
+
- Every concept listed in `required_concepts` must be defined somewhere visible to the importing module — in its own `***definitions***` section or in another module it imports
|
|
20
20
|
- If the importing module does not define a required concept, the spec is invalid
|
|
21
21
|
- Check `required_concepts` of all imported modules before finalizing a module
|
|
22
22
|
|
|
@@ -29,7 +29,7 @@ When adding or editing `required_concepts` in a `.plain` file's frontmatter, alw
|
|
|
29
29
|
```plain
|
|
30
30
|
---
|
|
31
31
|
required_concepts: [":AppName:", ":AppConfig:"]
|
|
32
|
-
description:
|
|
32
|
+
description: Imported module that requires AppName and AppConfig to be defined by the importer
|
|
33
33
|
---
|
|
34
34
|
|
|
35
35
|
***definitions***
|
|
@@ -39,6 +39,6 @@ description: Template that requires AppName and AppConfig to be defined by the i
|
|
|
39
39
|
- :MainFile: should load :AppConfig: on startup.
|
|
40
40
|
```
|
|
41
41
|
|
|
42
|
-
In this example, `:AppName:` and `:AppConfig:` are referenced but not defined — the module that imports this
|
|
42
|
+
In this example, `:AppName:` and `:AppConfig:` are referenced but not defined — the module that imports this import module must define them.
|
|
43
43
|
|
|
44
44
|
List concepts as a YAML array with each concept in `:ConceptName:` notation.
|
|
@@ -4,40 +4,56 @@ description: Rules for creating and using requires modules in .plain files
|
|
|
4
4
|
|
|
5
5
|
# Rules for requires modules
|
|
6
6
|
|
|
7
|
-
When creating or editing a `.plain` file that uses `requires`, always follow these rules:
|
|
7
|
+
When creating or editing a `.plain` file that uses `requires`, or one that is referenced in another `.plain` file's `requires` directive, always follow these rules:
|
|
8
8
|
|
|
9
9
|
## What requires does
|
|
10
|
-
|
|
11
|
-
-
|
|
12
|
-
- The required module's
|
|
13
|
-
-
|
|
10
|
+
|
|
11
|
+
- `requires` attaches the current module to the project's **build chain**: the required module is built first, and its generated code is copied as the starting point for the current module
|
|
12
|
+
- The copied code is the required module's **accumulated** output — it already contains the code of all of that module's own ancestors
|
|
13
|
+
- The `***functional specs***` of the required module **and of all its ancestors** become visible as **previous functional specs** — this visibility **is transitive** down the whole chain
|
|
14
|
+
- Of a required module's definitions, only the concepts listed in its `exported_concepts` are visible; the module's other definitions stay internal. Each exported concept arrives with its full definition text
|
|
15
|
+
- Export visibility **is not transitive**: an ancestor's exports are visible only if that ancestor is itself listed in `requires`
|
|
16
|
+
|
|
17
|
+
## Chain topology
|
|
18
|
+
|
|
19
|
+
- All modules connected by `requires` form a single **tree** with a common root: code accumulates along each branch, and branches never reconverge
|
|
20
|
+
- **Diamond joins are prohibited.** A module must not require two modules from divergent branches — there is no way to merge two divergent codebases into one starting point
|
|
21
|
+
- When `requires` lists multiple modules, every entry must lie on **one root-to-tip ancestor path**: each listed module must be an ancestor of the deepest one
|
|
22
|
+
- The **deepest entry is the attachment point** — it alone determines the build order and the starting code. The other entries add nothing to ordering; they exist **only** to make those ancestors' `exported_concepts` visible
|
|
23
|
+
- Branching outward is fine: two modules may each require the same parent, creating independent branches (and therefore independent top modules)
|
|
14
24
|
|
|
15
25
|
## Tech stack must match (hard rule)
|
|
26
|
+
|
|
16
27
|
- Because the required module's generated code is copied as the starting point and the renderer continues building on top of it with a single toolchain, two modules can only be linked with `requires` when they target the **same language, framework, and runtime**
|
|
17
28
|
- A runtime / network dependency between systems is **not** a reason to use `requires`
|
|
18
29
|
- Example of the mistake: a React frontend that talks to a Python/FastAPI backend over HTTP must **not** `requires: [backend]` — the stacks differ
|
|
19
|
-
- Model that pair as two independent root modules (each with its own `config.yaml` and test scripts) and express the contract through a shared API schema in `resources/` or shared concepts in an `import`ed
|
|
30
|
+
- Model that pair as two independent root modules (each with its own `config.yaml` and test scripts) and express the contract through a shared API schema in `resources/` or shared concepts in an `import`ed module — never through `requires`
|
|
20
31
|
|
|
21
|
-
##
|
|
22
|
-
|
|
23
|
-
- The
|
|
24
|
-
-
|
|
32
|
+
## Building upon but not necessarily extension
|
|
33
|
+
|
|
34
|
+
- The current module does not need to functionally extend the required module — it may address a completely different concern
|
|
35
|
+
- Even so, the two are never independent artifacts: the current module's output physically contains the required module's code, because both are stages of one accumulating codebase
|
|
36
|
+
- Use `requires` to place a module at the right point in that lineage, whether or not it builds on the required module's functionality
|
|
25
37
|
|
|
26
38
|
## Conflict prevention
|
|
27
|
-
- The current module's functional specs must not conflict with the required module's specs
|
|
28
|
-
- The required module's specs are treated as previous requirements — the renderer sees them as context
|
|
29
|
-
- Review the required module's functional specs before adding new ones
|
|
30
39
|
|
|
31
|
-
|
|
32
|
-
-
|
|
33
|
-
-
|
|
34
|
-
|
|
40
|
+
- The current module's functional specs must not conflict with the specs of **any module in its ancestor chain** — all of them are previous functional specs
|
|
41
|
+
- The renderer sees the whole chain's specs as prior requirements/context when rendering the current module
|
|
42
|
+
- Review the ancestor chain's functional specs before adding new ones
|
|
43
|
+
|
|
44
|
+
## Exports are the only visible definitions
|
|
45
|
+
|
|
46
|
+
- Only concepts listed in a required module's `exported_concepts` are available, and only for modules explicitly listed in `requires`
|
|
47
|
+
- Each exported concept is available with its full definition text
|
|
48
|
+
- Other concepts from required modules are internal and invisible
|
|
49
|
+
- To reference a concept exported by an ancestor deeper in the chain, list that ancestor in `requires` as well
|
|
50
|
+
- If you need shared definitions across branches or stacks, use `import` for that — not `requires`
|
|
35
51
|
|
|
36
52
|
## File locations
|
|
53
|
+
|
|
37
54
|
- Modules that use `requires` live at the **repository root** — they are functional modules with specs
|
|
38
55
|
- `requires` paths point to other root-level modules (e.g., `auth`, `messaging`)
|
|
39
|
-
-
|
|
40
|
-
- Never `require` a template — templates are for `import` only
|
|
56
|
+
- Never `require` an import module — import modules are for `import` only
|
|
41
57
|
|
|
42
58
|
## Format
|
|
43
59
|
|
|
@@ -47,11 +63,11 @@ requires:
|
|
|
47
63
|
- auth
|
|
48
64
|
import:
|
|
49
65
|
- airplain
|
|
50
|
-
description: Module built
|
|
66
|
+
description: Module built on top of auth, importing shared definitions
|
|
51
67
|
---
|
|
52
68
|
```
|
|
53
69
|
|
|
54
|
-
A module can
|
|
70
|
+
A module can list multiple required modules **only when they lie on one ancestor path**. Here `messaging` itself requires `auth`, so `auth` is an ancestor of `messaging`; the module attaches after `messaging` and lists `auth` only to see its exports:
|
|
55
71
|
|
|
56
72
|
```plain
|
|
57
73
|
---
|
package/forge/rules/test-reqs.md
CHANGED
|
@@ -28,20 +28,24 @@ When writing or editing a `***test reqs***` section in a `.plain` file, always f
|
|
|
28
28
|
|
|
29
29
|
## Test type reference
|
|
30
30
|
|
|
31
|
-
| Test type | Where to specify | Purpose |
|
|
32
|
-
|
|
33
|
-
| Unit tests | `***implementation reqs***` | Test individual functionalities in isolation |
|
|
34
|
-
| Conformance tests | `***test reqs***` | Verify implementation
|
|
35
|
-
| Acceptance tests | `***acceptance tests***` under a functional spec | Verify a
|
|
31
|
+
| Test type | Where to specify | Generated | Purpose |
|
|
32
|
+
|-----------|-----------------|-----------|---------|
|
|
33
|
+
| Unit tests | `***implementation reqs***` | Automatically | Test individual functionalities in isolation |
|
|
34
|
+
| Conformance tests | `***test reqs***` | Automatically | Verify each functionality's implementation against its functional spec |
|
|
35
|
+
| Acceptance tests | `***acceptance tests***` under a functional spec | Manually | Verify implementation of a functional spec through a specific end-to-end scenario |
|
|
36
36
|
|
|
37
37
|
## No duplication
|
|
38
38
|
- Do not duplicate guidance already present in the file or its imports
|
|
39
|
-
- Check imported
|
|
39
|
+
- Check imported modules before adding a new test req
|
|
40
40
|
|
|
41
41
|
## Concept references
|
|
42
42
|
- Reference predefined concepts like `:ConformanceTests:` where appropriate
|
|
43
43
|
- All other referenced `:Concepts:` must be defined in `***definitions***`
|
|
44
44
|
|
|
45
|
+
## Conciseness
|
|
46
|
+
- State each req in the fewest plain words that stay exact — see `concise-specs.md`
|
|
47
|
+
- Do not restate the default behavior of the test framework this section already names
|
|
48
|
+
|
|
45
49
|
## Format
|
|
46
50
|
|
|
47
51
|
```plain
|
|
@@ -50,6 +54,6 @@ When writing or editing a `***test reqs***` section in a `.plain` file, always f
|
|
|
50
54
|
- :ConformanceTests: will be run using "pytest" command.
|
|
51
55
|
- :ConformanceTests: must be implemented and executed - do not skip tests.
|
|
52
56
|
- :ConformanceTests: should mock all external HTTP calls.
|
|
53
|
-
- :ConformanceTests: are
|
|
57
|
+
- :ConformanceTests: are prepared via the prepare script [test_scripts/prepare_environment_python.sh](test_scripts/prepare_environment_python.sh).
|
|
54
58
|
- :ConformanceTests: are executed via the run script [test_scripts/run_conformance_tests_python.sh](test_scripts/run_conformance_tests_python.sh).
|
|
55
59
|
```
|
|
@@ -95,6 +95,7 @@ Acceptance tests extend conformance tests and are implemented according to the `
|
|
|
95
95
|
- [ ] Nested under the correct functional spec
|
|
96
96
|
- [ ] Indentation is correct (`***acceptance tests***` indented under the spec)
|
|
97
97
|
- [ ] Each test describes a concrete, verifiable outcome
|
|
98
|
+
- [ ] Trimmed per `concise-specs.md`: plain words, no obvious steps, no restatement of the parent spec
|
|
98
99
|
- [ ] Tests are scoped to the parent functional spec only
|
|
99
100
|
- [ ] Written in language-agnostic terms
|
|
100
101
|
- [ ] No duplication with existing acceptance tests on the same spec
|
|
@@ -36,7 +36,7 @@ Always use the skill `load-plain-reference` to retrieve the ***plain syntax rule
|
|
|
36
36
|
- CamelCase, starting with an uppercase letter
|
|
37
37
|
- Valid characters: letters, digits, `+`, `-`, `.`, `_`
|
|
38
38
|
- Must be globally unique across the spec and all its imports
|
|
39
|
-
- Exported concepts from `requires` modules are **not transitive** —
|
|
39
|
+
- Exported concepts from `requires` modules are **not transitive** — to reference an ancestor's export, list that ancestor in `requires`; if a concept is shared across branches, define it in a common import module instead
|
|
40
40
|
|
|
41
41
|
## Definition Format
|
|
42
42
|
|
|
@@ -68,4 +68,5 @@ Attributes and constraints are nested sub-bullets:
|
|
|
68
68
|
- [ ] All referenced concepts (`:OtherConcept:`) are already defined above
|
|
69
69
|
- [ ] No circular references between concepts
|
|
70
70
|
- [ ] Description is clear, concise, and language-agnostic
|
|
71
|
+
- [ ] Trimmed per `concise-specs.md`: one plain sentence, no restatement of the name, no rationale
|
|
71
72
|
- [ ] Placed inside a `***definitions***` section
|
|
@@ -66,7 +66,7 @@ Ask one writable question per `AskUserQuestion` call, targeting exactly one of:
|
|
|
66
66
|
- **A concept** — a new concept, or one single attribute of an existing one.
|
|
67
67
|
- **A single edge case** — one invalid input, empty state, or boundary value.
|
|
68
68
|
- **A single constraint** — one business rule, permission, ordering, or size limit.
|
|
69
|
-
- **Implementation guidance** —
|
|
69
|
+
- **Implementation guidance** — a policy that shapes how the functionality is built (retry, error handling, data format), or technology / a library / a pattern not already in the file or its imports.
|
|
70
70
|
- **Verification** — only when the *Conformance gate* below is satisfied: one concrete outcome that proves this functionality works.
|
|
71
71
|
|
|
72
72
|
Never bundle a second question into the prompt; never ask a question whose answer doesn't translate into a writable snippet on its own.
|
|
@@ -77,7 +77,7 @@ Route each answer to the right edit skill — never hand-author a `***plain` sec
|
|
|
77
77
|
|
|
78
78
|
- **New concept** → `add-concept` (into `***definitions***`; define before any reference).
|
|
79
79
|
- **New functional spec** → `add-functional-spec`. It runs `analyze-if-func-spec-too-complex` and `analyze-func-specs` for you — let it. **Never hand-author functional specs.** If it reports the spec is too complex, ask the user a follow-up question to split it (the next loop iteration) — do not break it down alone.
|
|
80
|
-
- **New implementation req** → `add-implementation-requirement`.
|
|
80
|
+
- **New implementation req** → `add-implementation-requirement`. When the answer states HOW the functionality is built — a retry or error-handling policy, a data format, a library, an architectural pattern — and it is not already in the file or its imports.
|
|
81
81
|
- **New acceptance test** → `add-acceptance-test`, under the relevant functional spec. Only when the *Conformance gate* is satisfied and the answer describes a concrete verification.
|
|
82
82
|
- **New test req** → `add-test-requirement`. Only when conformance testing is configured and the answer changes how conformance tests are run.
|
|
83
83
|
|
|
@@ -19,7 +19,7 @@ Always use the skill `load-plain-reference` to retrieve the ***plain syntax rule
|
|
|
19
19
|
|
|
20
20
|
1. **Identify the target `.plain` file.** If ambiguous, ask the user.
|
|
21
21
|
2. **Read the entire file** to understand existing definitions, implementation reqs, and all current functional specs (including those in `requires` modules).
|
|
22
|
-
3. **Draft the functional spec** following the rules below.
|
|
22
|
+
3. **Draft the functional spec** following the rules below, then run the review pass in `concise-specs.md` over the draft — trim it to the shortest wording that keeps every fact before any other check.
|
|
23
23
|
4. **Analyze complexity** — use the `analyze-if-func-spec-too-complex` skill to verify the drafted spec implies ≤ 200 LOC. If too complex, use the `break-down-func-spec` skill to split it, then repeat from step 3 for each resulting spec.
|
|
24
24
|
5. **Check for conflicts** with every existing functional spec — this is critical. Run `analyze-func-specs` **once** with the new spec plus all existing specs (in the file and in any `requires` chain) as a single batch. The batched analyzer returns every conflicting pair in one call — do **not** invoke a pair-by-pair analyzer. For each conflicting pair it reports, run `resolve-spec-conflict` on that pair; re-run `analyze-func-specs` over the touched set after each resolution until the verdict is `COMPATIBLE`.
|
|
25
25
|
6. **Append the spec** to the end of the `***functional specs***` section (specs are chronological; new ones go last).
|
|
@@ -80,5 +80,6 @@ If the functional spec needs verification, use the `add-acceptance-test` skill t
|
|
|
80
80
|
- [ ] All external interfaces are explicit (endpoint paths, methods, CLI args, formats, etc.)
|
|
81
81
|
- [ ] All referenced `:Concepts:` are defined in `***definitions***`
|
|
82
82
|
- [ ] Sentences are short, clear, and unambiguous
|
|
83
|
+
- [ ] Trimmed per `concise-specs.md`: nothing obvious, no rationale, no uncheckable adjectives or hedges, plain words throughout
|
|
83
84
|
- [ ] No redundancy with existing specs
|
|
84
85
|
- [ ] Placed in correct chronological position (usually at the end)
|
|
@@ -28,7 +28,7 @@ This skill is `add-functional-spec` applied **one spec at a time, in order**, wi
|
|
|
28
28
|
|
|
29
29
|
1. **Identify the target `.plain` file.** If ambiguous, ask the user.
|
|
30
30
|
2. **Read the entire file** to understand existing definitions, implementation reqs, and all current functional specs (including those reachable via `import` / `requires`).
|
|
31
|
-
3. **Draft all the specs you intend to add**, in the chronological order they should appear. Keep the drafts in a working list — nothing is inserted yet.
|
|
31
|
+
3. **Draft all the specs you intend to add**, in the chronological order they should appear. Keep the drafts in a working list — nothing is inserted yet. Run the review pass in `concise-specs.md` over every draft before continuing; trim each to the shortest wording that keeps every fact.
|
|
32
32
|
4. **For each drafted spec, in order:**
|
|
33
33
|
1. **Analyze complexity.** Run `analyze-if-func-spec-too-complex` on the spec. If the verdict is `TOO COMPLEX`, run `break-down-func-spec`, replace the entry in the working list with the resulting smaller specs (preserving order), and restart this step on the first of the replacements.
|
|
34
34
|
2. **Check for conflicts.** Run `analyze-func-specs` **once** with this single input set:
|
|
@@ -114,5 +114,6 @@ If any of the newly added functional specs need verification, use the `add-accep
|
|
|
114
114
|
- [ ] All external interfaces explicit (endpoint paths, methods, CLI args, formats, etc.)
|
|
115
115
|
- [ ] All referenced `:Concepts:` are defined in `***definitions***`
|
|
116
116
|
- [ ] Sentences are short, clear, and unambiguous
|
|
117
|
+
- [ ] Every spec trimmed per `concise-specs.md`: nothing obvious, no rationale, no uncheckable adjectives or hedges, plain words throughout
|
|
117
118
|
- [ ] No redundancy with existing specs or within the batch
|
|
118
119
|
- [ ] Specs placed in correct chronological positions
|
|
@@ -66,7 +66,7 @@ Implementation reqs are bullet points in the `***implementation reqs***` section
|
|
|
66
66
|
|
|
67
67
|
- :Implementation: should use pip for dependency management.
|
|
68
68
|
|
|
69
|
-
-
|
|
69
|
+
- :ReportExporter: writes a :CsvFile: with streaming writes, never holding the full dataset in memory.
|
|
70
70
|
|
|
71
71
|
```
|
|
72
72
|
|
|
@@ -82,5 +82,6 @@ Reference defined `:Concepts:` where they add clarity. Implementation reqs in no
|
|
|
82
82
|
- [ ] Describes HOW to build, not WHAT to build
|
|
83
83
|
- [ ] All referenced `:Concepts:` are defined in `***definitions***`
|
|
84
84
|
- [ ] Does not duplicate guidance already present in the file or its imports
|
|
85
|
+
- [ ] Trimmed per `concise-specs.md`: plain words, no rationale, no standard behavior of a technology already named
|
|
85
86
|
- [ ] Placed inside a `***implementation reqs***` section
|
|
86
87
|
- [ ] No behavioral requirements that should be in `***functional specs***`
|
|
@@ -69,5 +69,6 @@ Reference predefined concepts like `:ConformanceTests:` and any defined `:Concep
|
|
|
69
69
|
- [ ] Describes conformance testing concerns, not unit tests or behavior
|
|
70
70
|
- [ ] All referenced `:Concepts:` are defined (or are predefined like `:ConformanceTests:`)
|
|
71
71
|
- [ ] Does not duplicate guidance already present in the file or its imports
|
|
72
|
+
- [ ] Trimmed per `concise-specs.md`: plain words, no rationale, no default framework behavior
|
|
72
73
|
- [ ] Placed inside a `***test reqs***` section
|
|
73
74
|
- [ ] No behavioral requirements that should be in `***functional specs***`
|
|
@@ -201,5 +201,6 @@ If the original spec had acceptance tests, redistribute them to the most appropr
|
|
|
201
201
|
- [ ] All `:Concepts:` referenced in replacement specs are defined
|
|
202
202
|
- [ ] Any extracted technical component is defined as a concept, built by its own earlier spec, and only **referenced** (not re-described) by the original
|
|
203
203
|
- [ ] Replacement specs are language-agnostic
|
|
204
|
+
- [ ] Replacement specs trimmed per `concise-specs.md` — a split is not a licence to re-add prose
|
|
204
205
|
- [ ] All external interfaces remain explicit
|
|
205
206
|
- [ ] Acceptance tests (if any) have been redistributed or rewritten
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: check-forge-version
|
|
3
|
+
description: >-
|
|
4
|
+
Reports whether the plain-forge installation on this machine is current. Enumerates every agent
|
|
5
|
+
directory plain-forge can install into — project scope and global scope, across all supported
|
|
6
|
+
agent layouts — reads the installed version from each `.plain-forge/manifest.json`, resolves the
|
|
7
|
+
latest published version from the npm registry, and emits a PASS / WARN / FAIL verdict naming
|
|
8
|
+
every install that is current, stale, or unmanaged. For a stale install it surfaces the exact
|
|
9
|
+
`npx plain-forge update` command and asks the user to run it. This concerns the plain-forge
|
|
10
|
+
tooling itself, so it is not tied to any ***plain project or authoring workflow. Use when someone
|
|
11
|
+
asks whether plain-forge is up to date, on the first use of a forge skill in a session, after a
|
|
12
|
+
forge skill or rule behaves unexpectedly, or when a rule referenced in conversation appears to be
|
|
13
|
+
missing. Do not use to perform the update, which stays the user's decision. Do not use to check a
|
|
14
|
+
project's host tooling, which is what `check-plain-env` covers.
|
|
15
|
+
---
|
|
16
|
+
|
|
17
|
+
# Check Forge Version
|
|
18
|
+
|
|
19
|
+
This skill answers one question: **is the plain-forge installed on this machine the latest published
|
|
20
|
+
version?** It reads the installed version from every install's manifest, compares each against the
|
|
21
|
+
`latest` dist-tag on npm, and — when an install is behind — asks the user to run
|
|
22
|
+
`npx plain-forge update`.
|
|
23
|
+
|
|
24
|
+
The subject is **plain-forge itself**, not any project it is used on. This skill reads no `.plain`
|
|
25
|
+
files, needs no ***plain project in the working directory, and is equally valid in an empty
|
|
26
|
+
directory. Do not invoke `load-plain-reference` for it; no authoring rules apply.
|
|
27
|
+
|
|
28
|
+
## When to run
|
|
29
|
+
|
|
30
|
+
- **On the first use of any forge skill in a session** — the cheapest moment to discover that the
|
|
31
|
+
skills and rules about to be followed are from a superseded release.
|
|
32
|
+
- **On demand** — whenever the user asks whether plain-forge is up to date, which version is
|
|
33
|
+
installed, or where it is installed.
|
|
34
|
+
- **After a forge skill or rule behaves unexpectedly** — a skill that contradicts the documented
|
|
35
|
+
behavior, or a rule file that is missing entirely, is most often a stale install rather than a bug.
|
|
36
|
+
- **When a skill or rule named in conversation does not exist on disk** — a newer release probably
|
|
37
|
+
added it.
|
|
38
|
+
- **After a plain-forge release is announced**, to confirm the update actually landed in every scope.
|
|
39
|
+
|
|
40
|
+
## What this skill does NOT do
|
|
41
|
+
|
|
42
|
+
- It does **not** run `npx plain-forge update`, or install, or uninstall anything. It reports and
|
|
43
|
+
asks; acting is the user's decision.
|
|
44
|
+
- It does **not** modify any file — not a manifest, not a skill, not a rule. It is read-only apart
|
|
45
|
+
from the network read against the npm registry.
|
|
46
|
+
- It does **not** check project host tooling, runtimes, services, or credentials. That is
|
|
47
|
+
`check-plain-env`.
|
|
48
|
+
- It does **not** validate `.plain` specs. That is `plain-healthcheck`.
|
|
49
|
+
- It does **not** resolve which agent the user *should* install into. It reports what is present.
|
|
50
|
+
|
|
51
|
+
## Workflow
|
|
52
|
+
|
|
53
|
+
### Step 1 — Run the check
|
|
54
|
+
|
|
55
|
+
Run the bundled script with the `terminal` tool, passing its path relative to this `SKILL.md`:
|
|
56
|
+
|
|
57
|
+
```
|
|
58
|
+
node scripts/check-forge-version.mjs
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
**Run it from the project root** whose project-scope install should be checked, giving the full path
|
|
62
|
+
to the script — for a Claude layout, `node .claude/skills/check-forge-version/scripts/check-forge-version.mjs`.
|
|
63
|
+
The script always reports the install it lives in and the global installs regardless of where it is
|
|
64
|
+
invoked, but it can only find a *project-scope* install in the current working directory. When the
|
|
65
|
+
report is missing an install that is known to exist, re-run it from that project's root before
|
|
66
|
+
concluding anything.
|
|
67
|
+
|
|
68
|
+
Add `--json` for a machine-readable report, or `--offline` to list the installs without querying npm.
|
|
69
|
+
|
|
70
|
+
The script is the single source of truth for this check. It mirrors `bin/cli.mjs` for both the
|
|
71
|
+
install-path model (including the two global paths that break the usual pattern — ForgeCode's
|
|
72
|
+
`~/forge` and OpenCode's `~/.config/opencode`) and the version comparison. Do not re-derive either by
|
|
73
|
+
hand, and do not substitute an ad-hoc `npm view` plus a guess at where the install lives.
|
|
74
|
+
|
|
75
|
+
### Step 2 — Read the verdict
|
|
76
|
+
|
|
77
|
+
The verdict is the **first line of stdout**, and it is also the exit code:
|
|
78
|
+
|
|
79
|
+
| Exit | Verdict | Meaning |
|
|
80
|
+
|---|---|---|
|
|
81
|
+
| 0 | `PASS` | Every install found is at the latest version. |
|
|
82
|
+
| 1 | `FAIL` | At least one install is behind the latest version. |
|
|
83
|
+
| 2 | `WARN` | Installs found, but at least one version is indeterminate (no manifest, or a prerelease). |
|
|
84
|
+
| 3 | `ERROR` | The latest version could not be resolved from npm. |
|
|
85
|
+
| 4 | `NONE` | No install found in any candidate directory. |
|
|
86
|
+
|
|
87
|
+
Per-install statuses are `CURRENT`, `STALE`, `UNMANAGED` (a pre-manifest install — `update` adopts
|
|
88
|
+
it and writes a manifest going forward), or `INDETERMINATE`.
|
|
89
|
+
|
|
90
|
+
Each install is labelled with its scope: `self` is the install this skill is running from, `project`
|
|
91
|
+
is one found in the current working directory, and `global` is one under the home directory.
|
|
92
|
+
|
|
93
|
+
### Step 3 — Report and prompt
|
|
94
|
+
|
|
95
|
+
Relay the verdict to the user, then act on it:
|
|
96
|
+
|
|
97
|
+
- **`PASS`** — state the installed version and that it is current. Stop. Do not suggest an update.
|
|
98
|
+
- **`FAIL`** or **`WARN`** — do not report this as a passing aside. Lead with the recommendation,
|
|
99
|
+
name each affected install by agent, scope, and path, give the installed version and the latest
|
|
100
|
+
version, and **ask the user directly to update before continuing**:
|
|
101
|
+
|
|
102
|
+
```
|
|
103
|
+
npx plain-forge update
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
State plainly that **continuing on an outdated plain-forge is not recommended** — its skills and
|
|
107
|
+
rules have been superseded, specs authored against them may not match what the current `codeplain`
|
|
108
|
+
renderer expects, and fixes released since the installed version are not in effect. On `WARN`,
|
|
109
|
+
where the version could not be confirmed, say the install should be treated as outdated until
|
|
110
|
+
proven otherwise.
|
|
111
|
+
|
|
112
|
+
Add that a single `update` run covers every install across both scopes, prunes files that no longer
|
|
113
|
+
ship, and never touches the user's own or third-party skills — so the fix is one command and
|
|
114
|
+
carries no risk to their content.
|
|
115
|
+
|
|
116
|
+
Then **stop and wait for the user** — do not run the update. If the user chooses to continue
|
|
117
|
+
without updating, proceed with the task, but say once that the stale rules are a likely source of
|
|
118
|
+
any surprising behaviour that follows.
|
|
119
|
+
- **`NONE`** — report that plain-forge is not installed here and surface `npx plain-forge install`,
|
|
120
|
+
noting that the script's output lists every directory that was checked.
|
|
121
|
+
- **`ERROR`** — report that the installed versions could not be compared against anything, give the
|
|
122
|
+
reason from the script, and offer the two options: re-run when the network is reachable, or check
|
|
123
|
+
https://www.npmjs.com/package/plain-forge directly. Never guess whether the install is current.
|
|
124
|
+
|
|
125
|
+
When the user agrees to update, they run the command themselves. After it completes, re-run Step 1 to
|
|
126
|
+
confirm the result rather than assuming it worked.
|
|
127
|
+
|
|
128
|
+
## Error Handling
|
|
129
|
+
|
|
130
|
+
- **Registry unreachable** — the script already falls back from the registry endpoint to
|
|
131
|
+
`npm view plain-forge version`, which succeeds on hosts pointed at an internal mirror. If both
|
|
132
|
+
fail it exits 3; treat the version as unknown and surface the reason instead of assuming staleness.
|
|
133
|
+
- **`node` unavailable** — plain-forge requires Node ≥ 18, so this is itself the finding. Report that
|
|
134
|
+
Node is missing and that no plain-forge command, including `update`, can run without it.
|
|
135
|
+
- **Malformed or hand-edited manifest** — the script treats an unparseable manifest as absent and
|
|
136
|
+
falls back to detecting the install by its skill footprint, reporting it as `UNMANAGED`. Recommend
|
|
137
|
+
`npx plain-forge update`, which rewrites a correct manifest.
|
|
138
|
+
- **An install ahead of the registry** — a local development build reports `CURRENT`, not stale. Do
|
|
139
|
+
not prompt for an update in that case.
|
|
140
|
+
- **Prerelease installed** — a version such as `1.0.21-rc.1` is not dotted-numeric and reports
|
|
141
|
+
`INDETERMINATE`. Prereleases ship under npm's `next` tag, so a comparison against `latest` is not
|
|
142
|
+
meaningful; say so rather than prompting for an update that would move the user off the prerelease.
|
|
143
|
+
|
|
144
|
+
## Validation Checklist
|
|
145
|
+
|
|
146
|
+
- [ ] `scripts/check-forge-version.mjs` was actually executed; no verdict was inferred from memory or
|
|
147
|
+
from a bare `npm view`.
|
|
148
|
+
- [ ] The verdict reported to the user matches the script's first line and exit code.
|
|
149
|
+
- [ ] Every install the script listed was relayed with its agent, scope, and path.
|
|
150
|
+
- [ ] `npx plain-forge update` was **offered**, never executed by this skill.
|
|
151
|
+
- [ ] Nothing on disk was modified.
|
|
152
|
+
- [ ] No `.plain` file was read and `load-plain-reference` was not invoked — this skill is about the
|
|
153
|
+
tooling, not about any project.
|
|
154
|
+
- [ ] On `ERROR` or `NONE`, no claim was made about whether the install is current.
|