The basis for every project here was itself unversioned: no .git, no remote, no history. Changes to it had no diff and no revert, and two of its own guards could not run at all -- doc-claims.sh and doc-triggers.py both read git history, so the script written to catch documentation drift could not be run against the documents that define drift. This is the tree as it stands, including work that until now existed only as loose files on disk: WORK_CYCLE.md, TOOLS.md, the Portainer image-line fix in deploy.py, the status vocabulary corrected to the four words the conformance checker actually enforces, the Exempt: mechanism documented, and the Forgejo instance named in README.md. secrets.sh --tracked reports one candidate, migrate.sh:480. It is the comment documenting the three Postgres credential shapes that script redacts, with literal placeholders, and it is left alone deliberately: GUARDS.md section 2 is that a source-grep guard must tell code from the comment about code, and deleting an explanation to quiet a scanner is the failure it names. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> |
||
|---|---|---|
| .. | ||
| README.md | ||
| commit-msg | ||
| post-commit | ||
| pre-commit | ||
README.md
Git hooks
Status: Current
Owner: <who maintains this>
Last reviewed: <YYYY-MM-DD>
Governs: docs/architecture/githooks/** and the .githooks/ a project installs
Review trigger: A new guard the repository wants run before a commit; any change
to what a commit message must contain.
Three hooks, and the reason they live in the repository rather than in
.git/hooks: that directory is not versioned, so a hook living there protects
exactly one clone on exactly one machine. These are committed, and one command
points git at them.
Install
mkdir -p .githooks
cp docs/architecture/githooks/{pre-commit,commit-msg,post-commit} .githooks/
chmod +x .githooks/*
git config core.hooksPath .githooks
core.hooksPath is per-clone, so every checkout runs that last line once. Say so
in the project README — an uninstalled hook fails silently, which is the same
class of problem the hooks exist to prevent.
What each one does
| Hook | Guard |
|---|---|
pre-commit |
typecheck, then the test suite, when .ts/.tsx is staged |
commit-msg |
refuses a message with no conventional type |
post-commit |
pushes to the remote, so a guarded commit does not sit locally |
Two settings worth making before you rely on them
pre-commit will use a local test database if you tell it where one is.
Set TEST_DB_PORT and TEST_DB_URL together. Leave them unset and the database
suites skip, which is the right default on a machine with no Postgres — but it
means the hook is running a fraction of the suite, so release.sh is
deliberately stricter and refuses rather than warns.
post-commit pushes. That is the intent — the commit that first added a
pre-commit hook to the reference project sat unpushed for a day, guarded and
invisible — but it is a surprise if you were not expecting it. Read it before
installing it on a repository with a protected branch.
It has a second consequence worth knowing about: the push is what the Command
Center reads, so whatever documentation was not in that commit is now behind
the code by one push. That is the mechanical reason docs/WORK_CYCLE.md asks
for doc edits in the same commit as the change rather than in a tidy-up
afterwards — with this hook installed, "I will document it next commit" means
the site has already published the version without it.
Why a hook and not CI
Both. These are the guards that must run before the artifact exists: a hook that fires after a push, or a CI job that fires after a publish, catches the problem once it is already somewhere it cannot be taken back from. CI is the second opinion; this is the one that runs first.