# Python
__pycache__/
*.py[cod]
*$py.class
*.so
.Python
build/
develop-eggs/
dist/
downloads/
eggs/
.eggs/
lib/
lib64/
parts/
sdist/
var/
wheels/
*.egg-info/
.installed.cfg
*.egg

# Virtual environments
.venv/
venv/
ENV/

# IDE
.idea/
.vscode/
*.swp
*.swo
*~

# Testing
.coverage
htmlcov/
.pytest_cache/

# Ruff
.ruff_cache/

# PyInstaller
*.manifest
*.spec.warn

# OS
.DS_Store
Thumbs.db

# gzkit
.gzkit.yaml.local
.claude/hooks/.instruction-state.json
.claude/settings.local.json
.claude/worktrees/
.gzkit/cache/
# Locks are ephemeral runtime state and never committed. Exchange records are
# the opposite: they are the audit evidence a token surrender cites, and
# `validate_lock_exchange_coupling` fails closed on any cited path that is not
# in git's index (GHI #759). The `/*` form (not `/`) is load-bearing — git does
# not descend into an excluded DIRECTORY, so the negation below would be inert
# against `.gzkit/locks/` (GHI #763).
.gzkit/locks/*
!.gzkit/locks/exchange/
.antigravitycli/
artifacts/

# MkDocs
site/

# gzkit local sensor telemetry (ADR-0.0.70 — runtime-state, never committed)
.gzkit/sensors/

# Composer candidate intermediates (regenerable; committed rendition is the artifact)
.gzkit/renditions/**/*.candidate.md

# Test-health profiler runtime output (regenerated each run; CHORE-LOG.md is the tracked evidence)
.gzkit/chores/*/proofs/health-report.json

# Miner run telemetry (GHI #614 — per-run counts; runtime_state, never committed).
# Lives under proofs/ rather than .gzkit/sensors/ because ADR-0.0.70 Boundary
# Invariant 2 fences the miner to write only to its proofs directory.
.gzkit/chores/*/proofs/run-log.jsonl

# Ontology last-sweep diff-baseline cache (ADR-0.32.0 — Tier-B derived, regenerable, never authority)
.gzkit/ontology/last_sweep.json

# Closeout ceremony turn-lock (transient per-turn step-lock; the sibling
# .ceremony.json is the tracked ceremony state, the .turn-lock is not)
.gzkit/ceremonies/*.turn-lock

# Office owner/lock files (`~$name.pptx`, `~$doc.docx`). PowerPoint and Word write
# these beside an open document and embed the LOCAL USERNAME in the first bytes
# (a length prefix then the name). One was committed and carried the operator's
# real name through history until the 2026-08-20 PII sweep, where it survived
# --replace-text (a 165-byte binary) and had to be purged as a path. Never tracked.
~$*

# Corpus store write-discipline sidecars (GHI #875/#880/#881). `append_entry` takes an
# OS lock on `<surface>.jsonl.lock` and stages the new content beside the target before
# one atomic `os.replace`. Both live in `.gzkit/corpus/`, which IS tracked — the corpus
# is Layer-1 canon — so they must be ignored by name or every corpus write dirties the
# tree. The `.jsonl` globs in corpus_retirement_witness.py and bullet_retention.py do
# not see them; git would. Staging files are unlinked on failure, but survive SIGKILL.
.gzkit/corpus/**/*.lock
.gzkit/corpus/**/*.tmp
# The `**` is load-bearing: a rule-surface corpus nests under its own path
# (.gzkit/corpus/.gzkit/rules/<name>.md.jsonl), which a single-level glob misses.

# Ledger transaction-boundary sidecar (GHI #953). `Ledger.append` serializes the whole
# transaction — fragment recovery, the length probe, the write and the rollback — under an
# OS lock on `.gzkit/ledger.jsonl.lock`, because the rollback truncates to a length another
# writer had already grown past. `.gzkit/ledger.jsonl` IS tracked (it is Layer 2, the
# system-of-record), so the sidecar must be ignored by name or every governance event
# dirties the tree. The kernel releases the lock when the handle closes, abnormal exit
# included, so a stale sidecar is an empty file and never a held lock.
.gzkit/ledger.jsonl.lock

# Report publication uses an OS-lock sidecar; retained reports remain tracked.
**/reports/big-picture/publication.lock

