.loom/daemon-state.json
.loom/[0-9][0-9]-daemon-state.json
.loom/stuck-history.json
.loom/alerts.json
.loom/health-metrics.json
.loom/progress/

# Build artifacts
# Compiled native extension (rebuilt locally via `uv run kct build-native`)
*.so
# CMake tree left at repo root by scripts/build-cpp.sh (`kct build-native`)
/build/
# sdist/wheel output from `uv build` (RELEASING.md)
/dist/

# macOS Finder droppings
.DS_Store

# Python
# uv writes .venv/.gitignore itself, but that file is not tracked — deleting or
# recreating the venv with a tool that doesn't write one (python -m venv) would
# stage ~35k files. Same defense as htmlcov/ below.
.venv/
venv/
__pycache__/
*.py[cod]
.pytest_cache/
.mypy_cache/
.ruff_cache/
.coverage
.coverage.*
# coverage.py writes its own htmlcov/.gitignore, so the tree is ignored only as
# long as that generated file survives. Ignore it here too, or a `rm
# htmlcov/.gitignore` (or a --cov-report=html run that predates it) stages
# thousands of report files.
htmlcov/
*.egg-info/

# Test byproducts
# KiCad regenerates project-local state (.kicad_prl) when tests open the fixture
tests/fixtures/projects/*.kicad_prl
tests/fixtures/*.kicad_prl

# Board render artifacts (generated by `kct render`, Epic #3674)
boards/*/output/renders/
boards/*/*/output/renders/

# Board metrics (generated at runtime by `kct board-metrics`, Epic #3674)
boards/*/output/board.json
boards/*/*/output/board.json

# Board-generation log (removed by `make clean`, never committed)
boards/*/design_output.log
boards/*/*/design_output.log

# Silkscreen marking sidecars (written by `kct build`'s silkscreen step)
# NOTE: this is marking-identity state, not a cache -- the uuid->tag map
# cannot be recomputed from the PCB, because a gr_text node carries no tag
# of its own. If a board recipe ever commits a PCB carrying kct: markings,
# that board's sidecar MUST be force-added (`git add -f`) alongside it, or
# re-running `kct build` will append duplicate gr_text markings. No board
# does this today -- no boards/*/generate_design.py runs the silkscreen
# step (#4565).
boards/*/output/*.kct.json
boards/*/*/output/*.kct.json

# Local credentials (BYO API keys -- issue #4118). NEVER commit real keys.
.env
.env.*
!.env.example

# Repo Skills machine-local install metadata (absolute source path + timestamp)
.claude/skills/repo/.install-local.json

# Repo Skills runtime artifacts (transient by design -- never commit)
# handoff.md is a one-shot session baton written by /repo:handoff and deleted
# after the next session absorbs it; logs/ holds hook-errors.log and the
# opt-in guard-decisions.log telemetry.
.claude/handoff.md
.claude/skills/repo/logs/

# Loom model-cost experiment (canary A/B, #3725/#3731)
# The CANARY confirmation MUST stay uncommitted by design -- a copied/committed
# config can never confirm canary status. Local outcome-chain stats too.
# >>> loom-managed (do not edit) >>>
# Loom runtime state (don't commit these)
.loom-in-use
.loom-checkpoint
.loom-managed
.no-changes-needed
.snapshots/
.loom/.daemon.pid
.loom/.daemon.log
.loom/daemon.sock
.loom/daemon-loop.pid
.loom/daemon-metrics.json
.loom/loom-source-path
.loom/spawn-loop-state.json
.loom/spawn-loop.pid
.loom/stop-spawn-loop
.loom/issue-failures.json
.loom/interventions/
.loom/worktrees/
.loom/worktrees-local/
.claude/worktrees/
.loom/state.json
.loom/mcp-command.json
.loom/activity.db
.loom/claims/
.loom/locks/
.loom/signals/
.loom/status/
.loom/retry-state/
.loom/exit-codes/
.loom/sweep-checkpoint/
.loom/sweep-run/
.loom/stats/
.loom/diagnostics/
.loom/guide-docs-state.json
.loom/metrics_state.json
.loom/manifest.json
.loom/stuck-config.json
.loom/metrics/
.loom/usage-cache.json
.loom/claude-config/
.loom/tokens/
.loom/accounts.env
.loom/account-health.json
.loom/account-health.lock
.loom/CANARY
.loom/*.log
.loom/*.sock
.loom/*.tmp
.loom/*.bak
.loom/.install.lock
.loom/logs/
.loom/.resync-in-progress
.loom/gh-config/
.loom/gh-config-by-owner/
# <<< loom-managed <<<

# pytest-benchmark storage
.benchmarks/

# Local routing-benchmark artifact rewritten by tests/test_router_integration.py
# on every run (untracked in #4684 — timing noise, no reader)
tests/benchmark_results.json

# Corpus tooling output (scripts/corpus/, #4830). Holds taxonomy reports,
# manifest scorecards, and third-party CC-BY-4.0 KiCad payloads fetched from
# Hugging Face (probe --keep-sources, and check_manifest.py's payloads/ cache).
# NEVER commit these — the corpus stays referenced by URL + sha256 in
# scripts/corpus/manifests/, not vendored.
scripts/corpus/.cache/

# External DeepPCB-comparison benchmark boards (benchmarks/external/,
# #4932/#4933). Fetched-at-runtime third-party board files -- NEVER commit
# these, they stay referenced by pinned commit SHA in
# benchmarks/external/boards.toml instead.
.cache/kct-benchmarks/

# Disposable synthetic recipe runs; reviewed witnesses live in regression-fixture.
boards/*/regression-output/

# KiCad editor/session state is not part of a board release.
boards/**/*.kicad_prl

# Node (root package.json has npm scripts; site/ has its own .gitignore)
node_modules/
.squad/
.claude/skills/squad/.install-local.json
.mcp.json
