Skip to content
ttorta docs

Troubleshooting

The house rule linters

What the commit hook and the prose linter complain about, in their own words.


Every threshold comes from workflow.style in torta.json. See the style schema for the keys.

Enable the hook once per clone:

git config core.hooksPath .githooks

The commit hook refused my message

The hook prints one finding per line as <file>:<line>: <complaint>.

subject is not type(scope): summary

A subject is a lower case type, a scope of lower case letters, digits and . _ / -, then a colon, a space, and a summary.

subject is N columns, the shape is at least 80

Or at most 90. Columns are terminal columns, so a wide character counts as two.

the subject summary starts with x, which is past tense

Also which is a gerund and which is third person. Write update the wizard, not updated, updating or updates. When the word is an irregular verb the finding names the form to use instead, as in the imperative is write``. Otherwise it quotes the rule:

write it in the imperative, for example `update the wizard` rather than `updated the wizard`

A word that ends in a rejected suffix without being an inflection is kept: press and focus end in s, proceed ends in ed, and a stem with no vowel left was never a suffix.

the subject summary is 1 word

a description is a verb and its object, for example `update the wizard`, not `update`

the subject summary starts with Update, which is not lower case

a description starts with a lower case verb, for example `update the wizard`

bullet is N columns, the shape is at most 90

Body bullets have their own width.

body line is not - one thing this commit did

A body is - bullets, then the trailer sections: refs:[ticket#1][ticket#2], agents:[infra,archi,frontend], models:[fable(review),opus(build)], hitl:[published].

the refs: line is not refs:[ticket#1][ticket#2]

The section was recognised by its lead but does not match its pattern.

agents: sits below models:

The order is bullets, then refs:, then agents:, then models:, then hitl:. The same finding appears as a bullet sits below ... when a bullet comes after a section.

em dash in the commit message

em dash in the commit message; a comma or a colon says the same

attribution trailer

No Co-Authored-By:, no Claude-Session:, no Generated-By:, no “generated with”, anywhere in the message, not only at the end.

the commit message is empty

Nothing but git’s own comment lines.

Messages git wrote itself

A subject beginning Merge, Revert, fixup!, squash!, or exactly Initial commit, is exempt from the shape, the body grammar and the banned characters, but not from the trailer scan.

Getting past it

git commit --no-verify bypasses the hook. The backstop a review or CI runs is:

python3 engine/lint-commit.py --range A..B

The prose linter found something

python3 engine/lint-prose.py .
python3 engine/lint-prose.py --summary .

One finding per line as path:line: message, exit 1 when there is one. --summary adds counts per top level directory.

em dash in prose (U+2014)

Rewrite with a comma, a colon, or a full stop.

em dash in a comment (U+2014)

The same character inside a code comment. A banned character inside a string literal is allowed.

comment block is N rows, the shape is at most 4

A run of comment-only rows is a block. Cut it. A trailing comment after code does not join a block.

A block whose opener is exempt, /** by default, is not counted for rows, but is still counted for width and may not carry a banned character.

comment row is N columns, the shape is at most 80

Measured in terminal columns, so an emoji or a CJK character counts as two.

There is no –fix

Asking for one prints why:

lint-prose: there is no --fix. What replaces a banned character is a writing
decision (a dash, a comma, a colon, a rewrite), and a comment block over the
shape has to be cut, not reflowed.

lint-prose: no such path

That path does not exist. Exit code 2 means the linter could not run, where 1 means findings.

What is never read

Directories named node_modules, vendor, __pycache__ and .git, and files matching package-lock.json, *.min.js or *.lock. Extensions outside the prose and source lists are not opened.