# Section-ownership write-discipline sidecars (OBPI-0.35.0-04). Same shape as the corpus
# store above: `gz content unown` takes an OS lock on `<surface>.json.lock` and stages the
# new declaration beside the target before one atomic `os.replace`. `.gzkit/ownership/` IS
# tracked — the declaration is the coverage claim gating the unowned-byte ratchet — so the
# sidecars must be ignored by name or every un-owning dirties the tree. The `.journal` is
# the crash-recovery record for the declaration/ledger two-store transaction: it is written
# before either store is touched and cleared after both land, so it exists only mid-flight
# and must never be committed — a committed journal would make every clone replay a
# transition that already completed here.
.gzkit/ownership/**/*.lock
.gzkit/ownership/**/*.tmp
.gzkit/ownership/**/*.journal
# The `.journal.source` is the MEASURED SOURCE BYTES retained beside that journal for its
# lifetime (OBPI-0.35.0-04, Step-4b round-10 finding 2): recovery needs the exact bytes the
# interrupted run measured, and a digest names them without being able to supply them. It
# needs its own rule — the `*.journal` glob above requires the name to END in `.journal`,
# so it does not match `<surface>.json.journal.source` at all.
.gzkit/ownership/**/*.journal.source

# Landing transaction sidecars (OBPI-0.35.0-07). `gz content land` takes an OS lock on
# `.landing.lock` in the surface's rendition directory, writes its journal `.landing.json`
# before the first artifact byte, and stages new bytes under `.landing-staging-<id>/`.
# `.gzkit/renditions/` IS tracked, so these are ignored by name. The journal and staging
# exist only while a landing is in flight or interrupted; committing them would make every
# clone see an in-flight landing that already finished or was abandoned here.
.gzkit/renditions/**/.landing.lock
.gzkit/renditions/**/.landing.json
.gzkit/renditions/**/.landing-staging-*/
# The state-E extract is the one artifact written OUTSIDE `.gzkit/`. A refusal copies the
# retained bytes beside the surface so the operator can diff and restore from them, which for
# the real declaration means `AGENTS.md.unowning-recovery` — a full copy of Layer-1 canon in
# the tracked repository root. `AGENTS.md` § Execution Rules mandates `git add -A` BEFORE
# `gz check`, so an unignored copy is STAGED rather than merely noticed.
#
# NOT root-anchored, and the earlier `/`-anchored form was a measured defect. The extract path
# is derived per-surface — `surface_path.with_name(name + ".unowning-recovery")`, `_target_for`
# in `src/gzkit/commands/content/unown.py` — so it lands wherever the surface lives, and a
# surface is NOT always at the root: `.gzkit/corpus/.gzkit/rules/governance-core.md.jsonl` is a
# live corpus store today, so `.gzkit/rules/governance-core.md` is a content surface whose
# state-E refusal would write `.gzkit/rules/governance-core.md.unowning-recovery` into a
# tracked directory the root-anchored rule cannot see (Step-4b Task-1 quality review).
*.unowning-recovery
# The atomic writer stages into the TARGET's directory as `.<name>.<random>.tmp`
# (`write_bytes_atomically`, `src/gzkit/content/ownership.py`), so a crash mid-extraction leaves
# a staging file holding the MEASURED SOURCE BYTES behind. The rule above cannot match it: that
# glob requires the name to BEGIN with the surface name, and a staging file begins with a dot.
# Measured at Step-4b round 11: `EXTRACT_CRASH exit 99 residue
# ['/review/.doc.md.unowning-recovery.3.tmp']`, `RESIDUE_IS_MEASURED_SOURCE True`, and a
# successful recovery retained it. Cover the whole extraction-file family, final and temporary,
# at every depth (operator ruling 2026-09-05 point 4).
.*.unowning-recovery.*.tmp

# Local settings backups written by scripts/settings_local_backup.py.
# The backed-up file (.claude/settings.local.json) is itself ignored, so its
# snapshots must never be tracked either.
settings.local/

# Codex runtime scratch. Codex writes `.codex/tmp/arg0/*` (a .lock and symlinks to
# the host's absolute `codex` binary) when CODEX_HOME points at the repo. Committed,
# those symlinks broke `uv build` and kept v0.34.8 off PyPI; see
# tests/governance/test_tracked_symlinks.py.
.codex/tmp/

# Session runtime state, never repo content: Claude Code's scheduled-task lock
# (a sessionId/pid record) and the scratch directory agents write under .gzkit/tmp/
# (e.g. a ghi-triage rank input). Both were committed by sweeping syncs.
.claude/scheduled_tasks.lock
.gzkit/tmp/
