__pycache__/
*.py[cod]
*$py.class

# `.venv` (no trailing slash) so a worktree's symlink-to-main-venv is
# also ignored — `.venv/` only matches directories.
.venv
.venv/
venv/
.env

# Never commit private keys / certs / keystores — a safety net so a stray
# credential file can't be added by accident. (security review §I8)
*.pem
*.key
*.p12
*.pfx

build/
dist/
wheelhouse/
*.egg-info/

.pytest_cache/
.ruff_cache/
.mypy_cache/
.coverage
htmlcov/

docs/_build/

# pymongo-validation pytest-json-report raw output (regenerated each run).
.validation/

.DS_Store
.idea/
.vscode/

# Claude Code's per-worktree harness config (hooks, plugins, settings).
# settings.local.json (and everything else under .claude/) stays per-developer
# and uncommitted. Two things ARE shared and committed, on the same reasoning —
# they encode team knowledge, not personal setup:
#   * settings.json — e.g. the test-run permission allowlist every contributor
#     benefits from;
#   * skills/ — project procedure worth carrying between sessions and people
#     (how to probe against the mongod oracle, how to set up and land a batch).
.claude/*
!.claude/settings.json
!.claude/skills/
!.claude/skills/**

# Cross-driver smoke test scaffolding. The Go module's downloaded
# deps live in $GOPATH (already outside the repo); the Node smoke's
# node_modules is local to the workdir and downloaded on first run.
# package-lock.json IS committed for reproducibility.
tests/cross_driver/node/node_modules/
tests/cross_driver/node/.node_modules.lock

# Gradle wrapper / cache state for the Java cross-driver smokes is
# rebuilt from build.gradle.kts on first run; the uber-jar in build/
# is also excluded by the top-level `build/` rule above. The flock
# sentinel serialises parallel xdist workers around the build.
tests/cross_driver/java/.gradle/
tests/cross_driver/java/.smokesjar.lock

# Ruby bundler + PHP composer cache state for the cross-driver smokes.
# Restored on first run via `bundle install` / `composer install`.
tests/cross_driver/ruby/Gemfile.lock
tests/cross_driver/ruby/.bundle/
tests/cross_driver/ruby/.bundle.lock
tests/cross_driver/ruby/vendor/
tests/cross_driver/php/composer.lock
tests/cross_driver/php/vendor/
tests/cross_driver/php/.vendor.lock

# Rust target dir for the cross-driver smokes (currently gated on
# upstream mongo-rust-driver compatibility — see Cargo.toml). The
# .cargo.lock-build flock sentinel mirrors the Java/Ruby/Node pattern
# for serialising xdist workers around the per-test cargo build.
tests/cross_driver/rust/target/
tests/cross_driver/rust/Cargo.lock
tests/cross_driver/rust/.cargo.lock-build

# Pelican site build artifacts and ephemeral runtime state. The build
# output is regenerated each `invoke build`; theme/static/img/ is copied
# from ../brandkit/ at build time; secantus-data/ is a stray WiredTiger
# data dir that may be created if a SecantusDBServer is started with
# website/ as cwd; aws-state.json contains account-specific resource IDs.
website/output/
website/themes/secantus/static/img/
website/infra/aws-state.json
website/secantus-data/
# ...and the same stray dir at the repo root, from a server started with
# the checkout as cwd (an ad-hoc probe script, typically).
/secantus-data/
website/__pycache__/
xunit-results/

# WT-linked crate build artifacts from a LOCAL `cargo check`/`build` against the
# vendored WiredTiger (SECANTUS_WT_INCLUDE/LIB). The per-crate `target/` is a
# build artifact and stays ignored.
#
# The per-crate **`Cargo.lock` is NOT** ignored any more (2026-09-03). The note
# here used to say these crates are "built only by CI's CMake path", so a stray
# lock should never be committed -- but `test.yml`'s `rust-storage` job runs
# `cargo fmt / clippy / test` in each of these directories directly, which makes
# every one of them its own DEPENDENCY RESOLUTION ROOT. Without a lock they
# re-resolve on every run, so any upstream publish can break CI on a commit that
# changed nothing.
#
# That is not hypothetical: `tinyvec 1.13.0` shipped a lib that does not compile
# (`cannot find macro vec`), and of the six WT-linked roots the job builds, the
# two whose locks were ignored -- this pair -- were exactly the two that broke.
# The four with a tracked lock were immune, `secantusdb` included, which pulls
# tinyvec and pins 1.11.0. Committing these two locks makes the set consistent.
crates/secantus-storage-adapter/target/
crates/secantus-storage/target/
crates/secantus-server-py/target/

# Playwright MCP browser-session artefacts — Claude sessions write them to repo
# root when capturing screenshots / DOM dumps. Always untracked, never shipped.
.playwright-mcp/
docs-rust/_build/
faulthandler-dumps/

# Three-droplet DigitalOcean benchmark (crates/secantus-bench): per-run result
# directories and the harness's own ssh known_hosts / scratch files. Curated
# results can still be force-added; ad-hoc runs stay out of the tree.
bench/.do-state/
bench/results/do/

# Detached long-run state (scripts/detached_run.py)
.detached-runs/
