About add-functional-spec
add-functional-spec is a skill for authoring one functional spec in a .plain module. It guides checks for complexity, conflicts, clarity, ordering, and optional conformance tests.
Key features
- 01Draft and trim a spec
- 02Check the complexity limit
- 03Analyze conflicts in a batch
- 04Ensure explicit interfaces
- 05Add conformance tests
Use cases
- 01Add one feature requirement
- 02Check a spec against existing behavior
- 03Prepare a spec with test scenarios
Install
Copy the command and run it in a terminal.
npx skills add https:/ / github.com/ plainlang/ plain-forge --skill add-functional-specAfter installing, type/add-functional-specin Claude Code to confirm it loaded.
Details
From the project READMEAdd Functional Spec
Load the rules first. Before you read, edit, or write any .plain file, invoke the load-plain-reference skill and read the rule files it routes you to. Skip this only if load-plain-reference already ran in this conversation.
Workflow
- Identify the target
.plainfile. If ambiguous, ask the user. - Read the entire file to understand existing definitions, implementation reqs, and all current functional specs (including those in
requiresmodules). - Pick the func-id and draft the functional spec. Choose a func-id per
semantic-ids.mdand confirm no# <func-id>heading with that id exists in the module. Draft the body, a statement line and any detail under it, following the rules below, then run the review pass inconcise-specs.mdover the draft — trim it to the shortest wording that keeps every fact before any other check. - Analyze complexity — use the
analyze-if-func-spec-too-complexskill to verify the drafted spec implies ≤ 200 LOC. If too complex, use thebreak-down-func-specskill to split it, then repeat from step 3 for each resulting spec. - Check for conflicts with every existing functional spec — this is critical. Run
analyze-func-specsonce with the new spec plus all existing specs (in the file and in anyrequireschain) 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, runresolve-spec-conflicton that pair; re-runanalyze-func-specsover the touched set after each resolution until the verdict isCOMPATIBLE. - Append the spec to the end of the
***functional specs***section as a# <func-id>heading followed by its body, one blank line after the previous functionality (specs are chronological; new ones go last). - Read the file again to confirm correct placement and syntax.
- Write the test plain file. When conformance tests are enabled for the module, invoke
add-test-scenariosfor the new functionality (see Test Scenarios below).
Rules
Complexity Limit
Each functional spec must imply a maximum of 200 changed lines of code. If the requirement is too large, use the break-down-func-spec skill to split it into multiple smaller, independent specs. Do not include LOC estimates in the spec text.
Chronological Ordering
Specs are rendered incrementally, top to bottom. The renderer has no knowledge of future specs — only previously rendered specs are in context. This means:
- A new spec can reference behavior defined by earlier specs.
- A new spec cannot assume anything about specs that come after it.
- Place the new spec at the correct position if it must come before a future spec; otherwise append at the end.
Language Agnosticism
Write in terms of behavior, concepts, and domain logic — not implementation constructs. Avoid language-specific types, generics syntax, framework annotations, or other constructs tied to a particular language or framework. General technical terms (null values, JSON types, HTTP status codes) are fine. Language-specific guidance belongs in ***implementation reqs***.
No Conflicts
The new spec must not contradict any existing functional spec. Conflicting specs are the most costly outcome. Before adding, review all existing specs and verify the new one is compatible. Use analyze-func-specs to check the new spec against every existing spec in one batched call — it lists every conflicting pair, so a pair-by-pair analyzer is not needed. If ambiguity exists, add explicit detail to eliminate any conflicting interpretation. For each conflicting pair the batched analyzer reports, use the resolve-spec-conflict skill to diagnose and fix it before proceeding.
Disambiguation
Each functional spec must be unambiguous — the renderer should have only one reasonable interpretation. If the statement line is not enough to fully disambiguate the behavior, add - bullets or sentences under it. The detail clarifies the statement; it does not introduce separate functionality. Even with the detail, the spec must still imply ≤ 200 LOC.
***functional specs***
# send-message
A :User: can send a :Message: to a :Conversation:.
- A :Message: must have non-empty content.
- The :Message: is appended to the end of the :Conversation:.
- All :Participant: members of the :Conversation: can see the new :Message:.
Deterministic Interface
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.
Encapsulation
Functionality must be self-contained in the spec text. requires modules only import functional specs, so do not rely on implementation reqs to convey behavior — they won't be visible in downstream modules.
Test Scenarios
After inserting the spec, invoke add-test-scenarios for it. It writes the functionality's test plain file (by convention tests/<module>/<func-id>.plain), which should hold at least one conformance scenario when conformance tests are enabled for the module.
Validation Checklist
- Spec implies ≤ 200 lines of code changes
- No conflict with any existing functional spec
- Written in language-agnostic terms (no language/framework specifics)
- All external interfaces are explicit (endpoint paths, methods, CLI args, formats, etc.)
- All referenced
:Concepts:are defined in***definitions*** - Sentences are short, clear, and unambiguous
- Trimmed per
concise-specs.md: nothing obvious, no rationale, no uncheckable adjectives or hedges, plain words throughout - No redundancy with existing specs
- Placed in correct chronological position (usually at the end)
- The func-id follows
semantic-ids.mdand is unique in the module -
add-test-scenarioswas invoked for the new functionality when conformance tests are enabled