Skip to content

fix(setup): Keep Codemap state locally ignored - #128

Merged
JordanCoin merged 1 commit into
JordanCoin:mainfrom
reneleonhardt:fix/exclude-codemap-dir
Aug 13, 2026
Merged

fix(setup): Keep Codemap state locally ignored#128
JordanCoin merged 1 commit into
JordanCoin:mainfrom
reneleonhardt:fix/exclude-codemap-dir

Conversation

@reneleonhardt

@reneleonhardt reneleonhardt commented Aug 12, 2026

Copy link
Copy Markdown
Contributor

What does this PR do?

Writes .codemap/ to the clone-local .git/info/exclude (shared by linked worktrees, submodule-local) instead of a tracked .gitignore, so setup and config init never touch tracked files. codemap doctor verifies the effective ignore state: OK whenever git ignores .codemap/ by any mechanism, MISS only when it is genuinely unignored, with a minimal repair hint.

Type of change

  • Bug fix
  • New feature
  • New language support
  • Documentation
  • Other (describe below)

Checklist

  • I've tested this locally with go build && ./codemap .
  • I've read CONTRIBUTING.md (for new language support)
  • I've updated documentation if needed

Additional notes

  • Appends to info/exclude only when the entry is missing: idempotent, no duplicate on re-run, never creates a .gitignore.
  • Atomic write: a symlinked exclude is replaced, not written through; CRLF files and files without a trailing newline are preserved byte-for-byte.
  • Repair hint runs codemap setup --no-hooks --no-mcp --no-config so fixing the ignore rule does not rewrite hooks or MCP config.
  • Silent no-op outside a Git working tree.

Developed with carefully directed, manually reviewed AI assistance.

@JordanCoin

Copy link
Copy Markdown
Owner

Reviewed. The write path is genuinely careful and I like the core decision: it writes to .git/info/exclude, never to a tracked .gitignore. Setup is an install-time action, and appending to a committed .gitignore would create an unrequested diff for the whole team. info/exclude is also the lowest-precedence ignore source, so a team that deliberately tracks .codemap/ still wins.

