fsl-requirements · git:20261107.deab533 · 2026-11-07 · sha256 1e841322b265de00
fsl-requirements git:20261107.deab533A
Immutable. This exact content never changes and is served at /api/v1/blob/1e841322b265de00.
---
name: fsl-requirements
description: Write and verify FSL requirements-layer specs for PMs/PdMs from natural-language product requirements, acceptance criteria, forbidden flows, requirement IDs, traceability, decision tables, and NFRs such as permissions, audit, capacity, reliability, and SLA/deadline behavior, in English and optionally with a Japanese glossary and Japanese keywords. Use when the deliverable is a requirements specification, not business process discovery, software architecture, internal design, or implementation conformance.
---
# FSL Requirements Layer
A headerless document is FSL 4; to migrate it, see the migration guide in `docs/manual/LANGUAGE.md` ("FSL 4 to FSL 5 migration").
Use this skill to formalize PM/PdM requirements as a `requirements Name { ... }`
FSL file headed `fsl 5`. The deliverable is a requirements contract that
development can consume: the vocabulary (actors, entities, their lifecycles),
the events, the rules and decision tables with their requirement IDs and
source text, acceptance and forbidden scenarios, and optional conformance to an
upper business layer.
This skill owns the syntax of this layer:
- `references/syntax.md` — every requirements member, one checked spec walked
through member by member;
- `references/keywords.md` — the English / Japanese keyword table and the
particles;
- `references/glossary.md` — how to write a `glossary` and the Japanese
spelling of the example below.
Expressions (`and`, `or`, `not`, comparisons, arithmetic), the `verify { }`
block, and kernel members are those of `../fsl/references/syntax.md`. The shared
`../fsl/references/layers.md` covers edition-aware seams and delegates requirements
syntax here. **Do not use earlier requirements `process` / `transition` forms**:
`fsl 5` rejects them (`mixed_requirements_forms`). `fslc migrate --edition 5`
rewrites an existing process-form file into the forms below.
To maintain an existing headerless (FSL 4) requirements document as it is,
without migrating it, use the FSL 4 forms in
[`../fsl/references/layers.md`](../fsl/references/layers.md#requirements-the-requirements-layer).
An inline `implements` seam is fail-closed. How a failing seam changes the
top-level result and exit is in the core's
["Inline `implements` failures are fail-closed"](../fsl/SKILL.md#how-to-run);
reading `implements.result` and the seam fields is in
[fsl-verify §2](../fsl-verify/SKILL.md#2-results-that-look-green-but-are-not) and
[§3](../fsl-verify/SKILL.md#3-reading-the-deciding-fields-per-result).
## English is canonical
- Write the spec with English names and English keywords. The names on the
left of every declaration are the **canonical names**: ASCII, and the only
names the kernel, generated tests, conformance vectors and implementations
ever see.
- Japanese is a reading layer on top: a `glossary` gives a canonical name a
Japanese alias, every keyword of the table has a Japanese spelling, and the
particles が・を・で・は mark the words of a phrase. Add it after the English
spec checks, never instead of it. Both spellings lower to the same kernel.
- Requirement, scenario and policy IDs stay ASCII (`REQ-RET-001`). Quoted
source text is kept verbatim in its own language.
- Event names (`event request = …`) and the spec name are declared canonical;
they reach implementations.
## Faithfulness gate (definition of done)
The two failures this layer exists to prevent are **dropping** a requirement (a
thin spec) and **inventing** a rule the source never stated. Hold both ends:
- **Cover the source.** Every source requirement maps to at least one checked
declaration — a `rule`, a `decide` table or one of its rows, an event's
`covers`, a `scenario`, a `forbidden`, a `goal`, a `policy` — or is recorded
in the memo as out-of-scope or needs-decision.
- **Ground the spec.** A `rule ID "text"` and a `decide ID "text"` own their
ID and carry the verbatim text; a decision row or an event adds
`covers ID "text"` for the requirement it serves. Use a `MODEL-` / `ASSUME-`
prefixed ID for an explicit modeling choice the source does not state.
- **Do not invent to reach green.** If `check` / `verify` needs a guard,
bound, stage, actor, deadline or exception the source does not state, that
is a question for the human, not a default you may pick.
- **Make it mechanical.** `fslc check <file> --strict-tags --requirements
ids.txt` (the memo's IDs in a file, one per line) warns
`unreferenced_requirement` for an ID nothing carries; done = zero of those
and zero `untagged`, read from `warnings[]` of an exit-0 `ok`. `fslc lint`
must also report no findings (`non_canonical_id` for an ID outside the ID
policy).
## Boundary
Produce requirements-layer artifacts only: the `requirements` spec, its
scenarios and forbidden traces, the `implements` seam to a confirmed business
layer, and `fslc scenarios` output when requested. Do not write internal design
state, architecture, queues/outboxes, service modules, database schemas,
implementation adapters, or design-to-requirements refinement maps unless the
user explicitly moves to design work (`fsl-design`). If a requirement implies a
design decision but does not state it, ask rather than choosing.
Generating a stakeholder-facing requirements document from the checked spec is
a separate activity (`fsl-requirements-document`).
## Workflow
1. Post a requirements formalization memo in chat before writing `.fsl`:
- terms: actors, entities, the stages each entity goes through, the verbs
(one per thing an actor does), inputs and their ranges; with a Japanese
source, the Japanese term next to each English name (this becomes the
glossary, step 6)
- requirement normalization: trigger, constraint, exception, boundary for
each requirement
- coverage map: every source requirement ID → the declaration that will
carry it (rule, decide row, event covers, scenario, forbidden, goal,
policy), or out-of-scope / needs-decision
- acceptance and forbidden traces, with expected outcome
- upper business contract, if any
- assumptions and questions that affect behavior
2. Write the vocabulary and the lifecycles: `number`, `const`, `enum`,
`actor`; one `entity` per thing with a lifecycle, each field with an
initial value; one `lifecycle A -> B -> C` line per path. Then one `event`
per lifecycle edge (`Actor verb Entity A -> B`), or `in S` for one that
stays in its stage; `with field` for an input; `set` for other updates;
`fault event` for something that happens to the system rather than a step
anyone takes (not fair).
3. Write each rule the source states as a `rule ID "text" { … }` with the
statement that says it: `allow … only when / only for`, `never … when /
for`, `P only after Q`, `E enters T only from S`, `always …`, `deadline`.
A rule that rejects an operation is a guard **and** a `forbidden` trace
that tries it (`check` warns `never_rule_without_forbidden` when the trace
is missing). Name every `always` / `enters` statement (`AmountPositive:
always …`): the name is the property's name in results and diagnostics.
4. Where the source decides "who handles it / which way" by case, write a
`decide ID "text" for Stage Entity { Case => Actor verb … }` table. `check`
proves over the field domains that the rows cover every case
(`decide_not_exhaustive`, with the uncovered values) and never overlap
(`decide_overlap`, with the shared values). Close a real gap with a row or
`otherwise`; close a gap no reachable state has with `assuming Q`, which
`verify` then proves. Do not add a row the source does not state to make
the table exhaustive — ask.
5. Add `measure`, `goal`, `policy`, then `scenario` (positive) and `forbidden
… then rejected` (negative) traces, then the `verify { instances … values
… }` bounds. If the spec has intended stopping points, declare them in
`terminal { }` (step 7 shows why).
6. Optional Japanese layer: a `glossary` as the first item, Japanese keywords
and particles (`references/glossary.md`). Re-run the whole check loop.
7. Check loop: `fslc check` → `fslc check --strict-tags --requirements
ids.txt` → `fslc lint` → `fslc verify --depth 8` (read
`implements.result` when there is an upper layer) → `fslc verify --engine
induction` → `fslc scenarios`. A requirements spec of this edition gets no
derived `terminal { }`: a run that ends with every entity in a final stage
is reported as a `deadlock` warning until `terminal { }` names those
stages. Reading the results, the origin self-check with `fslc explain`,
`mutate`, and the completion report are in `../fsl-verify/SKILL.md`.
8. For SLA / deadline requirements, use `deadline` inside the rule with a
`time { }` block (`references/syntax.md`) and confirm the deadline can
fail: the variant without its `urgent` event must be `violated`.
## Example
A complete spec of this layer (checked as written):
```fsl check
fsl 5
requirements ReturnRequirements {
number Amount
const AUTO_LIMIT = 1
actor Customer, System, Manager, Finance
entity Return {
amount: Amount = 0
lifecycle Draft -> Requested -> Approved -> Refunded
lifecycle Requested -> Rejected
}
concept SmallReturn = Return where amount <= AUTO_LIMIT
concept LargeReturn = Return where amount > AUTO_LIMIT
event request = Customer requests Return with amount Draft -> Requested
event auto_approve = System approves Return Requested -> Approved
event mgr_approve = Manager approves Return Requested -> Approved
event reject = Manager rejects Return Requested -> Rejected
event refund = Finance refunds Return Approved -> Refunded
rule REQ-RET-001 "A return is requested with a positive amount" {
allow Customer requests Return only when input amount > 0
}
decide REQ-RET-002 "Who decides a requested return" for Requested Return {
SmallReturn => System approves
covers REQ-RET-003 "Returns at or below AUTO_LIMIT are approved by the system"
LargeReturn => Manager approves or rejects
covers REQ-RET-004 "Returns above AUTO_LIMIT are approved or rejected by a manager"
}
rule REQ-RET-005 "A requested return keeps a positive amount" {
AmountPositive: always Return not in Draft has amount > 0
}
terminal { forall r: Return { stage(r) == Refunded or stage(r) == Rejected } }
measure refunded_returns = count Return in Refunded
goal GOAL-RET-001 "A return can be refunded" some Return can reach Refunded
policy POL-RET-001 "Every requested return is eventually decided"
every Return in Requested must eventually be Approved or Rejected
scenario AC-RET-001 "A small return is approved by the system and refunded" {
Customer requests r1 with amount 1
System approves r1
Finance refunds r1
then r1 is Refunded
}
scenario AC-RET-002 "A large return is rejected by a manager" {
Customer requests r1 with amount 2
Manager rejects r1
then r1 is Rejected
}
forbidden FB-RET-001 "A return of zero is not requested" {
Customer requests r1 with amount 0
then rejected
}
forbidden FB-RET-002 "A manager does not decide a small return" {
Customer requests r1 with amount 1
Manager approves r1
then rejected
}
}
verify {
instances Return = 2
values Amount = 0..3
}
```
What the lowering makes of it, which `fslc explain --readable` prints:
- `Return` becomes an identity type of 2 instances; its fields become
`return_amount: Map<Return, Amount>` and `return_stage: Map<Return,
ReturnStage>`, and the stages, in the order they first appear in the
`lifecycle` lines, become the enum `ReturnStage`.
- Each event becomes a fair action of the same name with the entity instance
as its first parameter (`request(r, amount)`). Its stage move becomes a
`requires` and an assignment; `with amount` stores the input in the field.
- The `allow` adds `requires amount > 0` to `request`; the table adds
`requires return_amount[r] <= AUTO_LIMIT` to `auto_approve` and the other
row's condition to `mgr_approve` and `reject`. `AmountPositive` becomes an
invariant.
- The `scenario` / `forbidden` blocks are acceptance and forbidden traces
over those actions; `r1` is instance 0.
These lowered names are what a design layer maps to (`../fsl-design/`) and
what an `implements` block below this spec's business layer is written in.
## The seam to a business layer
`implements BusinessName from "business.fsl" { … }` is written at the top of
the spec (after the `glossary`, which must be the first item). `verify` checks
the seam and reports it as `implements.result`; it passes only on `refines`. For the example above
against the business spec of `../fsl-business/SKILL.md`:
```fsl-fragment
requirements ReturnRequirements {
implements ReturnToBe from "return_tobe.fsl" {
action request(r, amount) -> request(r)
action auto_approve(r) -> decide_ok(r)
action mgr_approve(r) -> decide_ok(r)
action reject(r) -> decide_ng(r)
action refund(r) -> refund(r)
map return_stage_via_Approved[r: Return] =
return_stage[r] == Approved or return_stage[r] == Refunded
}
…
}
```
- The block is written in **canonical, lowered names** (`return_stage`, the
action parameters). A glossary alias there is an error
(`alias_in_implements`).
- An event whose name matches a business transition is matched
automatically only when the parameters match too; the business transition's
parameter is `c`, so write the correspondence explicitly.
An automatically matched event whose actor differs from the business
transition's actor is a check-time `kind: "type"` error (`maps auto actor mismatch …`).
- The stage enum must be the same on both sides: list the lifecycle so the
stages first appear in the order of the business `stages` line (otherwise
`type 'ReturnStage' is declared differently in the impl and abs specs`).
- A business no-bypass policy (`must have passed through Approved`) adds a
history flag to the business kernel (`return_stage_via_Approved`); map it.
## Guardrails
- Completeness is part of the contract: a spec that formalizes only some
requirements is unfinished. Cover every source requirement or record why
not — but never close a gap by inventing a rule the source does not state.
- Carry every rejection rule the source states in a checked declaration, not
in prose alone: an `allow` / `never` / `only after` / decision row on the
event it restricts, plus a `forbidden` trace that tries the rejected call.
A rule that lives only in a quoted text or a `//` comment is checked by
nothing, and the model then accepts every call the remaining guards allow.
If an invariant or a forbidden trace catches the gap, add the rule the
source states (or ask) — never delete the invariant or the trace.
- A decision table states the whole case split. If `check` reports a gap or
an overlap, the source is ambiguous or the concepts are wrong; fix the
concepts or ask. `otherwise` is for a source that says "everything else";
`assuming` is for a gap that the rest of the spec already excludes, and
`verify` must prove it.
- Stop at the requirements contract. The handoff downward is a **refinement
seam**: the design layer refines this spec with `fslc refine`, so author it
as a contract a lower layer can honor. Upward, `implements` is the same kind
of seam to the business layer.
- Do not add retries, storage rules, asynchronous jobs, queues, screen
internals, or API shape unless the source explicitly requires them.
- A green `implements` result means this contract conforms to the agreed
business layer. It does not prove any design or implementation.
- When a counterexample suggests a repair, decide whether it is a missing
requirement, a wrong interpretation, or an intentional exception before
changing a guard, a row or an invariant.