1. Dictionary
Two vocabulary traps worth naming:
- Module spec ≠ Feature Spec. The module spec is a map of what exists and is edited freely. A Feature Spec is a contract for a change and is frozen once approved.
- FS ≠ TS. The FS says what to build and how we will know it is done. The TS says how to build it. A small feature needs no TS at all — the FS carries an Execution section instead.
2. The chain
implemented with no PR recorded fails CI; a table in the module spec that no FS claims is
reported as uncovered.
3. Identifiers
FS-{MODULE}-{NNNN} — four digits, so a module can hold up to 9999 feature specs without
renumbering. Module codes:
Examples:
FS-LOY-0002, FS-CORE-0014. Numbering is per module — modules grow at different
rates and a global sequence would force coordination for nothing. Numbers are never reused, not
even after a spec is withdrawn.
A feature that genuinely spans modules belongs to core, or it is two feature specs with a
depends_on between them. It is never one FS filed under two modules.
4. Versioning
Two different schemes, deliberately:
A spec starts at
0.1.0 in draft. It reaches 1.0.0 the moment it is approved — that is what
approval means: the contract is now stable enough to build against.
5. Lifecycle
The two rules that make this a system rather than a folder
R-S1 — No code merges for a feature whose FS is not approved.
The PR states its FS id; CI reads that spec’s status and blocks otherwise.
R-S2 — An approved FS is immutable in its normative sections.
Changing it is not editing it: bump the version, add a changelog row saying what changed and
why. If the change reverses a DEC or an ADR, that ADR is written first.
Normative sections are: Scope, Non-scope, Behaviour, Data, API, Events, Acceptance criteria.
Context, Notes and Open questions can be edited freely at any status.
6. Frontmatter — the machine-readable contract
Every FS carries this. The validators read it; nothing here is decoration.tables, events and endpoints are what the coverage validator uses: every entity in a module
spec must be claimed by exactly one FS. That is how we find out that something was designed and
never assigned to a delivery.
The Delivery record section in the body is generated from implementation. Never hand-write
it — same discipline as a generated data model.
7. Writing a module, step by step
- PRD. One document, from
TEMPLATE-prd.md. Who it is for, the jobs it does, explicit non-goals, how success is measured, pricing implications, phases. Written once, reviewed, then rarely touched. Write it before the technical design — if the PRD is hard to write, the module is not understood yet. - Module spec. The technical shape: entities and their invariants, events emitted, endpoints with the single permission each declares, extension points. This one stays alive and is edited as reality changes.
- Decompose into features. Cut along independently shippable increments, not along tables. The test: can this ship alone and be worth something? If not, it is a section inside another FS. Record the resulting list in the module hub as a roadmap table.
- Write each FS from
TEMPLATE-feature-spec.md, in dependency order. Statusdraft→review→ owner approves →1.0.0. - Execute. Small feature: the FS’s Execution section is enough. Larger: open a TS declaring its archetype. The agent works from the approved FS, not from a chat prompt — that is the whole point of the system.
- Merge and record. The PR appends an
implementationentry and moves the status toimplemented. Then the module spec is updated to match what now exists.
8. Languages
Module documentation ships in English and Spanish, so anyone can read and review a module in full — not only the people who work in English all day.- English is authoritative.
modules/{m}/is the normative version: it is what agents read, what CI validates, and what wins on any discrepancy. - Spanish is a faithful translation, not a variant. It lives at
es/modules/{m}/and mirrors the tree exactly, file for file. A translation that says something different from the original is a defect, not a local decision. - Both carry the same front-matter — same
id,versionandstatus. Thetranslationvalidator fails when they diverge, which is what stops a version bump from silently leaving the Spanish behind. - Every translated file opens with a line naming its authoritative original.
docs/public/) is a different document with a different audience, and neither
side of it is a translation of anything here.
9. Validation
Report mode is the default while modules are still being decomposed;
strict is what CI runs.
docs:validate:mint runs Mintlify’s own CLI. It is deliberately not pinned and not a CI
gate: it validates against a hosted product that ships several releases a day, so pinning it would
check us against a stale contract. Run it before a documentation push; the rules it enforces that
we already know about are encoded in links and navigation, which are pinned and deterministic.
9b. Writing for Mintlify
Three constraints are not obvious, and each of them silently produced a 404 rather than an error..mdis parsed as MDX. A bare<followed by anything that is not a letter reads as the start of a JSX tag and kills the page. Latency budgets are the common case: writep95 <300 ms, notp95 <300 ms. A bare{opens a JavaScript expression — escape it as\{, or put the token in backticks. HTML comments fail the same way: use{/* ... */}, never<!-- ... -->. Inside fenced code blocks and inline code spans, none of this applies.
A page must never be namedREADME.md. Mintlify skips those files entirely; the navigation then points at a page that, as far as the build is concerned, does not exist. Section overviews areoverview.md. The oneREADME.mdindocs/internal/is deliberate — it is the GitHub-facing file, and it is the only place relative,.md-suffixed links are correct.
Links resolve from the docs root, not from the file. A link whose target is written asThedata.mdworks on GitHub and 404s on the site, and so does a barestandards/data. Every internal link is absolute and extensionless:/standards/data.
links and navigation validators enforce all three.