Documentation
Rules
A document's rules live in one file, .semgit/rules.yaml, on main. Writ always reads that copy — never a branch's own edited version of it — so a submission can't quietly loosen the rule that would have caught it.
A real example
This is the file from Writ's own demo document, unedited:
version: 1
instructions: |
Be strict about claims that outrun the evidence. A reported effect size needs
a table or citation in the same paragraph.
rules:
- id: claims-need-evidence
check: llm
severity: flag
text: "A new claim needs evidence in the same paragraph."
scope: {headings: [4. Discussion]}
- id: no-intensifiers
check: regex
severity: flag
pattern: "\b(obviously|clearly|very|significantly)\b"
- id: methods-frozen
check: protected_section
severity: block
scope: {headings: [3. Results]}
allow: [maintainers]
Three rules, three different check types:
claims-need-evidenceasks a model to judge, in plain English, whether a changed sentence in the Discussion section is backed by a citation nearby.no-intensifiersis a plain regular expression, checked deterministically against every new sentence in the whole document — no model call, no judgment.methods-frozenblocks any change of meaning under the Results heading unless it comes from someone on themaintainerslist.
The three check types
Today there are exactly three, and nothing else:
| Check | How it decides | Can it block? |
|---|---|---|
llm | A model judges the changed text in plain English against a rule you write in plain English. | No — capped at needs attention, never blocked. |
regex | A plain regular expression against the new sentence text. Deterministic, no model call. | Yes, if you set severity: block. |
protected_section | Deterministic: did anyone outside an allow-list change the meaning of anything under these headings? | Yes, and this is the one meant to be a hard stop. |
Severities
- block — the finding has to be fixed or waived by a maintainer, with a reason, before the submission can be finished.
- flag — opens a discussion (a "needs attention" thread); doesn't stop a merge on its own.
- note — an annotation attached to the sentence, nothing more.
Honest limits
What a rule can't do today, by design or by what's built so far:
- An
llmcheck can never block a submission outright — the worst it can do is open a discussion. If you need a hard stop, it has to be a deterministicregexorprotected_sectionrule. - There is no way to run arbitrary code as a check — the three types above are the whole vocabulary. Anything a rule can catch has to be expressible as one of them.
instructions(the text prepended to every model call for the document) is capped at roughly 2,000 tokens.- Changing the rules file is itself always treated as a meaning-changing submission and follows the document's approval policy. Writ continues checking that submission against the copy already on
main, so it cannot loosen the rule used to judge itself. Today the only way to change it is a real Git push to.semgit/rules.yamlon its own branch, proposed like any other submission; the Rules page in the web app shows you the parsed file and lets you dry-run it againstmain, but has no editor of its own yet. - That dry-run only checks what's already on
main— there's no way yet to preview how a rule would judge a branch you haven't proposed as a submission.
See What isn't built yet for the current state of the rules editor specifically.