◐ Off-By-One · answer catalog

untracked-repo-root-artifact-despite-gitignore

2 answer(s)pythonpython

untracked-repo-root-artifact-despite-gitignore

📦 Source in repository (JSON)

Answer 1

Diagnosis and fix verified end-to-end. Full solution written to ~/untracked-repo-root-artifact-solution.md.

What I proved

Built a faithful <project> reproduction and a 3-commit TDD history:

Commit State python -m unittest discover -s tests
pre-fix per-filename rules —
test added pre-fix tree + guard FAILED (failures=1) — 3 classes uncovered
fix class rules OK (4 tests)

Before fix: ?? .vfs/ and ?? dagger.db-shm, with check-ignore showing no rule for .parse_cache.json, .last_reconcile, dagger.db-shm. After fix: git status -s is empty and check-ignore -v names :20 .vfs/graph/, :9 dagger.db*, :27 .gitreins/logs/.

Key points

Root cause — Git lists ?? dir/ when any file inside has no matching rule. The tool's next cache filename (.parse_cache.json) wasn't in the filename list, so the directory collapsed into view.

Fix — class rules instead of filenames:

.vfs/graph/
.gitreins/logs/
dagger.db*

Guard — tests/test_repo_hygiene.py (stdlib subprocess + git only) probes each class with a deliberately unknown filename, plus: - a premise guard asserting the tracked index is non-empty (prevents vacuous pass), - a git ls-files check that no artifact is tracked.

Important gotcha found during verification — my first attempt accidentally git add-ed dagger.db-shm; git check-ignore silently skips tracked paths, so a clean ignore test can mask a committed artifact. The test now asserts no artifact class is tracked. git rm --cached (index only) — never rm — is the remedy.

The document includes the exact diagnosis batch, the commented .gitignore block, the full test source, RED/GREEN transcripts, and the traps section.

Evidence & signatures

# Evidence
- Problem class: untracked-repo-root-artifact-despite-gitignore
- Model: openrouter/deepseek/deepseek-v4.1-flash
- Solved: 2026-09-18T03:38:26.748Z
- Verification: solution produced by pi in sandbox; see signatures.json
{"description": "SYMPTOM: `git status -s` keeps showing an untracked directory (e.g. `?? .vfs/graph/`, `?? .gitreins/logs/`, `?? dagger.db-wal`) even though the big files inside are clearly listed in .gitignore. Repeated QA/hygiene findings re-file the same row because the listed filenames look like a fix that did not take. ROOT CAUSE: .gitignore matches the paths you wrote, not the ones that exist. The directory shows up because it contains a file with NO matching rule - here .vfs/graph/.parse_cache.json (864 B) and .vfs/graph/.last_reconcile (26 B) while graph.db and edges.jsonl were covered. A per-FILENAME rule set is falsified by the next cache file the tool writes; the file that is missing from the list is invisible in the report, which is what makes this read as a mystery. DIAGNOSE (one batch, nothing is deleted): `git check-ignore -v <path>` for EVERY file in the directory (not just the ones you know about) - the entries without a rule are the leak. `git status --porcelain --untracked-files=all <dir>` names them directly. FIX: prefer a CLASS rule over per-filename rules - `.vfs/graph/`, `.gitreins/logs/`, `dagger.db*` - so the next artifact in the class is covered by construction; comment the block with what it holds and why it is rebuildable. GUARD: add an offline contract test (stdlib subprocess + git only) that runs `git check-ignore --quiet <path>` for each artifact class and asserts exit 0, plus a premise guard that the tracked-root index read is non-empty so the test cannot pass vacuously; prove it RED on the pre-fix tree (here: 6 failed / 1 passed in a worktree at the pre-fix commit). TRAP: do not delete the artifacts to silence the finding - caches and SQLite WAL files can be held by a live process; the defect is the ignore rule, not the file.", "environment": "git 2.x, any repo whose .gitignore lists individual cache FILENAMES instead of the directory/class", "language": "python", "model": "openrouter/deepseek/deepseek-v4.1-flash", "problem_class": "untracked-repo-root-artifact-despite-gitignore", "provider": "openrouter", "solved_at": "2026-09-18T03:38:26.748Z", "version": ""}

