Skip to content

Hooks and the ratchet

No. 013

cube check reports what is wrong with a knowledge base. The git hooks keep it from getting worse: they run the same checks on every commit, merge and push, and refuse a change that adds an ERROR. The hooks work the same way for a person and for any agent that commits through git.

In the alpha, the hooks run on Linux only. On macOS and Windows they refuse every commit until a later release adds those systems’ ownership checks (see the known limits).

cube adopt writes the hook scripts into .cube/hooks/. cube setup turns them on:

cube setup

It writes the absolute paths of cube, git, gh and python3 into your global git config, and core.hooksPath=.cube/hooks and merge.ff=false into the repository (a fast-forward merge runs no hook). The hooks never run gh.

The hooks run a program only when its file and every folder above it are owned by root and not writable by others. A program in your home folder, such as a cube the installer put in ~/.local/bin, passes only when you list it yourself in cube.allowUserOwned in your global git config. cube setup says when the hooks will refuse a program and prints the command that opts it in:

git config --global --add cube.allowUserOwned <the absolute path of the program>

With the opt-in, every hook prints a warning that this is not tamper-proof against agents that run as your account. Cube never writes the opt-in itself: it is your decision. cube doctor shows how the hooks judge each program.

  • pre-commit checks the staged tree and compares its ERRORs with those of HEAD (the ratchet, below). A refused commit prints the fix brief, which cube check --format agent also prints, and writes it to .cube/cache/fix-brief.md.
  • commit-msg checks the Cube-* trailers of the message and writes the Cube-Checked stamp on a commit that passed.
  • pre-push checks every pushed commit again against its first parent.

A crashed or unreadable kit check judges nothing, so it refuses the commit too (CHECK_FAILED).

A commit since adoption that carries no valid Cube-Checked stamp, for example one made with git commit --no-verify, is reported as SHORTCUT_HOOK_BYPASS. Only a person can clear it.

The ratchet lets a knowledge base with old ERRORs move forward, but never backward:

  • A commit with no ERROR passes.
  • While ERRORs remain, a commit passes only when it removes at least one and adds none.
  • A commit that keeps exactly the same ERRORs needs a trailer that says why (a move or a migration).
  • A merge passes when it adds no ERROR that none of its parents has. ERRORs it inherits never block it.
  • A commit that raises a new SHORTCUT_* finding is refused unless it carries a rule-change act.

The ratchet compares findings by their code, the file or node they are about, and their details. Line numbers and message text do not count, so moving lines in a file does not change the count.

A kit’s own rules cannot be quietly weakened

Section titled “A kit’s own rules cannot be quietly weakened”

The checks also judge the change itself. The SHORTCUT_* codes catch the shortcuts a person or an agent might take to make a finding go away instead of fixing it:

Code What it catches
SHORTCUT_RULE_WEAKENED kit.yml judges less than it did at HEAD.
SHORTCUT_SUPPRESS A suppression marker or a new allow-list entry.
SHORTCUT_EVIDENCE_REMOVED A test, fixture or ledger artifact deleted, or the file a finding points at deleted with the finding.
SHORTCUT_TEST_EDITED A test changed in the same commit as the code it tests.
SHORTCUT_PLACEHOLDER A required field filled with TBD, TODO, n/a or another placeholder.
SHORTCUT_GUARD_OVERRIDE A guard switched off, or a generated file edited by hand.

Every SHORTCUT_* code is an ERROR from the first release. So a commit that weakens the kit’s rules is refused, like any other commit that adds an ERROR.

Keeping such a change is a rule change. A rule change is a person’s act: a person commits it with a Cube-Act trailer that names the rule and its old and new severity. Removing a pack is a rule change too. The finding codes page lists every code.

In the alpha, cube records each rule change, each cleared bypass and each change to the code that judges commits with a HUMAN_ONLY_BY_AGENT warning. It does not check signatures on those commits yet.

These checks guard the knowledge base, not your machine. An agent that runs as your account can still replace a cube program your account owns, or change your global git settings, and the checks cannot see that. Full protection needs a cube that root owns, which a later release installs; the alpha’s per-user install works only with the opt-in described in the programs the hooks run, and every hook then warns you.