CLAUDE.md · git:20261001.20a4c6c · 2026-10-01 · sha256 df6b1f3d2c27b4c6
CLAUDE.md git:20261001.20a4c6cA
Immutable. This exact content never changes and is served at /api/v1/blob/df6b1f3d2c27b4c6.
# CLAUDE.md — zat.env repo
This is the `zat.env` repo: a reproducible Claude Code harness — skills, hooks, conventions, and an install script that wires them into the live system. For what the system does and how downstream users adopt it, see [README.md](README.md). For machine-wide Claude conventions active in every session, see [`claude/global-claude.md`](claude/global-claude.md) (symlinked to `~/.claude/CLAUDE.md`).
This file is for developers modifying this repo: what each piece is, what must stay in sync, how to test changes.
## What this repo contains
- `zat.env-install.sh` — wires this repo's config into the live system (git config, symlinks, skills, hooks). Idempotent; safe to re-run.
- `hw-bootstrap.sh` — optional system provisioning (apt packages, NVIDIA drivers, Docker, Tailscale, Claude Code). Targets Ubuntu 22.04 dev boxes; downstream adopters typically don't need it.
- `claude/global-claude.md` — machine-wide Claude conventions
- `claude/references/` — detailed reference docs (networking, ML/GPU) read on demand
- `claude/skills/` — global Claude Code skills: `/spec` (with `backlog` and `plan` subcommands), `/codereview`, `/codefix`, `/security`, `/architect`, `/tester`, `/pr`
- `bin/` — helper scripts: `review-external.sh` (multi-model reviewer, stdin diff → stdout findings), `spec-backlog-apply.sh` (deterministic BACKLOG.md mutator), `codereview-marker` (single-source codereview push marker hash/write/path), `codereview-skip` (one-shot push-gate bypass), `zatmux` (tmux session toggle)
- `gitconfig/` — versioned git aliases and global gitignore, included via `~/.gitconfig`
- `hooks/` — Claude Code hooks: `pre-push-codereview.sh` (gates `git push` on passing review), `allow-venv-source.sh` (auto-approves venv activation outside auto mode), `post-tool-exit-plan-mode.sh` (reminds about `/spec plan` after exiting plan mode)
- `tests/` — structural lint and behavioral tests; `tests/run-all.sh` runs every suite
- `docs/` — extended walkthroughs (e.g., `hardware-setup.md`)
## Working on this repo
**Shell scripts** (`hw-bootstrap.sh`, `zat.env-install.sh`, `bin/*`):
- Must be idempotent — safe to run multiple times
- Use `set -euo pipefail` at the top
- Guard installs with existence checks (`command -v`, `[[ -d ... ]]`, etc.)
- Print `==> Section name` banners so output is scannable
**Skill files** (`claude/skills/<name>/SKILL.md`):
- Self-contained prompt; YAML frontmatter then Markdown
- Skills must be self-sufficient — they start with empty context and gather their own information
- Keep each SKILL.md under ~500 lines; use `references/` subdirectory if details grow
- After modifying any skill or hook, run `tests/run-all.sh` to check structural consistency
**Hook scripts** (`hooks/*.sh`):
- Idempotent and side-effect-free other than blocking/allowing the action
- Exit 0 = allow, exit 2 = block (stderr is shown to Claude)
- A PreToolUse `permissionDecision: allow` skips the auto-mode classifier and the permission rules, so an auto-approve hook must make no decision when `permission_mode` is `auto`. `allow-venv-source.sh` does; `tests/test-allow-venv-hook.sh` covers it
- Registered in `~/.claude/settings.json` by `zat.env-install.sh`
**gitconfig files**:
- `gitconfig/aliases.gitconfig` uses `[alias]` block — plain git alias syntax
- `gitconfig/ignore-global` is a plain gitignore file (no ini sections)
**README.md vs. CLAUDE.md audience split** — CLAUDE.md is for developers working on this repo: what to check, what must stay in sync, how to test. README.md is for downstream users: how the system works, the design philosophy, what to expect. They serve different purposes and should not repeat each other. Operational guidance (contract points, lint instructions) goes in CLAUDE.md. Conceptual explanations (enforcement model, trade-offs) go in README.md. Keep the directory overview section in README.md updated whenever the repo structure changes.
**Coding Practices sync** — the `## Coding Practices` section in `README.md` is a verbatim copy of the bullet points in `claude/global-claude.md`. Whenever `global-claude.md`'s Coding Practices section changes, update `README.md` to match.
**Prompt/infrastructure boundary** — this repo has two kinds of logic: deterministic (hook scripts, helper scripts, marker files) and instructed (skill prompts interpreted by the LLM). They interact at specific contract points where prose and code must stay in sync. `tests/lint-skills.sh` enforces each one:
- **Marker hash computation and path.** Codereview Step 8 writes the push marker via `codereview-marker write`; the pre-push hook reads the current hash via `codereview-marker hash` and compares it to the marker file at `codereview-marker path`. The skip marker is at `codereview-marker skip-path`. Codereview resolves the review base via `codereview-marker base` in Step 2 (review scope), Step 5 (security surface), and Step 5.5 (external reviewers), which exposes the same `@{upstream}` → `origin/<branch>` → empty-tree resolution the gate uses, so the diff the human review reads matches in scope the diff the push marker hashes; in particular a first push (no upstream) resolves to the empty tree, so the review covers the entire committed tree rather than reading as "nothing to review" (the IC-Panel failure mode, where a no-upstream first push was misread as an empty review surface and a hand-picked spot check was recorded as a clean review). All five pieces (hash, write, base, path, skip-path) come from the same `bin/codereview-marker` script, so PROJ_HASH derivation, UPSTREAM resolution (with the `@{upstream}` → `origin/<branch>` → empty-tree fallback chain), sha256 truncation, the exclusion list, and the marker directory layout are single-sourced. Markers live under `${XDG_CACHE_HOME:-${HOME}/.cache}/claude-codereview/` (mode 0700, per-user), not `/tmp`; the older `/tmp/.claude-codereview-<hash>` location was vulnerable to a cross-user symlink race. Lint enforces that neither the SKILL.md nor the hook nor `codereview-skip` carries inline `UPSTREAM=`, `${UPSTREAM}`, `sha256sum ... cut`, `/tmp/.claude-codereview-`, or `md5sum | cut -c1-8` patterns — they all belong in the script. The hook MUST fail closed on unexpected `codereview-marker` error (`exit 2`, not the older `exit 0`), so a missing-from-PATH or otherwise broken `codereview-marker` cannot silently bypass the gate; the message must say "Refusing to push" and never "Allowing push." The previous parallel-bash-snippet contract drifted silently in PanelForge when the LLM split codereview's Step 8 snippet across multiple Bash tool calls (shell variables don't persist across Bash tool invocations, so `${UPSTREAM}` went empty in the second call and the marker fell through to the empty-tree hash); the script eliminates that failure mode by making the whole computation one bash invocation regardless of how the LLM partitions its calls.
- **REVIEW_META field names.** Codereview writes a JSON footer; refresh detection (Step 2) and `/pr merge` grep for specific field names. Renaming a field silently breaks the readers.
- **Built-in review (Step 5.6).** `/codereview` launches `claude -p "/code-review high"` in the background right after Step 3, with output redirected to a `mktemp` file, and reads that file only at Step 5.6 after the harness completion notice. Reading earlier would let the built-in's findings anchor the inline review, which is the independence the second finder exists to add. It is fail-open, skipped for light review, never re-run in Step 7, and its findings are tagged `(claude-code)` (or `(also claude-code)` when both finders reported the issue) and classified in Step 6, which drops style and review-output-file findings. Lint pins the Step 3 launch pointer, the invocation with its redirect, the mktemp path, the Step 5.6 no-poll and no-re-run statements, the Step 6 classification, the light-review skip, and the Step 9 template line. Adopted on the escaped-defect comparison recorded in README (10 of 12 bugs against 9).
- **Skill effort levels.** Frontmatter `effort:` overrides the session level in both directions while a skill runs, so it is set only on skills whose difficulty is fixed: `/security` max, `/codereview` xhigh, `/codefix` high, `/pr` medium. `/spec`, `/architect`, and `/tester` must not set it, so they inherit the level the user chose for the session. README describes each level; lint pins the frontmatter and the README text together, and pins the install script to seeding `effortLevel` only when unset (`//=`), so a re-run never overwrites a level chosen with `/effort`.
- **Uncommitted prior entry guard.** `/codereview` (reading taken in Step 1, before Step 6.5 can write a preliminary entry, and applied in Step 9), `/security` Step 5, and `/tester` Step 5 condense the prior entry only when `git status --porcelain -- <file>` is empty; otherwise they carry it forward verbatim under `## Prior review carried forward (uncommitted; not current findings)`. The carried block must drop its META footer, because the META readers grep file-wide and a second footer would yield two values (two `reviewed_up_to` SHAs break the Step 2 refresh diff; two `block` counts confuse the `/pr merge` gate). SPEC.md is deliberately excluded: uncommitted SPEC.md is the normal state at turn boundaries, and a carried-forward prior spec with unchecked criteria would be misread as the current contract.
- **Skip marker path.** `bin/codereview-skip` writes the skip marker at `codereview-marker skip-path` and the pre-push hook reads it from the same source. Single-sourcing the path replaces the older convention of keeping two inline path templates byte-identical by hand; lint asserts neither file constructs the path inline.
- **Builder/verifier tool boundary.** Codereview has `Bash(*)` but no Edit/Write (verifier-only); codefix has Edit but no Write (modifies existing code, does not create files). The "Never fix code yourself" instruction (codereview, in Prompt Design Principles) and the explicit do-not-modify list (codefix) are the only guards against `sed -i` or shell-redirect bypass. Writer skills (`/spec`, `/tester`, `/security`) carry Write/Edit because they create and update working documents — `/tester` in particular had Write/Edit added when `/tester design` landed so it could write the durable contract section of TESTING.md. All BACKLOG.md mutations by `/spec` and `/tester` still flow through `spec-backlog-apply.sh` (see the Sweep manifest bullet); Write/Edit on BACKLOG.md from inside a skill is a regression.
- **Subagent completion delivery.** Codereview forks `/security` (Step 5) and `/codefix` (Step 7) via the Skill tool; completion is delivered by the harness, either as the Skill call's direct result or as a task notification when the fork runs as a background task. Step 7 states this, forbids improvised polling ("do not poll for completion": no `until`/`while` + `sleep` loops, no `pgrep`, no watching task-output files), and extends the rule to the Step 5 `/security` invocation. The guard exists because a downstream OrgSmith session, left without a stated wait mechanism, improvised `until ! pgrep -f "codefix" ...; do sleep 10; done` as a background shell: the `pgrep -f` matched the loop's own command line so the exit condition could never fire, the sentinel file it watched used an invented task id, and there was no timeout, so the loop ran orphaned for 20+ hours across sessions before being killed manually. Polling is also redundant even when correct: the harness always pushes the completion signal, so there is nothing to check in the interim. `tests/lint-skills.sh` pins the no-poll phrase, the task-notification mechanism naming, and the Step 5 extension clause so the guard cannot silently drift out of the prompt.
- **BACKLOG.md prompt contracts.** Three intra-spec producer/consumer handoffs with no runtime gate: `### Backlog Sweep` (produced in Step 3c.5, consumed in Step 3g, summarized in Step 5); `### Revisit candidates` (produced in Step 3d, consumed in Step 3g); and the four-field entry template (`One-line description`, `Why deferred`, `Revisit criteria`, `Origin`) duplicated between Step 3c and the BACKLOG.md Format section. A subsection rename or field rename in one site silently misses entries in the other.
- **Sweep manifest stdin interface.** Spec Step 3g and `/tester design` Step D.6 pipe manifests to `bin/spec-backlog-apply.sh` via heredoc; the script parses `delete:`, `adopt:`, `purge-origin:`, and `append:` / `end-append` ops and mutates BACKLOG.md deterministically. All BACKLOG.md mutations flow through the script; no Write/Edit on BACKLOG.md from inside the skills. This split exists precisely so LLM non-compliance on state-mutation edits cannot silently rot BACKLOG.md: the skill decides; the script executes. Op prefixes, the `end-append` delimiter, and the annotation regex (`(ACTIVE in spec YYYY-MM-DD)`) must stay identical between skill and script. `purge-origin`'s carve-out for ACTIVE-annotated entries lives in the script's awk and is what lets `/tester design` dedup prior rollout entries without destroying adopted work. `append:`'s same-name collision check (with or without annotation) is the gate that stops `/tester design` from producing duplicate headings on revision.
- **External-only review pre-flight contract.** `/codereview external` Step E.1 invokes `review-external.sh --check`; the script's `--check` mode reads the same `~/.config/claude-reviewers/.env` and applies the same `HAS_OPENAI`/`HAS_GOOGLE`/`HAS_LOCAL` derivation as its default stdin-reading mode, so a config edit affects both paths identically. The default no-flag invocation must remain fail-open (silent exit 0) so the full `/codereview` Step 5.5 stays fail-open when reviewers are unconfigured; only `--check` flips to fail-loud (exit 1). The External-Only Mode body must NOT contain `codereview-marker write` (the push marker is gating-review-only) or invoke `/codefix` (external mode is analysis-only), and the user-visible footer must explicitly disclaim CODEREVIEW.md / marker / codefix mutation so the user does not confuse external mode with the gate semantics of full review. Step E.4 must invoke `review-external.sh --range "<range>"` so the `=== COMMITS ===` context block prepended to the user message matches the user-supplied range; without it the script falls back to `@{upstream}..HEAD`, which mismatches the diff bytes whenever the user's range differs from the branch's upstream. `/codereview` Step 5.5 (full review) does NOT pass `--range`; its diff is scoped to `codereview-marker base` (`@{upstream}` on a branch with an upstream, the empty tree on a first push), which the script's `@{upstream}..HEAD` `COMMIT_SUMMARY` fallback matches closely enough that no `--range` is needed (on a first push the fallback yields an empty `COMMIT_SUMMARY`, which is harmless). The script's `--range`-vs-fallback split is exactly what lets a single binary serve both call sites without coupling them. `tests/lint-skills.sh` enforces argument-hint advertising the mode, the Step 0 dispatch branches, the Step E.1 `--check` invocation, the Step E.4 `--range` invocation, the no-marker / no-codefix invariants in the External-Only Mode body, and the disclaimer prose. The `--check` and silent-exit-0 fallback are both lint-asserted in `bin/review-external.sh`, along with the `--range` flag parsing, the `git log --oneline "${RANGE}"` use, and the `${UPSTREAM}..HEAD` fallback. The script also rejects a `--range` beginning with `-` (lint-asserted): git parses such a value as an option rather than a revision, and `git log --oneline --output=<path>` truncates and overwrites that path, so an unvalidated leading dash is an arbitrary-file-write primitive. Step E.2's instruction to validate refs with `git rev-parse` is LLM-executed and is not an enforcement boundary, so the guard has to live in the script. Provider `call_*` functions are structurally pinned to `return` (not `exit`) so a single provider failure cannot abort the parallel run.
- **Tester/spec cross-skill contracts.** Four contact points where `/tester` prose must agree with `/spec` prose (or with itself). (1) The BACKLOG.md four-field entry template is duplicated in `/spec` (Step 3c and BACKLOG.md Format section) and `/tester` (Design Mode Step D.5); a field rename in any one site breaks consumers. (2) `tester design YYYY-MM-DD` is a canonical Origin form listed in `/spec`'s BACKLOG.md Format and emitted by `/tester design`; drift on either side means tester entries look ad-hoc to `/spec`'s sweep and overlap scans. (3) `# Durable test-architecture contract` is the exact H1 heading `/tester` Design Mode Step D.6 step 1 writes (Step D.4 only drafts in memory; the actual write moved to D.6 to make D.5.5 a true pre-mutation gate) and `/tester` audit Step 5 preserves; a rename in one site means audit mode stops preserving the contract. (4) `/tester design` Step D.5.5 posts a fixed five-component pre-apply checklist (signals fingerprint, contract shape + line count, rollout count + justification, per-entry overlap scan, conditional SPEC tension) to the user before any TESTING.md or BACKLOG.md mutation in Step D.6; the checklist is always-on, the SPEC tension component is flag-not-block, and `tests/lint-skills.sh` enforces all five component names, both guards, the D.4-drafts/D.6-writes ordering (D.4 has no imperative-write phrasing; D.6 step 1 owns the H1-replace revision-behavior text), and three model-compliance anchors added after a daydream `/tester design` run skipped the checklist entirely and collapsed it into the D.7 post-mutation report: the literal `## Pre-apply checklist` H2 heading template (gives the model a copy-fillable scaffold and gives the user a grep-able anchor in transcripts), the `Do not invoke Edit, Write, or Bash` hard-gate phrase that explicitly forbids tool calls before the checklist is emitted, and the `as a text message before any further tool call` framing that distinguishes "emit a standalone visible message" from "include this content somewhere in your eventual response." This is the user's only course-correct surface for proportionality and overlap calls; silent drift on a component name or on the always-on/flag-not-block guards or on the ordering or on the visible-heading/hard-gate/text-message anchors shrinks that surface.
- **External reviewer output handling.** `review-external.sh` captures each provider's stdout and stderr to separate files, and the demux promotes only a line on a provider's own stdout carrying that provider's own `(tag)`; everything else goes to stderr. The findings stream becomes CODEREVIEW.md and then `/codefix` input, so neither a status line nor a line claiming another provider's tag may pose as a finding. Token counts from provider responses are checked as integers before they reach `bc`, and key-shaped text (`sk-...`, `AIza...`) is redacted from provider error messages before they reach the cost log. Model defaults sit at the top of the script with links to the providers' model pages, and the price tables are `case` blocks in the cost section of `call_openai` and `call_google`; check both when a provider retires a model. Lint pins the separate capture for all three providers, the own-tag regex, and the per-provider demux calls; `tests/test-review-external.sh` covers the behavior with a fake curl.
- **Reviewer credential file mode.** `zat.env-install.sh` chmods `~/.config/claude-reviewers/` to 700 and its `.env` to 600 unconditionally on every run, not only at creation, so a file written by an earlier install under a looser umask is repaired. That file is the documented home for `OPENAI_API_KEY` and `GEMINI_API_KEY`; `mkdir`/`cat` honour the process umask, which yields 0755/0644 on a default macOS or Ubuntu account. Both chmods are lint-asserted. Relatedly, the settings.json permissions block resets `allow` wholesale (allow entries widen what runs unprompted, so resetting is the safe direction) but unions `deny` with whatever is already there (deny entries only narrow, and silently dropping a hand-added rule was the defect); the union is lint-asserted.
When editing any skill, hook, or bin script, run `tests/run-all.sh` afterward. If you add a new contract point (new marker, new cross-skill field, new shared path), add a corresponding lint check.
**Upstream fix pattern** — when an issue is discovered while working on a downstream project (skill produces wrong output, convention is missing, prompt needs adjustment), the fix is made in this repo, never patched locally in a downstream project. Changes to skills, hooks, and global-claude.md affect every project on the machine, so always confirm the intended change with the user before editing. Test the fix by re-running the skill or checking global-claude.md in a downstream project session. The goal is that improvements compound: every fix benefits all future projects.
**Model baseline** — skills and conventions were last reviewed against Anthropic's guidance on 2026-10-01, for Opus 5.5 (the daily default, run at xhigh) and Fable 5.1 (hard design work, high to max). When a new model generation ships, repeat that review against the new model's prompting and effort guidance. Start with `/doctor prompt-audit ~/src/zat.env/claude` (Claude Code 2.1.283 or later), which flags instructions written for older models and proposes edits without applying them. Then check skill `effort` frontmatter, the install script's effort seed, and any prompt text that compensates for a model limit (self-verification steps, confidence thresholds, emphatic phrasing). Each such piece encodes an assumption about what the model cannot do on its own; keep it only if the assumption still holds.
## What NOT to put here
- Project-specific code, configs, or dependencies — those belong in each project's own repo
- Secrets or credentials of any kind
- Large files (models, datasets) — those go in `~/data/`