guard-spec-translation-sync¶
Block a commit that stages one language of a multilingual specification topic
while leaving a tracked sibling translation behind. It keeps every
spec/<topic>/<lang>.md set moving in lockstep — one canonical language, the
rest strict translations.
See References → Common contract for the pinning, fail-open, and primary-checkout-versus-worktree conventions that apply across every hook.
Prerequisites¶
- pre-commit on the
PATH. - A
gitrepository whose specs follow thespec/<topic>/<lang>.mdlayout. The hook shells out togitonly; it has no other runtime dependency.
Arguments¶
| Argument | Default | Purpose |
|---|---|---|
--lang <code> |
en de |
A language that must stay in sync. Repeat the flag for each language (--lang=en --lang=de --lang=fr). A topic with only one configured language is never blocked. |
--spec-dir <path> |
spec |
The root directory holding <topic>/<lang>.md trees. |
Arguments are supplied through pre-commit's args: list:
hooks:
- id: guard-spec-translation-sync
args: [--lang=en, --lang=de, --spec-dir=spec]
Behaviour¶
- Inspects the staged set, not a file list. Registered with
always_run: trueandpass_filenames: false, the hook reads the staged paths itself viagit diff --cached. That's deliberate: pre-commit may split a per-hook file list across parallel processes, which could separate anen.md/de.mdpair and cause a false positive. Reading the whole staged set once avoids that. - Enforces lockstep per topic. When at least one configured
<lang>.mdof a topic is staged, every configured language whose file is tracked inHEADfor that topic must also be staged. A missing, tracked sibling blocks the commit with exit 1 and lists the unstaged files. - Ignores brand-new topics. A topic with no tracked sibling yet (for example
a freshly created
spec/<topic>/en.mdwith node.mdinHEAD) is allowed — that's a completeness concern, not a sync concern. - Fails open in CI. When
CIis set, the hook exits 0 immediately; CI runspre-commit run --all-files, where there is no staged set to reason about. - Never mutates state. It only refuses the commit; the index and working tree are left untouched.
Example¶
# .pre-commit-config.yaml in the consumer repository
repos:
- repo: https://github.com/nolte/pre-commit-hooks
rev: v1.0.0
hooks:
- id: guard-spec-translation-sync
A commit that stages only the canonical language of an existing topic:
✖ Spec translations out of sync — commit blocked.
The nolte spec convention keeps every spec/<topic>/<lang>.md in
lockstep. You staged one language of a topic but left its tracked
sibling translation(s) unstaged:
spec/hook-authoring/de.md
Troubleshooting¶
- The hook blocks a legitimate, deliberate work-in-progress. Bypass the
single commit with
git commit --no-verify, then bring the translation in sync in a follow-up. - A new language should be enforced. Add it to the
argslist (--lang=fr). The hook only requires languages it's told about. - Specs live outside
spec/. Point the hook at the right root with--spec-dir=<path>. - CI commits are unexpectedly blocked. They aren't: the hook exits 0 whenever
CIis set. To enforce translation parity in CI, run a dedicated job rather than this commit-time guard.
Related¶
spec/hook-authoring/— the reference knowledge base this hook was designed against.