Fts Constitution
Write project invariants as a checkable FTS model.
Skill metadata
| Source | Bundled (installed by default) |
| Path | skills/software-development/fts-constitution |
| Version | 1.0.0 |
| Author | Digitable |
| License | Apache-2.0 |
| Platforms | linux, macos, windows |
| Tags | fts, spec-driven, constitution, invariants, requirements, ftspec |
| Related skills | fts, fts-specify, fts-admit, fts-memory |
Reference: full SKILL.md
The following is the complete skill definition that Digit loads when this skill is triggered. This is what the agent sees as instructions when the skill is active.
FTS Constitution
Write the project constitution: invariants that no future specification may violate. The constitution is an executable FTS model whose utilities return a count of violations, not a markdown list of good intentions.
When to Use
- A new spec-driven project starts and the non-negotiables must be fixed first.
- The same argument ("can we give 50 % just this once?") returns every quarter.
- A rule was violated in production and must become machine-checkable.
- An existing markdown "principles" document must become enforceable.
When NOT to Use
- The constraint depends on live data (stock levels, rates, a user's balance). A constitution reads scalars supplied to it; it does not query anything.
- The constraint is about code style, architecture, or process. FTS checks domain values, not review etiquette.
- The rule holds only under a quantifier ("for every line item in the order"). FTS has no collections; the honest move is to name the gap in the README.
- One project, one team, one constitution. Do not write per-feature ones.
Prerequisites
ftsc— compiler (module linking, functors) — andftspec— corpus checker (applies invariants to specs) — both onPATH. Install from the FTS language repository (github.com/the-homeless-god/fts).- Optionally Digit's MCP tools
fts_check/fts_testfor a single model. - The corpus root layout, because roles are decided by path:
<корпус>/
constitution.fts ← this skill (also: конституция.fts)
specs/<NNN-имя>/spec.fts ← fts-specify
memory/NNNN-имя.fts ← fts-memory
mapping/*.fts ← functors between specs
The Contract That Makes a Statute Work
ftspec applies a constitution utility to a spec utility only when:
- field
итогis present — the spec utility's result is substituted there; - every other required field of the invariant's input object matches a field of the spec utility's input object by name and by type.
No match, no application — silently. This is the single most common failure:
the statute compiles, its examples pass, and it checks nothing. An invariant
whose object has only итог matches every utility in the corpus; that is how a
project-wide statute is written.
Consequence: constitution field names are the project's vocabulary. Pick the customer's words and make the specs use the same ones.
Procedure
-
Collect the non-negotiables. Ask for the sentence that ends an argument: "we never discount more than 30 %", "we never take money without confirmation". Three to seven statutes. More than that and nobody reads them.
-
Turn each into: object → utility → rules → property → examples. Start from
templates/constitution.fts.объект «Скидка на чек»сумма является деньгамиитог является деньгамиутилита «Скидка не превышает предела»принимает «Скидка на чек»возвращает числоначинает с 0правило «Скидка выше 30 процентов чека»если сумма больше 0и итог больше 30 процентов от поля суммато добавить 1The same statute on the English surface — one parser, one canonical model:
utility "Discount stays under the cap"accepts "Discount on a receipt"returns numberstarts with 0rule "Discount above 30 percent of the receipt"if сумма is greater than 0and итог is greater than 30 percent of field суммаthen add 1Domain names in quotes are never translated. The surface is a team decision (which language the customer speaks); the semantics are identical. Surfaces cannot be mixed inside one file — the language is fixed by the first non-comment line.
-
One statute, one rule, one field where possible. A rule reading three fields produces a diagnostic nobody can act on: the engineer cannot tell which boundary was crossed.
-
Guard the grid, not just the sensible inputs.
ftspecevaluates invariants on a finite grid built from the spec's own thresholds: each threshold and threshold ± 1. For a threshold of 0 that includes −1, where "30 percent of сумма" changes sign. Add the guarding condition (если сумма больше 0) or you will ship false positives and the team will stop reading the output. -
Add boundary examples. Threshold, threshold − 1, threshold + 1. An example deep inside the range verifies nothing. Every rule needs one example that actually fires it.
-
Compile and check.
ftsc check <корпус>ftspec check <корпус> -
Verify the statutes actually bind. In the
ftspec checkoutput readsummary.invariantsandsummary.gridPoints. Ifinvariantsis smaller than the number of utilities you wrote, some of them matched nothing — go back to the contract above and fix the field names. -
Record each threshold as a decision in
memory/(skillfts-memory) and reference it from a comment in the constitution. A number without a recorded reason gets renegotiated in six months.
Success Criteria
-
constitution.ftssits at the corpus root andftsc checkpasses. -
ftspec checkreportsdiagnostics: []andconstitution: constitution.fts. -
summary.invariantsequals the number of statutes you wrote. - Every statute has boundary examples and they pass.
- Every threshold has a decision file in
memory/. - A deliberately illegal spec is rejected with
FTSPEC_CONSTITUTION— test this once, on a throwaway copy of the corpus. An untested gate is not a gate.
Common Mistakes
| Mistake | What happens | Fix |
|---|---|---|
| Invariant field names differ from the specs' | Statute never applies, silently | Match names and types exactly |
No итог field | The utility's result is never examined | Add итог |
| Statute fires on grid edges (negative sums) | False positives, output ignored | Add a guarding condition |
| One rule reads four fields | Diagnostic is unactionable | Split into rules |
Threshold with no decision in memory/ | Renegotiated every quarter | Write the decision |
| Statute written as prose in a comment | Checks nothing | Prose belongs in comments, statutes in utilities |
| Twenty statutes | Nobody reads them; the corpus becomes noise | Keep the ones that actually end arguments |
The Honest Boundary
This is not a proof that the requirements are consistent. What is actually checked:
- type agreement — field types across the corpus (
ftsc); - condition intersection — whether two rules can apply at once, on
intervals over constants (
ftspec); - invariants on a finite grid — thresholds ± 1, capped at 1024 points per
utility; a truncated grid is reported in
summary.gridTruncated; - example coverage — whether any example fires each rule.
Not checked, and the constitution must say so out loud:
- requirements with dependencies between fields beyond the comparisons FTS has;
- anything under a quantifier ("for every position in the order");
- anything depending on external data — a database, a rate, another service;
- whether the statute is right. A wrong threshold checked exactly is still wrong.
A violation between two grid points can be missed. This is an empirical argument (rule behaviour changes at condition boundaries), not a logical one. Write these limits into the project README; do not let them be discovered later.
Verification
ftsc check <корпус>
ftspec check <корпус>
ftspec report <корпус>
Working corpus: skills/software-development/fts-admit/example/.
Language and case catalog: https://courses.digitable.life/fts/.