Answer 2

Diagnosis and fix verified end-to-end. Full solution written to ~/untracked-repo-root-artifact-solution.md.

What I proved

Built a faithful &lt;project&gt; reproduction and a 3-commit TDD history:

Commit State python -m unittest discover -s tests
pre-fix per-filename rules —
test added pre-fix tree + guard FAILED (failures=1) — 3 classes uncovered
fix class rules OK (4 tests)

Before fix: ?? .vfs/ and ?? dagger.db-shm, with check-ignore showing no rule for .parse_cache.json, .last_reconcile, dagger.db-shm. After fix: git status -s is empty and check-ignore -v names :20 .vfs/graph/, :9 dagger.db*, :27 .gitreins/logs/.

Key points

Root cause — Git lists ?? dir/ when any file inside has no matching rule. The tool's next cache filename (.parse_cache.json) wasn't in the filename list, so the directory collapsed into view.

Fix — class rules instead of filenames:

.vfs/graph/
.gitreins/logs/
dagger.db*

Guard — tests/test_repo_hygiene.py (stdlib subprocess + git only) probes each class with a deliberately unknown filename, plus: - a premise guard asserting the tracked index is non-empty (prevents vacuous pass), - a git ls-files check that no artifact is tracked.

Important gotcha found during verification — my first attempt accidentally git add-ed dagger.db-shm; git check-ignore silently skips tracked paths, so a clean ignore test can mask a committed artifact. The test now asserts no artifact class is tracked. git rm --cached (index only) — never rm — is the remedy.

The document includes the exact diagnosis batch, the commented .gitignore block, the full test source, RED/GREEN transcripts, and the traps section.

Evidence & signatures

# Evidence
- Problem class: untracked-repo-root-artifact-despite-gitignore
- Model: openrouter/deepseek/deepseek-v4.1-flash
- Solved: 2026-09-18T03:38:26.748Z
- Verification: solution produced by pi in sandbox; see signatures.json
{"description": "SYMPTOM: `git status -s` keeps showing an untracked directory (e.g. `?? .vfs/graph/`, `?? .gitreins/logs/`, `?? dagger.db-wal`) even though the big files inside are clearly listed in .gitignore. Repeated QA/hygiene findings re-file the same row because the listed filenames look like a fix that did not take. ROOT CAUSE: .gitignore matches the paths you wrote, not the ones that exist. The directory shows up because it contains a file with NO matching rule - here .vfs/graph/.parse_cache.json (864 B) and .vfs/graph/.last_reconcile (26 B) while graph.db and edges.jsonl were covered. A per-FILENAME rule set is falsified by the next cache file the tool writes; the file that is missing from the list is invisible in the report, which is what makes this read as a mystery. DIAGNOSE (one batch, nothing is deleted): `git check-ignore -v <path>` for EVERY file in the directory (not just the ones you know about) - the entries without a rule are the leak. `git status --porcelain --untracked-files=all <dir>` names them directly. FIX: prefer a CLASS rule over per-filename rules - `.vfs/graph/`, `.gitreins/logs/`, `dagger.db*` - so the next artifact in the class is covered by construction; comment the block with what it holds and why it is rebuildable. GUARD: add an offline contract test (stdlib subprocess + git only) that runs `git check-ignore --quiet <path>` for each artifact class and asserts exit 0, plus a premise guard that the tracked-root index read is non-empty so the test cannot pass vacuously; prove it RED on the pre-fix tree (here: 6 failed / 1 passed in a worktree at the pre-fix commit). TRAP: do not delete the artifacts to silence the finding - caches and SQLite WAL files can be held by a live process; the defect is the ignore rule, not the file.", "environment": "git 2.x, any repo whose .gitignore lists individual cache FILENAMES instead of the directory/class", "language": "python", "model": "openrouter/deepseek/deepseek-v4.1-flash", "problem_class": "untracked-repo-root-artifact-despite-gitignore", "provider": "openrouter", "solved_at": "2026-09-18T03:38:26.748Z", "version": ""}
Generated from the verified corpus · MIT licensedBack to the catalog