Troubleshooting & gate recovery
Most first-run friction is environmental, not code. This chapter collects the failures people actually hit on a fresh checkout and the shortest path back to a green tree. If you haven’t set up the environment yet, start with Development loop → First-time setup; if a commit is being rejected for signing, see Your first PR.
First-run failures
Docker daemon not running
Every just target shells into the dev container via docker compose run …, so a stopped daemon makes all of them fail at once — usually
with Cannot connect to the Docker daemon at unix:///var/run/docker.sock.
Start (or restart) Docker Desktop / the docker service and re-run the
target. Nothing in aozora runs on the host toolchain; the daemon is a
hard prerequisite for build, test, lint, and ci alike.
Disk full / image build fails midway
docker compose build dev pulls a rust:*-bookworm base and layers a
pinned toolchain on top. A build that dies partway through — or a
no space left on device from just build — almost always means the
Docker volume is out of room, not a Dockerfile bug.
docker system prune # reclaim dangling images / layers / build cache
Keep roughly 5 GB free for the dev image plus the named cargo /
sccache volumes. After pruning, re-run docker compose build dev; the
layer cache resumes from the last good step.
Commit signing fails
Signed commits are mandatory. If a commit is rejected — or the
post-commit re-amend rolls your commit back because the signer was
unavailable — your SSH/GPG signing key isn’t reachable from the
container’s git context. Walk through the signing setup in
CONTRIBUTING.md → First-time setup
and confirm git config commit.gpgsign is true with a configured
user.signingkey.
This is the three-layer defense working as designed: a post-commit
re-amend, the signing-check pre-push command
(scripts/check-signed-commits.sh), and GitHub’s “require signed
commits” ruleset. Do not weaken any layer — the redundancy is
intentional. Fix the key, don’t disable the gate.
Hooks not firing
If fmt / clippy / typos aren’t running on commit, or the signing
re-amend never happens, lefthook isn’t installed for this clone. Hooks
live in .git/hooks/, which is per-clone and never committed, so a
fresh checkout always needs:
just hooks # (re)install the lefthook git hooks
Re-run it any time hooks go quiet (e.g. after git init-level surgery
or switching the hooks path).
Reading lefthook output
Lefthook prints one icon per command in its post-run summary. The non-obvious one:
- 🥊 is a failure, not decoration. Lefthook falls back to its
branding glyph when the underlying tooling (a
docker compose run, or a multi-stepjustrecipe with background jobs) buries the real exit status. Treat 🥊 exactly like a plain failure mark and scroll up: the actual error line is in the command output above the summary, not in the summary itself.
Each command in lefthook.yml also carries a fail_text: hint
naming the recipe responsible, so a failing push prints both the raw
output and a pointer at what to fix.
When a gate fails
just ci runs the full pipeline; the pre-push hook runs the same jobs
plus a deep property sweep. When one trips, this table maps the symptom
to its recovery recipe:
| Gate | Symptom | Recovery |
|---|---|---|
| coverage | Region coverage below the floor | just coverage-html, open coverage/html/index.html, add tests for the uncovered regions |
| clippy / fmt | cargo fmt --check diff or clippy denial | just fmt to auto-format, fix any clippy findings, then re-run just lint |
| drift-gate (schema) | wire JSON Schema is stale | just schema to regenerate, then commit the diff |
| drift-gate (types) | TypeScript .d.ts drift | just types to regenerate, then commit the diff |
| drift-gate (langs) | Generated host-SDK wire types are stale | just types-langs to regenerate, then commit the diff |
| typos | Spelling hit | just typos to see every hit; fix, or add a genuine term to typos.toml |
| deny / audit | License / advisory failure | Read the captured log under /tmp (the recipe writes the full cargo deny / cargo audit output there), then update the dependency or the deny.toml exception |
For the schema / types gates, the regenerate-then-commit step is the
fix — the gate only checks that the committed artefact matches what the
generator would emit, so a stale checkout fails until you regenerate
and stage it. See Wire format for what wire /
.d.ts / langs each cover.
Escape hatches
Two exist, and they are not interchangeable:
SKIP_TAGS=deep git push— the narrow hatch. Skips only the taggeddeepcommand (the 4096-caseprop-deepsweep) while leavingsigning-check,ci, and everything else in force. Use this when a deep-sweep regression is unrelated to your change — and file an issue against the failing crate so it doesn’t stay hidden.LEFTHOOK=0— the nuclear hatch. Disables all hooks, includingsigning-check. An unsigned commit pushed this way is rejected server-side by the ruleset anyway, so you gain nothing but a later, more confusing failure. Avoid it. Reach forSKIP_TAGSinstead.
See also
- Development loop — the daily
justrecipes and watch mode. - Your first PR — the lightweight doc-fix path.
- Testing strategy — what each coverage layer asserts.