Verified across 15 disposable repos with byte-level before/after: fresh repo with no .gitignore (none created), existing .gitignore with unrelated entries (byte-identical after), already-ignored .codemap/ (not duplicated), two consecutive runs (idempotent), pre-populated exclude (pure append), no trailing newline (no concatenation onto the last line), CRLF file (no corruption), linked worktree (lands in the main repo's exclude via --git-common-dir), submodule (lands in .git/modules/sub/), read-only exclude (warns, exits 0), and GIT_DIR pointing elsewhere (writes nothing, fails safe). That's thorough.

One thing I'd fix before merging.

cmd/doctor.go:115-126 — doctor fails repos where .codemap/ is provably ignored

Reproduced on codemap's own repo:

$ git check-ignore -v .codemap/
.gitignore:12:.codemap/	.codemap/

$ codemap doctor --agent claude .
MISS local Codemap ignore: .../.git/info/exclude does not contain .codemap/;
     repair with `codemap setup ...`
1 check(s) need attention.

The runtime state is ignored — doctor just isn't looking at the mechanism that's doing it. Two consequences: every existing user configured by a prior version gets a brand-new failure and nonzero exit until they re-run setup, and this repo fails its own doctor.

It also contradicts the comment 200 lines further down the same file:

"Reporting it MISS because the project file does not repeat it describes the file layout rather than the effective configuration."

That's the exact bug #100 fixed for hooks and MCP, reappearing as a new check. Consulting gitCheckIgnore first and reporting OK with the effective mechanism — reserving the failure for genuinely unignored repos — keeps the check useful without the false alarm. Or keep the note and don't failures++.

Smaller

  • cmd/gitignore.go:180-188hasIgnoreLine uses bufio.Scanner and ignores scanner.Err(). On an exclude whose first line exceeds 64KB, Scan() returns false with ErrTooLong and .codemap/ reads as absent even when present. Measured: the entry went 1 → 2 → 3 across two setup runs, and doctor then reports MISS permanently. Pre-existing function, but this PR puts it on setup's unconditional path and behind a new doctor failure. bytes.Split or scanner.Buffer(...) plus an Err() check.
  • cmd/gitignore.go:154-176appendIgnoreEntry uses os.WriteFile, which follows symlinks and is non-atomic. With .git/info/exclude symlinked outside the repo, setup wrote through it. The repo already has writeFileAtomic (cmd/setup.go:38) whose comment is about exactly this; os.Rename replaces the symlink instead of writing through it and removes the truncate-then-write window.
  • ignoreTracked (cmd/gitignore.go:19-21) is now dead in production — both call sites moved to ignoreLocal, and only tests reference it, which is why staticcheck stays quiet.
  • The notice prints a repo-escaping relative path in worktrees (Added .codemap/ to ../k/.git/info/exclude); prefer absolute when filepath.Rel starts with ...
  • The repair hint suggests full codemap setup, which rewrites hooks and MCP to fix one ignore line; --no-hooks --no-mcp --no-config is the minimal repair.

Test gaps worth locking in, since all three pass today: CRLF, missing trailing newline in local mode (only tracked mode is covered), and a pre-populated exclude.

@reneleonhardt
reneleonhardt force-pushed the fix/exclude-codemap-dir branch from 3f107fb to 2b09e27 Compare August 13, 2026 06:54
@reneleonhardt
reneleonhardt marked this pull request as draft August 13, 2026 08:01
Write .codemap/ to the clone-local .git/info/exclude instead of a tracked
.gitignore, so setup and config init never touch tracked files. doctor now
verifies the effective ignore state, reporting OK whenever git ignores
.codemap/ by any mechanism and failing only when it is genuinely unignored.
The write is idempotent and a no-op outside a git work tree.

Co-Authored-By: GPT-5.6 Sol <codex@openai.com>
@reneleonhardt
reneleonhardt force-pushed the fix/exclude-codemap-dir branch from 2b09e27 to 9d2ff8f Compare August 13, 2026 08:17
@reneleonhardt

Copy link
Copy Markdown
Contributor Author

Thanks for the review — all six points are addressed, with a couple of small extras.

Confirmed, as reviewed:

  • Doctor no longer fails repos where .codemap/ is provably ignored. It now consults git check-ignore first and reports OK local Codemap ignore: effective via <source>:<line>:<pattern> (your exact repro now shows effective via .gitignore:12:.codemap/); MISS is reserved for genuinely unignored repos.
  • hasIgnoreLine dropped bufio.Scanner for bytes.Split — a >64 KiB first line can no longer hide a present entry (regression test locked in).
  • appendIgnoreEntry now writes via writeFileAtomic — symlinked exclude is replaced, not written through, and there's no truncate-then-write window.
  • Dead ignoreTracked removed, along with the tracked-.gitignore write path it guarded.
  • The notice keeps the path absolute when filepath.Rel would escape the root (linked worktrees).
  • Repair hint is now the minimal codemap setup --no-hooks --no-mcp --no-config.

Also changed:

  • Squashed to a single commit on top of current main, with a tightened commit message and PR body to match the final state.
  • Tests added for the gaps you flagged: CRLF, missing trailing newline in local mode, and pre-populated exclude, plus the 70 KiB-line regression and a linked-worktree absolute-path notice.
  • One behavioral consequence of the atomic write to flag: a read-only exclude in a writable .git/info is now silently replaced (rename) rather than warning. If you'd rather keep the warning, say so and I'll add an explicit writability probe.

@reneleonhardt
reneleonhardt marked this pull request as ready for review August 13, 2026 08:19

@JordanCoin JordanCoin left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All three findings verified fixed.

Doctor now reports the effective state — OK local Codemap ignore: effective via .gitignore:12:.codemap/ on this repo — and still MISSes a genuinely unignored repo, so the check wasn't gutted to make the failure go away. It also handles a non-git directory (gates off silently rather than reporting something misleading) and a repo with no commits.

hasIgnoreLine moved to bytes.Split, so a 70KB line in .git/info/exclude no longer makes an existing entry read as absent — idempotency holds at exactly one entry across repeated runs. And appendIgnoreEntry now goes through writeFileAtomic, so a symlinked exclude gets replaced rather than written through, and there's no truncate-then-write window.

The repair hint is also now the minimal --no-hooks --no-mcp --no-config form rather than a full setup. Thanks for taking all of it.

@JordanCoin
JordanCoin merged commit d10e739 into JordanCoin:main Aug 13, 2026
12 checks passed
@reneleonhardt
reneleonhardt deleted the fix/exclude-codemap-dir branch August 13, 2026 16:09
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants