API Reference
The style schema
The house rules the prose and commit linters read, and how to change them for your own project.
workflow.style in torta.json states the house rules in one place.
banned
Characters banned from prose: markdown files and code comments. Code, string literals and vendored bytes are left alone.
{ "banned": { "\u2014": "em dash" } }
The key is the character itself, written above as a JSON escape.
comment
The shape one comment block has to fit:
{ "comment": { "maxRows": 4, "maxColumns": 80 } }
A run of comment-only rows is a block. Over maxRows tall is a finding on its
first row. Over maxColumns wide is a finding on the row itself, where a wide
character counts as two columns.
exemptBlockOpeners
{ "exemptBlockOpeners": ["/**"] }
A block whose opener is on this list is exempt from the row count, and only from that.
prose and source
{ "prose": [".md"], "source": [".js", ".py", ".json", ".sh"] }
A prose file is read whole. A source file is tokenised, and only its comments are read. An extension in neither list is not read at all.
shebangs
{ "shebangs": { "sh": ".sh", "bash": ".sh", "python": ".py" } }
How to read a file with no extension, such as a git hook.
includeDocstrings
false by default. Turn it on to read Python docstrings as prose too.
skipDirs and skipFiles
{
"skipDirs": ["node_modules", "vendor", "__pycache__", ".git"],
"skipFiles": ["package-lock.json", "*.min.js", "*.lock"]
}
A skipFiles glob is tried against the file name and against the path, so
vendor/* and *.min.js both work.
commit
The commit message rule, in the same form:
{
"pattern": "^([a-z]+)\\(([a-z0-9][a-z0-9._/-]*)\\): (\\S.*)$",
"shape": "type(scope): summary",
"minColumns": 80,
"maxColumns": 90
}
commit.description
The summary is a verb, then its object, then where or what. The example the
config carries is
feat(module-ticketnumber): update the house rule linters for the settings screen widget.
Three shapes are rejected outright, each named in the finding:
| Suffix | Called | Kept anyway |
|---|---|---|
ed |
past tense | words ending eed |
ing |
a gerund | none |
s |
third person | words ending ss or us |
A table of irregular verbs maps wrote, built, sent and the rest back to
their imperative. The three complaints:
write it in the imperative, for example "update the wizard" rather than "updated the wizard"a description is a verb and its object, for example "update the wizard", not "update"a description starts with a lower case verb, for example "update the wizard"
commit.body
A body, if there is one, is - bullets up to 90 columns, then optional
trailer sections in a fixed order:
| Section | Shape |
|---|---|
refs |
refs:[ticket#1][ticket#2] |
agents |
agents:[infra,archi,frontend] |
models |
models:[fable(review),opus(build)] |
hitl |
hitl:[published] |
Every bullet is an imperative description too.
commit.trailers
Attribution trailers are refused anywhere in the message: Co-Authored-By:,
Claude-Session:, Generated-By:, the phrase “generated with”, and any
co-authored-by spelling.
commit.exempt
Exempt from the shape, the body grammar and the banned characters: Merge,
Revert, fixup!, squash!, and Initial commit.
Running the linters
python3 engine/lint-prose.py [path ...]
python3 engine/lint-prose.py --summary .
python3 engine/lint-commit.py <message file>
python3 engine/lint-commit.py --range <revision range>
lint-prose prints one finding per line as path:line: message and exits 1
when there is one. --summary adds counts per top level directory. There is
no --fix.