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

# 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

# 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.*
.py-coverage.lock
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/, glovebox-monitor/ or glovebox-driver/ 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
/glovebox-driver/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

# 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 — every artifact scripts/build-hook-bundles.mjs or
# scripts/build-sbx-dispatcher.mjs writes, wherever its home. Built once per tree by
# `pnpm install`'s postinstall, by setup.bash at install time, in the nix buildPhase and
# in the image's own bundler stage, so the reviewable surface is the sources plus
# pnpm-lock.yaml. Suffix, not a directory or a member list: a bundle whose home the
# pattern misses leaves every tree that builds it permanently dirty.
*.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/
# TLC's own scratch metadir, written here by a run given no -metadir.
/docs/tla/states/

# 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-*
