.worktrees
.claude/worktrees/
.agents/
.codex/

# sbx agent boot trace: a runtime diagnostic the in-VM entrypoint writes into the
# workspace (read back by bin/checks/sbx/egress.bash); never a committed source file.
.gb-agent-boot-trace

# Dependencies
# Tool-managed dirs use the BARE name (no trailing slash). A `foo/` pattern
# matches only a *directory*, so a symlink named `foo` — e.g. one linked into a
# git worktree to run tests — slips past it and `git add -A` stages it (how a
# node_modules symlink twice reached CI this repo's history). The bare name
# matches the dir AND a same-named symlink/file, so tool output can't be
# committed by accident. Only applied to names that are never a legitimately
# tracked file (dependency/venv/cache/report dirs); generic names like dist/ or
# build/ keep the trailing slash so they don't over-ignore a real file.
node_modules
.pnpm-store

# Build outputs
dist/
build/
out/

# Environment files
.env
.env.local
.env.*.local

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

# OS
.DS_Store
Thumbs.db

# Logs
*.log
npm-debug.log*
pnpm-debug.log*

# Coverage
coverage/
coverage.json
.c8-output
.coverage
.coverage.*
htmlcov

# Mutation testing (Stryker): sandbox, HTML report, and the incremental cache
.stryker-tmp
reports/

# Generated LaunchAgent (rendered from the .template by setup.bash at install time)
launchagents/*.generated.plist

# Python
__pycache__
*.pyc
.venv
.uv
.pytest_cache

# `uv run` inside inspect-glovebox/ or glovebox-monitor/ writes a lock beside that
# pyproject.toml. Each wheel is a library, so it pins nothing; committing one adds a
# `uv.lock -merge` path that scripts/resolve-generated.mjs owns no rule for, which
# reds the JS suite.
/inspect-glovebox/uv.lock
/glovebox-monitor/uv.lock
# pytest scratch (repo-relative --basetemp): throwaway fixture files, including
# fake executables the suite writes (keyprobe, docker stubs). Ignored so they never
# surface as uncommitted changes or get captured onto a session-end review branch.
.pytest-tmp

# Fuzzer crash reproducers (jazzer.js / atheris drop these in the repo root, which
# is their cwd). Anchored with a leading slash: an unanchored `crash-*` matches at
# ANY depth, so it silently swallowed bin/checks/sbx/crash-resilience-int.bash — a
# `git add` of a legitimately-named source file that reports success and stages
# nothing.
/crash-*
/timeout-*
/oom-*
/leak-*

# cosmic-ray mutation sessions + HTML reports. mutation-floor.sh writes
# <session>.html at the repo root for the run's artifact, one per tools/mutation/
# toml, so this is a glob: a per-session list leaves the next session's report
# untracked-but-not-ignored, which is what egress-filter and mcpgw-derive were.
*.sqlite
/*.html

# History working files — each lives on the perf-history or ci-timings branch and is
# seeded into the checkout by persist-perf-history.sh read or record-setup-timing.sh;
# never committed to main or a feature branch. A glob, not a list: a tool that walks
# "tracked plus untracked-not-ignored" takes a seeded file for a repository file, and
# a per-metric list leaves the next metric's file out of the pattern. That is what
# widened perf-gates.yaml's generated paths-regex on the runner and nowhere else.
.github/*-history.json
.github/*-history.jsonl

# pytest --basetemp droppings (used where /tmp is noexec)
.pytest-exec-tmp

# sbx agent-entrypoint boot trace — a runtime log appended into the workspace by
# gb_boot_trace; a diagnostic artifact, never committed.
.gb-agent-boot-trace

# Hypothesis property-testing example database (regenerated locally; never committed)
.hypothesis/

# CI timing maps that balance the shard fan-outs. They live in R2 (uploaded only by
# main runs, fetched best-effort by CI — see .github/ci-durations.json) and are never
# committed; ignored so a CI fetch or a local `uv run pytest --store-durations` /
# sbx-live run can't accidentally stage them.
tests/.gb-test-durations.json
tests/.gb-kcov-durations.json
tests/.gb-drvfs-durations.json
tests/.gb-macos-durations.json
# The plan job's published shard assignment, one per leg (keyed by that leg's
# duration-map name): derived from the fetched map, shipped to the shards in the
# same artifact, never committed.
tests/.gb-*-shard-assignment.json
.github/sbx-live/durations.json
.github/kata-live/durations.json

# The pinned scanner binary that .github/scripts/python-deps-vuln-scan.sh downloads
# into the repo root (~56 MB). Running that script locally to reproduce a red
# osv-scanner check is the documented response to one, so the artifact lands in
# every contributor's tree; ignored so it can't be staged by a `git add -A`.
/osv-scanner

# esbuild output of the sbx-kit/image/*.mjs sources. The image builds these in its own
# bundler stage (sbx-kit/image/Dockerfile); the local copies exist so the CT eval harness
# can subprocess the dispatcher without a docker build. Glob, not a member list: an
# unlisted bundle leaves every tree that builds it permanently dirty.
sbx-kit/image/*.bundle.mjs

# esbuild output of the host guardrail hooks (scripts/build-hook-bundles.mjs), which
# settings.json launches. Built once per tree by `pnpm install`'s postinstall, by
# setup.bash at install time, and in the image's own bundler stage — so the reviewable
# surface is the hook sources plus pnpm-lock.yaml, not a 12k-line generated diff.
.claude/hooks/*.bundle.mjs

# Sharded mutation configs are deterministic build output. The planner and
# `pnpm install` render them from config/mutation-sessions.json.
/tools/mutation/*of*.toml

# The pinned kcov binary that tests/install-kcov-local.sh builds into the repo
# root — the same path .github/actions/install-kcov caches to, so tests/run-kcov.sh
# finds a local build with no PATH edit. Reproducing a red "Bash coverage (kcov)"
# check locally is the documented response to one, so the artifact lands in every
# contributor's tree; ignored so it can't be staged by a `git add -A`.
/kcov-bin/
.claude/.hookpin-*/

# synced-deps.test.mjs's untracked-file probe, mkdtemp'd under a synced path on
# purpose. Ignored so a concurrent `git add -A` elsewhere in this checkout can't
# stage it mid-test: staged, it would pass trackedUnder()'s check and leave an
# AD entry in the shared index once the test deletes it from disk.
.claude/consumer-probe-*/

# `ct settings pull` writes this per-checkout pin cache into the repo root. The pin this
# harness measures lives in evals/control_tower/ct-settings-pin.txt; a committed copy here
# would be a second one that drifts.
.settings.local.yml

# `gh` writes its per-machine state here when it runs with the repo root as the
# XDG state home — a device identifier, which no checkout should carry.
/.local/

# Generated doc pages, published to the CDN instead of committed
# (scripts/render-doc-site.py, .github/workflows/docs-publish.yaml). A committed
# page is re-derived by every branch that touches its generator's inputs, which
# made these the busiest files in the history and conflicted branches on bytes
# nobody typed. Their generators still run, and still refuse a bad input; only
# the output stays out of the tree.
/docs/architecture-callgraph.md
/docs/ci-map.generated.md
/docs/tla/configs.md
/docs/tla/reachable-subsets.md
/docs/tla/diagrams/
/docs/tla/modules/

# The eval time series live on the `metrics-history` branch, which `main` never
# merges (.github/scripts/eval-series.sh). A tracker or a local run materializes its
# own working copy here; committing one would put the series back on the merge path.
metrics/monitor-eval.jsonl
metrics/sabotage-eval.jsonl
metrics/breakout-ctf.jsonl

# `nix build` links its output here.
/result
/result-*
