Compatibility
From 1.0.0, rulec's version numbers follow Semantic Versioning. This page says what "compatible" means for each thing rulec reads or writes, so that a 1.x release is a promise you can hold it to.
The short form: a rule that passes rulec check under 1.0 passes under every 1.x, means
the same thing, and its generated code is called the same way and answers the same.
What every 1.x keeps
- The
.rulelanguage. Every construct 1.0 accepts keeps its syntax and its meaning — the state machines (machine,held,never,once,scenario, and the values carried besides the state: a furthercarry,initial <input> <value>,always), the prerequisites of an enum's values (prerequisiteswithstart,requires,nothingandordered by), several sequences walked by any number ofcount,sum,max,minandfoldlines in the order what they read gives them, a fold's answer named with->, what a rule reads out of the caller's object — a total, a largest or smallest value of one field over a collection (from sum,from max,from min), and a sequence taken from a collection (elements … from) — the inputs drawn at random with what is claimed of the chances of the answers (random,chances, a pick by weight withtake_weightedanddraw), and the merging of files — open values (open … combine), contribution files (contribution,to,after,into,extend), open tables, clauses, enums and blocks, and the pinssignature:andmeaning:— included. Anapplyheading pinned withsha256:, as written beforemeaning:came, is read as it was. New constructs may be added. - The names you may use. The reserved words — the 65 words E009 refuses as a name,
listed in
src/kw.rs— are fixed at 1.0, and a test holds the list. A later release may add words to the language, but never to that list: a new word is read only where no name can stand, or gives way to a name the rule declares. A name that is legal under 1.0 stays legal. The words of the merging of files came this way:open,combine,product,contribution,to,intoandextendare read by their place in the line, and a rule may name an inputopenorto. - What the generated code answers. For a rule that passes, the code generated in every language returns the same outputs for the same inputs under every 1.x, and so does the reference evaluator: a record replayed under a later 1.x gets the answer it got before.
- How the generated code is called. The names of modules, functions, types, fields and
errors, the integers on the wire (§10.2), the record a call writes, the
_traced,_record,_fromand_chancesfunctions — everythingrulec apidescribes. New functions may appear beside them. - The command line. Commands, flags, their values, and the exit codes: 0 notes only, 1 findings, 2 bad arguments or a file that cannot be read. New commands and flags may be added.
- Diagnostic codes. A code keeps its meaning. A code that is retired stays in the ledger,
marked as retired, and its number is never given to anything else. New codes may be added;
a new warning leaves a rule that passed passing (W901, a key written in a rule, came this
way, and means the same in every language of ritsu). Two numbers took a new meaning before
1.0, when a rule came to walk more than one sequence and to walk one twice (§15.219): E031,
which had said that a rule needs a second walk, says that one item reads the elements of two
sequences, and E020, which had said that a rule declares two, says that nothing walks one.
E044 narrowed before 1.0, when an
applycame to read a rule that applies another, walks a sequence or produces an enum of its own (§15.230): it says that applies go round, or that the callee cannot be read or does not passcheck, and a rule it stopped for the other three now passes. E033 and E040 say of a value a contribution adds and of a contribution's pin what they say of an imported value and of an apply's pin (§15.228, §15.230). - Machine-readable formats. Every
--format jsonoutput, the vectors, the fixtures and the replay manifest, theadapter/1andextract/1protocols, the certificate, the MCP tools with their arguments and resources, the GitHub Action's inputs, and the names of the release archives and of the packages beside them (i2y/tap/ritsuin Homebrew, the.deband the.rpm) keep every field they have at 1.0, with the same meaning. A 1.x may add a field, and may add a value to a set of values the documentation lists — a new diagnostic code, a newwhatin thedomainofdiff. A new construct may give a field a new shape where it is written, and nowhere else: aconstraintthat adds inputs up is the one place itsleftorrightis a list of names, in the certificate and underpreconditionsalike, and anapplypinned withmeaning:the one place its entry ofapplieshas an emptysha256, besidemeaning. A reader should skip a key it does not know, and treat a value it does not know as it treats an unknown key. Where a format carriesv, that is the version of its shape:checkis at 2 and the certificate at 1, and an output withoutvis at 1. A re-checker of certificates refuses avit was not written for, astools/recheck.pyand the Lean program inproofs/do.
What a 1.x may change
- Prose. Diagnostic titles, notes and hints,
--help, the text reports, whatrulec docwrites, and this documentation. Read codes and JSON keys, not sentences (§11 principle 5). - The text of the generated code. Its layout, comments and local names, and its header,
which names the version of rulec that wrote it. Upgrading rulec therefore means running
rulec genagain and committing what changed;rulec gen --checkfails until you do. CI installs one exact release (uses: i2y/ritsu@v1.0.0), and there is no moving tag such as@v1: the generated files would go stale under it without a change of yours. - A pass that should have been a failure. When a 1.x finds that an earlier version let through something this page or what it proves says it catches — a gap or an overlap it missed, a line it read into nothing — the fix makes that rule fail. A false green is not kept for the sake of compatibility. Each such change is named in the release notes, with the kind of rule it can affect.
- An answer that disagreed with the reference evaluator. If the code generated in some language answered differently from the reference evaluator, the fix makes it agree, and the release notes name it.
- An input a door let through. If the code generated in some language answered an input
outside its contract that the reference evaluator refuses — a value of another kind, an input
that is not there, a combination a
constraintrules out — the fix makes it refuse the input, as the other languages do, so a call that was answered can fail from then on. The sentence a door says is prose. The release notes name the change. - An input a door refused inside its contract. If the code generated in some language, or
the reference evaluator, refused an input its contract allows — a whole number written with
a point or an exponent,
1000.0or1e3— the fix makes it take the input, so a call that failed can be answered from then on. The release notes name the change. - How long a check takes, as long as a rule that finished within the default budget still does.
What is not covered
- The Rust library (
src/lib.rs). Its modules are public so that the tests, ritsu (its commands and its playground) andrulec mcpcan reach them, not as an interface; they change with the tool. Use the command line,rulec mcp, or the generated code. - ritsu's playground and the site.
- Everything under
experiments/.