plain-forge 1.0.18 → 1.0.19
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 +15 -8
- package/bin/cli.mjs +53 -24
- package/forge/rules/bullet-continuation.md +59 -0
- package/forge/rules/definitions.md +15 -0
- package/forge/rules/func-specs.md +52 -7
- package/forge/rules/integration-embedded-testing.md +4 -1
- package/forge/rules/linked-resources.md +7 -6
- package/forge/skills/add-acceptance-test/SKILL.md +3 -25
- package/forge/skills/add-concept/SKILL.md +3 -22
- package/forge/skills/add-feature/SKILL.md +71 -67
- package/forge/skills/add-feature/references/checklist.md +38 -0
- package/forge/skills/add-feature/references/phase-0-intent.md +29 -0
- package/forge/skills/add-functional-spec/SKILL.md +3 -23
- package/forge/skills/add-functional-specs/SKILL.md +3 -24
- package/forge/skills/add-implementation-requirement/SKILL.md +4 -23
- package/forge/skills/add-resource/SKILL.md +7 -8
- package/forge/skills/add-test-requirement/SKILL.md +4 -21
- package/forge/skills/analyze-2-func-specs/SKILL.md +10 -10
- package/forge/skills/analyze-func-specs/SKILL.md +10 -10
- package/forge/skills/analyze-if-func-spec-too-complex/SKILL.md +93 -31
- package/forge/skills/break-down-func-spec/SKILL.md +78 -29
- package/forge/skills/create-import-module/SKILL.md +7 -0
- package/forge/skills/create-requires-module/SKILL.md +2 -1
- package/forge/skills/forge-plain/SKILL.md +80 -299
- package/forge/skills/forge-plain/references/checklist.md +66 -0
- package/forge/skills/forge-plain/references/phase-0-intent.md +29 -0
- package/forge/skills/forge-plain/references/phase-1-product.md +40 -0
- package/forge/skills/forge-plain/references/phase-2-tech.md +24 -0
- package/forge/skills/forge-plain/references/phase-3-testing.md +66 -0
- package/forge/skills/forge-plain/references/phase-4-validate-handoff.md +49 -0
- package/forge/skills/init-config-file/SKILL.md +2 -0
- package/forge/skills/init-plain-project/SKILL.md +4 -0
- package/forge/skills/init-plain-project/references/checklist.md +31 -0
- package/forge/skills/load-codeplain-reference/SKILL.md +188 -0
- package/forge/skills/load-plain-reference/SKILL.md +48 -678
- package/forge/skills/load-plain-reference/references/project-model.md +47 -0
- package/forge/skills/load-plain-reference/references/rendering-and-testing.md +57 -0
- package/forge/skills/plain-healthcheck/SKILL.md +2 -2
- package/forge/skills/resolve-spec-conflict/SKILL.md +13 -5
- package/forge/skills/run-codeplain/SKILL.md +1 -1
- package/package.json +1 -1
- package/forge/rules/line-length.md +0 -45
|
@@ -1,136 +1,140 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: add-feature
|
|
3
3
|
description: >-
|
|
4
|
-
End-to-end feature addition
|
|
5
|
-
|
|
6
|
-
specs,
|
|
7
|
-
|
|
4
|
+
End-to-end feature addition on an existing ***plain project: runs a short feature-intent
|
|
5
|
+
interview, then takes the confirmed request and, one question at a time, incrementally writes
|
|
6
|
+
***plain specs (concepts, implementation reqs, functional specs, test reqs,
|
|
7
|
+
acceptance tests) to disk — asking, authoring, and reviewing per
|
|
8
|
+
functionality — then closes with a plain-healthcheck gate. Use when the user
|
|
9
|
+
wants to add a feature to an existing project. Not for bootstrapping a new
|
|
10
|
+
project from scratch (use forge-plain) or for editing generated code (the
|
|
11
|
+
.plain specs are the source of truth).
|
|
8
12
|
---
|
|
9
13
|
|
|
10
14
|
# Add Feature
|
|
11
15
|
|
|
12
16
|
Always use the skill `load-plain-reference` to retrieve the ***plain syntax rules — but only if you haven't done so yet.
|
|
13
17
|
|
|
14
|
-
|
|
18
|
+
`add-feature` is the continuous-loop counterpart of `forge-plain`. Where `forge-plain` bootstraps an entire project from scratch, `add-feature` adds a single feature to an **existing** set of `.plain` specs. It begins with a short intent phase, then runs the authoring loop scoped to one feature.
|
|
15
19
|
|
|
16
|
-
## Core
|
|
20
|
+
## Core loop: one question → one answer → write to disk
|
|
17
21
|
|
|
18
|
-
This
|
|
22
|
+
This loop starts after Phase 0. Each iteration is a single question followed by an immediate write:
|
|
19
23
|
|
|
20
|
-
1. Ask
|
|
21
|
-
2.
|
|
22
|
-
3. **
|
|
23
|
-
4. Ask the next question, which often refines, extends, or corrects what you just wrote.
|
|
24
|
+
1. **Ask** one focused question via `AskUserQuestion` — never bundle two. Shape it so any plausible answer maps directly to one writable snippet: a single behavior, concept, attribute, edge case, or constraint — not an open-ended design question. Bad shape: "How should the feature behave?" Good shape: "When the user submits an empty title, should the request be rejected with HTTP 400, accepted with a default title, or something else?" Offer concrete options plus a free-form catch-all whenever the answer space is predictable.
|
|
25
|
+
2. **Author immediately** — the moment the user answers, write the snippet to disk (see *2b* for which skill to route to). Do not wait for "enough" context; eager writes are the point. A snippet that is wrong on the first try is expected — the next question corrects it, and the user can read exactly where things stand after every step.
|
|
26
|
+
3. **Refine on the next question**, which often extends or corrects what was just written.
|
|
24
27
|
|
|
25
|
-
|
|
28
|
+
**One question per call, but drill as deep as the topic needs.** "One question" governs the `AskUserQuestion` call, not the topic. If an answer is vague or leaves real choices open, the *next* question drills into the same topic — another iteration of the loop — until it is concrete enough to write. Stopping early and writing on top of a vague answer is worse than one more focused follow-up.
|
|
26
29
|
|
|
27
|
-
**
|
|
30
|
+
**Fix contradictions in place.** If a later answer refines or contradicts a snippet already on disk, edit that snippet right now. Never leave stale intent on disk; surface a non-trivial change in the next question.
|
|
28
31
|
|
|
29
32
|
## Input
|
|
30
33
|
|
|
31
|
-
A feature request from the user — anything from a one-liner ("add dark mode") to a detailed
|
|
34
|
+
A feature request from the user — anything from a one-liner ("add dark mode") to a detailed
|
|
35
|
+
description. Phase 0 turns it into a confirmed feature-intent brief; Phase 2 sharpens the behavior
|
|
36
|
+
as it is authored.
|
|
37
|
+
|
|
38
|
+
## Phase 0 — Feature intent
|
|
39
|
+
|
|
40
|
+
Read `references/phase-0-intent.md`. Ask its five core questions one at a time, write nothing to
|
|
41
|
+
disk, summarize the answers as a feature-intent brief, and get explicit confirmation. Ask no more
|
|
42
|
+
than one focused clarification when an answer is too vague to summarize.
|
|
43
|
+
|
|
44
|
+
Carry the confirmed brief into every later phase. Before advancing, check the Phase 0 block in
|
|
45
|
+
`references/checklist.md` inline.
|
|
32
46
|
|
|
33
47
|
## Phase 1 — Scope
|
|
34
48
|
|
|
35
|
-
Keep this
|
|
49
|
+
Keep this short. The goal is to know enough to ask the **very first** writable question — not to design the whole feature on paper.
|
|
36
50
|
|
|
37
|
-
1. **Read the
|
|
38
|
-
2. **Read the target `.plain` file(s)
|
|
39
|
-
3. **Pick the target module
|
|
51
|
+
1. **Read the confirmed feature-intent brief.** Identify which existing `.plain` file(s) the feature most likely belongs to.
|
|
52
|
+
2. **Read the target `.plain` file(s)**, following their `import` and `requires` chains, so the existing definitions, implementation reqs, functional specs, test reqs, and acceptance tests are in context. This is what lets Phase 2 recognize impact when it surfaces.
|
|
53
|
+
3. **Pick the target module with one question — only if it is genuinely ambiguous** which file to modify. Otherwise skip it and start authoring immediately.
|
|
40
54
|
|
|
41
|
-
End Phase 1 the moment
|
|
55
|
+
End Phase 1 the moment the target file and a single concrete starter question are clear. Do **not** ask framing, scope, or multi-part design questions here.
|
|
42
56
|
|
|
43
57
|
## Phase 2 — One-question loop
|
|
44
58
|
|
|
45
|
-
|
|
59
|
+
A single repeating cycle: exactly one question, then an immediate write. The loop ends when the user says the feature is fully covered.
|
|
46
60
|
|
|
47
61
|
### 2a. Ask one question
|
|
48
62
|
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
Each question targets exactly one of:
|
|
63
|
+
Ask one writable question per `AskUserQuestion` call, targeting exactly one of:
|
|
52
64
|
|
|
53
65
|
- **Behavior** — a single trigger and its outcome.
|
|
54
|
-
- **A concept** —
|
|
55
|
-
- **A single edge case** — one invalid input, empty state, boundary value.
|
|
66
|
+
- **A concept** — a new concept, or one single attribute of an existing one.
|
|
67
|
+
- **A single edge case** — one invalid input, empty state, or boundary value.
|
|
56
68
|
- **A single constraint** — one business rule, permission, ordering, or size limit.
|
|
57
|
-
- **Implementation guidance** — only when the functionality
|
|
69
|
+
- **Implementation guidance** — only when the functionality needs technology / a library / a pattern not already in the file or its imports.
|
|
58
70
|
- **Verification** — only when the *Conformance gate* below is satisfied: one concrete outcome that proves this functionality works.
|
|
59
71
|
|
|
60
|
-
|
|
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.
|
|
61
73
|
|
|
62
74
|
### 2b. Write immediately
|
|
63
75
|
|
|
64
|
-
|
|
76
|
+
Route each answer to the right edit skill — never hand-author a `***plain` section:
|
|
65
77
|
|
|
66
|
-
- **New concept** →
|
|
67
|
-
- **New functional spec** →
|
|
68
|
-
- **New implementation req** →
|
|
69
|
-
- **New acceptance test** →
|
|
70
|
-
- **New test req** →
|
|
78
|
+
- **New concept** → `add-concept` (into `***definitions***`; define before any reference).
|
|
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`. Only when the answer introduces technology / a library / a data format / an architectural pattern not already present.
|
|
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
|
+
- **New test req** → `add-test-requirement`. Only when conformance testing is configured and the answer changes how conformance tests are run.
|
|
71
83
|
|
|
72
|
-
If the
|
|
84
|
+
If the answer contradicts or refines an earlier snippet, fix that snippet in place now (per *Fix contradictions in place* above).
|
|
73
85
|
|
|
74
86
|
### 2c. Handle conflicts just-in-time
|
|
75
87
|
|
|
76
|
-
If `add-functional-spec` (via
|
|
88
|
+
If `add-functional-spec` (via its analyzers) reports a conflict, or the snippet just written would **break** (contradict, invalidate) or **augment** (change the meaning of, add behavior to) an existing concept / functional spec / implementation req / test req / acceptance test, the **next question** must be about that conflict. Show the exact existing snippet in the question and offer:
|
|
77
89
|
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
- **(a) keep** the existing spec — back out or narrow what you just wrote,
|
|
90
|
+
- **(a) keep** the existing spec — back out or narrow what was just written,
|
|
81
91
|
- **(b) augment** the existing spec — embed the proposed new wording in the question,
|
|
82
92
|
- **(c) replace** the existing spec.
|
|
83
93
|
|
|
84
|
-
Apply the
|
|
94
|
+
Apply the choice the instant the user answers. If they augmented a concept, walk every spec that references it and update each in place, limited to the approved scope. Never silently rewrite prior intent.
|
|
85
95
|
|
|
86
96
|
### 2d. Decide what's next
|
|
87
97
|
|
|
88
|
-
Ask the user whether the feature is fully covered.
|
|
98
|
+
Ask the user whether the feature is fully covered. Yes → Phase 3. No → return to 2a with the next single question.
|
|
89
99
|
|
|
90
100
|
### Conformance gate
|
|
91
101
|
|
|
92
|
-
|
|
102
|
+
Author `***test reqs***` and `***acceptance tests***` **only** when the project has a `config.yaml` with a valid `conformance-tests-script` entry pointing at an existing script in `test_scripts/`. Check the `config.yaml` that covers this module (multi-part projects may have several) and confirm the referenced script exists. If conformance testing is not configured, skip those two authoring paths entirely — concepts, functional specs, and implementation reqs are still authored normally.
|
|
93
103
|
|
|
94
104
|
## Phase 3 — Final review
|
|
95
105
|
|
|
96
|
-
Most checks
|
|
106
|
+
Most checks already happened in the loop; this is a slim consistency pass whose final automated step is always `plain-healthcheck`.
|
|
97
107
|
|
|
98
108
|
1. Read the modified `.plain` file(s) in full.
|
|
99
109
|
2. Verify:
|
|
100
|
-
-
|
|
101
|
-
- Chronological ordering is correct end-to-end
|
|
110
|
+
- Every new concept is defined before use, with no circular references.
|
|
111
|
+
- Chronological ordering is correct end-to-end — no spec depends on something that comes after it.
|
|
102
112
|
- Functional specs are language-agnostic.
|
|
103
|
-
-
|
|
113
|
+
- Every external interface is explicit (endpoint paths, methods, CLI args, formats).
|
|
104
114
|
- Acceptance tests (if any) are consistent with their parent specs.
|
|
105
|
-
3. Present the final diff for the modified file(s)
|
|
106
|
-
4. If the user requests changes, drop **straight back into the one-question loop**
|
|
107
|
-
5. **
|
|
115
|
+
3. Present the final diff for the modified file(s) and get the user's approval.
|
|
116
|
+
4. If the user requests changes, drop **straight back into the one-question loop** — one question, one write, one fix at a time. Do not restart the loop from scratch.
|
|
117
|
+
5. **Run `plain-healthcheck`** — the last thing this skill does. It validates every `config.yaml` and dry-runs every top module, so a feature is never finished while the project would fail to render. If it returns `FAIL`, work through its numbered list (fixing only `.plain` files / `config.yaml` / scripts — never generated code) by dropping back into the loop, then re-run. Repeat until `PASS`. Only then tell the user the feature is ready and remind them to re-render with `codeplain <module>.plain`.
|
|
108
118
|
|
|
109
|
-
##
|
|
119
|
+
## Next feature
|
|
110
120
|
|
|
111
|
-
After
|
|
121
|
+
After one feature is done, the user may describe the next. Start again from Phase 0 — a continuous
|
|
122
|
+
loop: **intent → scope → one-question loop → final review → intent → …**
|
|
112
123
|
|
|
113
|
-
##
|
|
124
|
+
## Error handling
|
|
114
125
|
|
|
115
|
-
-
|
|
116
|
-
-
|
|
117
|
-
-
|
|
118
|
-
-
|
|
119
|
-
- [ ] No circular concept references
|
|
120
|
-
- [ ] Every conflict / break / augment surfaced by the analyzers was put to the user as the next question and resolved before continuing
|
|
121
|
-
- [ ] Functional specs are language-agnostic
|
|
122
|
-
- [ ] All external interfaces are explicit (endpoint paths, methods, CLI args, formats, etc.)
|
|
123
|
-
- [ ] Acceptance tests are consistent with their parent functional specs
|
|
124
|
-
- [ ] User approved the final diff
|
|
125
|
-
- [ ] `plain-healthcheck` returned `PASS` after the final diff was approved
|
|
126
|
+
- **A user answer contradicts prior specs** → edit the affected snippet in place immediately; surface a non-trivial change in the next question.
|
|
127
|
+
- **`add-functional-spec` reports "too complex"** → ask the user a follow-up question to split the spec (next loop iteration); never split it alone.
|
|
128
|
+
- **A conflict / break / augment surfaces** → make it the next question (2c) and resolve it before continuing.
|
|
129
|
+
- **`plain-healthcheck` returns `FAIL`** → do not declare the feature done; fix the listed `.plain` / `config.yaml` / script items via the loop and re-run until `PASS`.
|
|
126
130
|
|
|
131
|
+
## Validation checklist
|
|
127
132
|
|
|
128
|
-
|
|
133
|
+
Run the loop block in `references/checklist.md` on each iteration, and walk the whole list during Phase 3 before declaring the feature done. It is a self-audit of this workflow — never a substitute for it. A box only counts as met when the spec is on disk and explicitly approved; complete any unmet step before finishing.
|
|
129
134
|
|
|
130
|
-
|
|
135
|
+
## Question style
|
|
131
136
|
|
|
132
137
|
- Prefer short, direct sentences over compound or nested clauses.
|
|
133
138
|
- Use plain words over jargon when both convey the same meaning.
|
|
134
|
-
- One idea per sentence
|
|
135
|
-
|
|
136
|
-
Simpler grammar must not come at the cost of detail. Keep every constraint, edge case, option, and piece of context the user needs to answer accurately. If simplifying a sentence would drop a detail, split it into more sentences instead.
|
|
139
|
+
- One idea per sentence; split a comma-chained list of clauses into separate sentences.
|
|
140
|
+
- Never drop detail to simplify — keep every constraint, edge case, and option the user needs, splitting into more sentences instead.
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
# Add Feature Workflow Checklist
|
|
2
|
+
|
|
3
|
+
Use this to verify the workflow was followed — never as a substitute for it. Run the loop block on each iteration, and run the whole list once more during Phase 3 before declaring the feature done. A box only counts as met when the spec is on disk and explicitly approved, not merely discussed. If a box is unmet, go back and complete the step.
|
|
4
|
+
|
|
5
|
+
## Phase 0 — Feature intent
|
|
6
|
+
|
|
7
|
+
- [ ] Invoked `load-plain-reference` first (unless already loaded this session).
|
|
8
|
+
- [ ] Read `references/phase-0-intent.md` and asked its five core questions one at a time.
|
|
9
|
+
- [ ] Asked no more than one focused clarification for an answer too vague to summarize.
|
|
10
|
+
- [ ] Wrote no project or specification files during Phase 0.
|
|
11
|
+
- [ ] Kept concepts, spec wording, implementation technology, and tests for later phases.
|
|
12
|
+
- [ ] Summarized motivation, affected user, desired outcome, observable success criterion, and
|
|
13
|
+
boundary with explicit exclusions; got explicit confirmation.
|
|
14
|
+
|
|
15
|
+
## Phase 1 — Scope
|
|
16
|
+
|
|
17
|
+
- [ ] Carried the confirmed feature-intent brief into scope and identified the likely target
|
|
18
|
+
`.plain` file(s) without restarting intent discovery.
|
|
19
|
+
- [ ] Read the target `.plain` file(s) and followed their `import`/`requires` chain before authoring.
|
|
20
|
+
- [ ] Picked the target module with a question only when genuinely ambiguous — otherwise started authoring immediately (no framing/scope/multi-part design questions here).
|
|
21
|
+
|
|
22
|
+
## Every loop iteration (Phase 2)
|
|
23
|
+
|
|
24
|
+
- [ ] Asked exactly one focused question via `AskUserQuestion` — not two bundled together — targeting one writable snippet (behavior, one concept/attribute, one edge case, one constraint, implementation guidance, or verification).
|
|
25
|
+
- [ ] Wrote the snippet to disk immediately after the answer via the dedicated edit skill — never hand-authored a `***plain` section.
|
|
26
|
+
- [ ] Every functional spec was authored via `add-functional-spec` (never hand-written); if it reported "too complex", split it via a follow-up question rather than alone.
|
|
27
|
+
- [ ] New concepts defined before they are referenced, with no circular references.
|
|
28
|
+
- [ ] Any answer that contradicted an earlier snippet was fixed in place before the next question — no stale intent left on disk.
|
|
29
|
+
- [ ] Every conflict / break / augment surfaced by the analyzers was put to the user as the *next* question (keep / augment / replace) and resolved before continuing.
|
|
30
|
+
- [ ] `***test reqs***` / `***acceptance tests***` authored only when the Conformance gate holds (a `config.yaml` with a valid `conformance-tests-script` pointing at an existing script).
|
|
31
|
+
- [ ] Asked the user whether the feature is fully covered before leaving the loop.
|
|
32
|
+
|
|
33
|
+
## Phase 3 — Final review
|
|
34
|
+
|
|
35
|
+
- [ ] Read the modified `.plain` file(s) in full.
|
|
36
|
+
- [ ] Verified: concepts defined before use, no cycles, chronological ordering correct end-to-end, functional specs language-agnostic, every external interface explicit (endpoints, methods, CLI args, formats), acceptance tests consistent with their parent specs.
|
|
37
|
+
- [ ] Presented the final diff and got the user's approval (change requests dropped back into the one-question loop, not a restart).
|
|
38
|
+
- [ ] Ran `plain-healthcheck`; worked its numbered list to `PASS` (fixing only `.plain`/`config.yaml`/scripts — never generated code) before declaring the feature ready and reminding the user to re-render with `codeplain <module>.plain`.
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
# Phase 0 — What is the feature intent?
|
|
2
|
+
|
|
3
|
+
Extract the intent of this feature before choosing files or authoring specifications. Ask exactly
|
|
4
|
+
five core questions, one at a time, and do not write to disk.
|
|
5
|
+
|
|
6
|
+
## Questions
|
|
7
|
+
|
|
8
|
+
1. **Motivation** — What problem or limitation should this feature address?
|
|
9
|
+
2. **Affected user** — Who should benefit from or interact with this feature?
|
|
10
|
+
3. **Desired outcome** — What should that user be able to accomplish after the feature exists?
|
|
11
|
+
4. **Success criterion** — What observable result would prove the feature is useful and working?
|
|
12
|
+
5. **Boundary** — What should this feature deliberately leave unchanged or out of scope?
|
|
13
|
+
|
|
14
|
+
Ask one focused follow-up only when an answer is too vague to summarize. Do not choose concepts,
|
|
15
|
+
spec wording, implementation technology, or tests yet.
|
|
16
|
+
|
|
17
|
+
## Feature-intent brief
|
|
18
|
+
|
|
19
|
+
Summarize the answers in five short bullets:
|
|
20
|
+
|
|
21
|
+
- Motivation
|
|
22
|
+
- Affected user
|
|
23
|
+
- Desired outcome
|
|
24
|
+
- Observable success criterion
|
|
25
|
+
- Boundary and explicit exclusions
|
|
26
|
+
|
|
27
|
+
Ask the user to confirm or correct the brief. Phase 0 finishes only after explicit confirmation.
|
|
28
|
+
Carry the brief into Phase 1 so scope discovery maps the intent onto existing modules instead of
|
|
29
|
+
restarting the feature discussion.
|
|
@@ -51,34 +51,14 @@ The new spec must not contradict any existing functional spec. Conflicting specs
|
|
|
51
51
|
Each functional spec must be unambiguous — the renderer should have only one reasonable interpretation. If a single line is not enough to fully disambiguate the behavior, use **nested sub-bullets** to add detail. Nested lines clarify the parent spec; they do not introduce separate functionality. Even with nested detail, the spec must still imply ≤ 200 LOC.
|
|
52
52
|
|
|
53
53
|
```plain
|
|
54
|
-
|
|
54
|
+
***functional specs***
|
|
55
|
+
|
|
56
|
+
- A :User: can send a :Message: to a :Conversation:.
|
|
55
57
|
- A :Message: must have non-empty content.
|
|
56
58
|
- The :Message: is appended to the end of the :Conversation:.
|
|
57
59
|
- All :Participant: members of the :Conversation: can see the new :Message:.
|
|
58
60
|
```
|
|
59
61
|
|
|
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
62
|
|
|
83
63
|
### Deterministic Interface
|
|
84
64
|
|
|
@@ -74,35 +74,14 @@ That coverage is achieved by running `analyze-func-specs` **once per spec being
|
|
|
74
74
|
Each functional spec must be unambiguous — the renderer should have only one reasonable interpretation. If a single line is not enough to fully disambiguate the behavior, use **nested sub-bullets** to add detail. Nested lines clarify the parent spec; they do not introduce separate functionality. Even with nested detail, the spec must still imply ≤ 200 LOC.
|
|
75
75
|
|
|
76
76
|
```plain
|
|
77
|
-
|
|
77
|
+
***functional specs***
|
|
78
|
+
|
|
79
|
+
- A :User: can send a :Message: to a :Conversation:.
|
|
78
80
|
- A :Message: must have non-empty content.
|
|
79
81
|
- The :Message: is appended to the end of the :Conversation:.
|
|
80
82
|
- All :Participant: members of the :Conversation: can see the new :Message:.
|
|
81
83
|
```
|
|
82
84
|
|
|
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
85
|
|
|
107
86
|
### Deterministic Interface
|
|
108
87
|
|
|
@@ -61,36 +61,17 @@ Implementation reqs are bullet points in the `***implementation reqs***` section
|
|
|
61
61
|
|
|
62
62
|
```plain
|
|
63
63
|
***implementation reqs***
|
|
64
|
-
- :Implementation: should be in Python 3.12.
|
|
65
|
-
- :Implementation: should use pip for dependency management.
|
|
66
|
-
- When writing CSV files, :Implementation: should use streaming writes to avoid holding large datasets in memory.
|
|
67
|
-
```
|
|
68
|
-
|
|
69
|
-
Reference defined `:Concepts:` where they add clarity. Implementation reqs in non-leaf sections apply to all subsections.
|
|
70
|
-
|
|
71
|
-
## Line syntax (hard rule)
|
|
72
64
|
|
|
73
|
-
|
|
65
|
+
- :Implementation: should be in Python 3.12.
|
|
74
66
|
|
|
75
|
-
-
|
|
76
|
-
- Nested detail is also a `- ` item, indented under its parent. The indentation alone is not enough; the leading `- ` is required.
|
|
67
|
+
- :Implementation: should use pip for dependency management.
|
|
77
68
|
|
|
78
|
-
|
|
69
|
+
- When writing CSV files, :Implementation: should use streaming writes to avoid holding large datasets in memory.
|
|
79
70
|
|
|
80
|
-
```plain
|
|
81
|
-
- :Implementation: tech stack will be finalized in Phase 2.
|
|
82
|
-
- Until then, treat this section as a placeholder so the renderer accepts
|
|
83
|
-
the file. Phase 2 will replace this with language, framework, HTTP
|
|
84
|
-
client, packaging, and architecture decisions.
|
|
85
71
|
```
|
|
86
72
|
|
|
87
|
-
|
|
73
|
+
Reference defined `:Concepts:` where they add clarity. Implementation reqs in non-leaf sections apply to all subsections.
|
|
88
74
|
|
|
89
|
-
```plain
|
|
90
|
-
- :Implementation: tech stack will be finalized in Phase 2.
|
|
91
|
-
- Until then, treat this section as a placeholder so the renderer accepts the file.
|
|
92
|
-
- Phase 2 will replace it with language, framework, HTTP client, packaging, and architecture decisions.
|
|
93
|
-
```
|
|
94
75
|
|
|
95
76
|
## Encapsulation Warning
|
|
96
77
|
|
|
@@ -15,7 +15,7 @@ Linked resources are external files referenced from within a `.plain` spec using
|
|
|
15
15
|
|
|
16
16
|
## Workflow
|
|
17
17
|
|
|
18
|
-
1. **Identify or create the resource file.**
|
|
18
|
+
1. **Identify or create the resource file.** Keep it inside the project — the `resources/` directory is the conventional home; it does not have to sit next to the `.plain` file.
|
|
19
19
|
2. **Add the markdown link** at the appropriate place in the spec.
|
|
20
20
|
3. **Verify the file path** is relative to the `.plain` file location.
|
|
21
21
|
4. **Read the file back** to confirm correct link syntax and path.
|
|
@@ -35,16 +35,15 @@ Use standard markdown link syntax inside any spec section:
|
|
|
35
35
|
|
|
36
36
|
***functional specs***
|
|
37
37
|
|
|
38
|
-
-
|
|
38
|
+
- An API conforming to the [API specification](resources/api_spec.yaml) is exposed.
|
|
39
39
|
```
|
|
40
40
|
|
|
41
41
|
## Path Rules
|
|
42
42
|
|
|
43
|
-
- Paths are resolved **relative to the
|
|
44
|
-
-
|
|
45
|
-
- No absolute paths.
|
|
43
|
+
- Paths are resolved **relative to the directory where `codeplain` is run** (the current working directory).
|
|
44
|
+
- Relative paths may traverse into parent directories — `[resource](../../resources/resource.md)` is valid — as long as they resolve to a real text file on disk.
|
|
46
45
|
- **No external URLs** — only local file references.
|
|
47
|
-
- **No folder paths** —
|
|
46
|
+
- **No folder paths** — the target must be a file, not a directory.
|
|
48
47
|
- Only text-based files are supported.
|
|
49
48
|
|
|
50
49
|
### A linked resource MUST be a single, text-based file that exists on disk
|
|
@@ -70,7 +69,7 @@ The **only** exceptions are URLs and paths that are *values the produced softwar
|
|
|
70
69
|
|
|
71
70
|
### What a linked resource CAN be
|
|
72
71
|
|
|
73
|
-
- A single file
|
|
72
|
+
- A single file inside the project, reachable by a path relative to where `codeplain` is run — parent-directory traversal (`../`) is allowed (`resources/` is the conventional home).
|
|
74
73
|
- A **text-based** file the renderer can read end-to-end: JSON, YAML, XML, HTML, Markdown, plain text, CSV, TSV, source code in any language (`.py`, `.js`, `.ts`, `.go`, `.java`, `.rb`, `.rs`, `.kt`, `.swift`, `.c`, `.cpp`, `.cs`, …), shell scripts, SQL, JSON Schema, OpenAPI, AsyncAPI, Protobuf `.proto`, GraphQL SDL, `.jolt`, `.env.example`, `.toml`, `.ini`, `.proto`, `Dockerfile`, etc.
|
|
75
74
|
|
|
76
75
|
## Common Resource Types
|
|
@@ -102,6 +101,6 @@ The **only** exceptions are URLs and paths that are *values the produced softwar
|
|
|
102
101
|
- [ ] **Target is a text-based file** (no binary extensions: `.png`, `.jpg`, `.jpeg`, `.gif`, `.bmp`, `.tiff`, `.webp`, `.ico`, `.pdf`, `.docx`, `.xlsx`, `.pptx`, `.zip`, `.tar`, `.gz`, `.mp3`, `.mp4`, `.wav`, `.exe`, `.so`, `.dylib`, `.class`, `.wasm`, …)
|
|
103
102
|
- [ ] **No URLs or folder paths anywhere in the surrounding `.plain` content** (not as link targets, not in body prose), with the sole exception of URLs / paths that are runtime values the generated software itself uses
|
|
104
103
|
- [ ] Path is relative to the `.plain` file, not absolute
|
|
105
|
-
- [ ]
|
|
104
|
+
- [ ] Path resolves to a real text file on disk from where `codeplain` is run (`../` traversal is allowed)
|
|
106
105
|
- [ ] Markdown link syntax is correct: `[display text](relative/path)`
|
|
107
106
|
- [ ] Resource content is relevant and adds value beyond what the spec text says
|
|
@@ -51,35 +51,18 @@ Test reqs are bullet points in the `***test reqs***` section:
|
|
|
51
51
|
|
|
52
52
|
```plain
|
|
53
53
|
***test reqs***
|
|
54
|
+
|
|
54
55
|
- :ConformanceTests: should be implemented using pytest framework.
|
|
56
|
+
|
|
55
57
|
- :ConformanceTests: will be run using "pytest" command.
|
|
58
|
+
|
|
56
59
|
- :ConformanceTests: must be implemented and executed - do not skip tests.
|
|
60
|
+
|
|
57
61
|
- :ConformanceTests: should mock all external HTTP calls.
|
|
58
62
|
```
|
|
59
63
|
|
|
60
64
|
Reference predefined concepts like `:ConformanceTests:` and any defined `:Concepts:` where they add clarity.
|
|
61
65
|
|
|
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
66
|
|
|
84
67
|
## Validation Checklist
|
|
85
68
|
|
|
@@ -31,8 +31,8 @@ Work through each question. If any answer is "yes", the specs likely conflict.
|
|
|
31
31
|
Do the two specs make mutually exclusive assertions about the same behavior?
|
|
32
32
|
|
|
33
33
|
```
|
|
34
|
-
Spec A:
|
|
35
|
-
Spec B:
|
|
34
|
+
Spec A: :Resource: items are returned sorted by name in ascending order.
|
|
35
|
+
Spec B: :Resource: items are returned sorted by creation date in descending order.
|
|
36
36
|
|
|
37
37
|
Verdict: CONFLICTING — both define the sort order for the same response,
|
|
38
38
|
but specify different fields and directions. A single implementation cannot
|
|
@@ -44,8 +44,8 @@ satisfy both unless scoped to different contexts.
|
|
|
44
44
|
Does one spec set a state or value that the other spec assumes is different?
|
|
45
45
|
|
|
46
46
|
```
|
|
47
|
-
Spec A: :TaskList:
|
|
48
|
-
Spec B: :TaskList:
|
|
47
|
+
Spec A: :TaskList: is initially empty.
|
|
48
|
+
Spec B: :TaskList: contains a default "Welcome" :Task: on first load.
|
|
49
49
|
|
|
50
50
|
Verdict: CONFLICTING — both define the initial state of :TaskList: differently.
|
|
51
51
|
```
|
|
@@ -55,8 +55,8 @@ Verdict: CONFLICTING — both define the initial state of :TaskList: differently
|
|
|
55
55
|
Does the later spec silently replace behavior established by the earlier spec without acknowledging it?
|
|
56
56
|
|
|
57
57
|
```
|
|
58
|
-
Spec A:
|
|
59
|
-
Spec B:
|
|
58
|
+
Spec A: :User: credentials are validated using an API key.
|
|
59
|
+
Spec B: :User: credentials are validated using OAuth 2.0.
|
|
60
60
|
|
|
61
61
|
Verdict: CONFLICTING — both define the authentication mechanism but pick
|
|
62
62
|
different approaches. The later spec overrides the earlier one.
|
|
@@ -67,8 +67,8 @@ different approaches. The later spec overrides the earlier one.
|
|
|
67
67
|
Are the two specs ambiguous enough that a renderer could interpret them as conflicting, even if the user intends them to be complementary?
|
|
68
68
|
|
|
69
69
|
```
|
|
70
|
-
Spec A:
|
|
71
|
-
Spec B:
|
|
70
|
+
Spec A: All :Resource: items are returned.
|
|
71
|
+
Spec B: Only active :Resource: items are returned.
|
|
72
72
|
|
|
73
73
|
Verdict: CONFLICTING (ambiguous) — "all" vs "only active" appear contradictory.
|
|
74
74
|
Could be resolved by scoping each to different conditions (e.g., filtered vs unfiltered).
|
|
@@ -79,8 +79,8 @@ Could be resolved by scoping each to different conditions (e.g., filtered vs unf
|
|
|
79
79
|
Do both specs impose constraints on the same `:Concept:` that cannot coexist?
|
|
80
80
|
|
|
81
81
|
```
|
|
82
|
-
Spec A: :BatchSize:
|
|
83
|
-
Spec B: :BatchSize:
|
|
82
|
+
Spec A: :BatchSize: is 100 items.
|
|
83
|
+
Spec B: :BatchSize: is 50 items for :Resource: types with attachments.
|
|
84
84
|
|
|
85
85
|
Verdict: COMPATIBLE — Spec B adds a conditional refinement, not a contradiction.
|
|
86
86
|
```
|
|
@@ -41,8 +41,8 @@ Work through each question for the pair under inspection. If any answer is "yes"
|
|
|
41
41
|
Do the two specs make mutually exclusive assertions about the same behavior?
|
|
42
42
|
|
|
43
43
|
```
|
|
44
|
-
Spec A:
|
|
45
|
-
Spec B:
|
|
44
|
+
Spec A: :Resource: items are returned sorted by name in ascending order.
|
|
45
|
+
Spec B: :Resource: items are returned sorted by creation date in descending order.
|
|
46
46
|
|
|
47
47
|
Verdict: CONFLICTING — both define the sort order for the same response,
|
|
48
48
|
but specify different fields and directions. A single implementation cannot
|
|
@@ -54,8 +54,8 @@ satisfy both unless scoped to different contexts.
|
|
|
54
54
|
Does one spec set a state or value that the other spec assumes is different?
|
|
55
55
|
|
|
56
56
|
```
|
|
57
|
-
Spec A: :TaskList:
|
|
58
|
-
Spec B: :TaskList:
|
|
57
|
+
Spec A: :TaskList: is initially empty.
|
|
58
|
+
Spec B: :TaskList: contains a default "Welcome" :Task: on first load.
|
|
59
59
|
|
|
60
60
|
Verdict: CONFLICTING — both define the initial state of :TaskList: differently.
|
|
61
61
|
```
|
|
@@ -65,8 +65,8 @@ Verdict: CONFLICTING — both define the initial state of :TaskList: differently
|
|
|
65
65
|
Does the later spec silently replace behavior established by the earlier spec without acknowledging it?
|
|
66
66
|
|
|
67
67
|
```
|
|
68
|
-
Spec A:
|
|
69
|
-
Spec B:
|
|
68
|
+
Spec A: :User: credentials are validated using an API key.
|
|
69
|
+
Spec B: :User: credentials are validated using OAuth 2.0.
|
|
70
70
|
|
|
71
71
|
Verdict: CONFLICTING — both define the authentication mechanism but pick
|
|
72
72
|
different approaches. The later spec overrides the earlier one.
|
|
@@ -77,8 +77,8 @@ different approaches. The later spec overrides the earlier one.
|
|
|
77
77
|
Are the two specs ambiguous enough that a renderer could interpret them as conflicting, even if the user intends them to be complementary?
|
|
78
78
|
|
|
79
79
|
```
|
|
80
|
-
Spec A:
|
|
81
|
-
Spec B:
|
|
80
|
+
Spec A: All :Resource: items are returned.
|
|
81
|
+
Spec B: Only active :Resource: items are returned.
|
|
82
82
|
|
|
83
83
|
Verdict: CONFLICTING (ambiguous) — "all" vs "only active" appear contradictory.
|
|
84
84
|
Could be resolved by scoping each to different conditions (e.g., filtered vs unfiltered).
|
|
@@ -89,8 +89,8 @@ Could be resolved by scoping each to different conditions (e.g., filtered vs unf
|
|
|
89
89
|
Do both specs impose constraints on the same `:Concept:` that cannot coexist?
|
|
90
90
|
|
|
91
91
|
```
|
|
92
|
-
Spec A: :BatchSize:
|
|
93
|
-
Spec B: :BatchSize:
|
|
92
|
+
Spec A: :BatchSize: is 100 items.
|
|
93
|
+
Spec B: :BatchSize: is 50 items for :Resource: types with attachments.
|
|
94
94
|
|
|
95
95
|
Verdict: COMPATIBLE — Spec B adds a conditional refinement, not a contradiction.
|
|
96
96
|
```
|