Skip to content

fix(core): make case-only renames stick on case-insensitive filesystems - #1362

Merged
phernandez merged 2 commits into
mainfrom
fix/1281-case-only-rename
Aug 29, 2026
Merged

fix(core): make case-only renames stick on case-insensitive filesystems#1362
phernandez merged 2 commits into
mainfrom
fix/1281-case-only-rename

Conversation

@phernandez

Copy link
Copy Markdown
Member

Fixes #1281.

What actually happens today (re-checked on main)

The 0.22.1 "already exists" rejection is gone since #1002's samefile guards, but the rename still doesn't land:

  1. move_note docs/config.md → docs/Config.md returns 202 and the row becomes docs/Config.md.
  2. Materialization writes through temp_path.replace(target); on APFS/NTFS that replaces the bytes through the existing case-variant entry and keeps the old casing — disk stays config.md.
  3. Old-path cleanup is (correctly) skipped by the same-inode guard.
  4. The next scan misses config.md byte-wise, checksum move-detection rewrites the row back to docs/config.md, and the permalink churns config → config-1 → config because the move's collision check counts the entity itself.

No duplicate rows anymore (those came from the pre-#1002 flow); the reporter's duplicates were on 0.22.1.

Fix

  • InlineNoteFileDeleteEnqueuer already recognizes the same-inode alias; when the two spellings differ only by case it now renames the directory entry to the accepted casing (FileService.move_filePath.rename). On a case-sensitive filesystem the alias check only passes for hard links, where a rename onto itself is a POSIX no-op — the existing hard-link P0 test still passes.
  • resolve_permalink takes current_file_path (the row being re-resolved) and does not suffix a permalink that row already owns. prepare_move_entity_content and the scan-detected move planner pass it; create paths are unchanged.

Tests

  • test_inline_delete_adopts_accepted_casing_for_case_only_rename — on a case-insensitive filesystem the entry ends up spelled Config.md with the bytes intact (skips on case-sensitive FS).
  • test_resolve_permalink_keeps_the_permalink_the_entity_already_ownsdocs/Config.md resolves to docs/config-1 without the hint and to docs/config with it.
  • Touched suites (move tool, move content updates, materialization, permalink formatting, entity service, knowledge router) pass; ruff / ty clean.

Out of scope, as discussed on the issue: directory case-only renames and empty-dir cleanup.

🤖 Generated with Claude Code

https://claude.ai/code/session_017STCpbNsYjZgUdftxgEAZ4

After #1002 a case-only move (config.md -> Config.md) is accepted and the
row updated, but materialization writes through temp.replace(target),
which on APFS/NTFS keeps the existing directory entry's old casing. The
next scan misses the new spelling byte-wise, treats the old one as a
move back, and rewrites the row — the tool reports success and nothing
changes, while the permalink churns config -> config-1 -> config
because the move's collision check counted the entity itself.

- In the local cleanup path that already recognizes the same-inode
  alias, rename the directory entry to the accepted casing so disk
  agrees with the row and the scan is a no-op.
- resolve_permalink takes the row being re-resolved (current_file_path)
  and no longer suffixes a permalink that row already owns; moves and
  scan-detected moves pass it.

Fixes #1281

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017STCpbNsYjZgUdftxgEAZ4
Signed-off-by: phernandez <paul@basicmachines.co>
@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Aug 29, 2026

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review Completed 2026-08-29T02:32:04.924434Z 711cbe3 Manual request
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@phernandez

Copy link
Copy Markdown
Member Author

@codex review

…ure uses

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017STCpbNsYjZgUdftxgEAZ4
Signed-off-by: phernandez <paul@basicmachines.co>
@phernandez

Copy link
Copy Markdown
Member Author

@codex review

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. 👍

Reviewed commit: 711cbe3be5

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

@phernandez
phernandez merged commit 8e2ef0c into main Aug 29, 2026
25 checks passed
@phernandez
phernandez deleted the fix/1281-case-only-rename branch August 29, 2026 02:57
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.

[BUG] Case-only renames on macOS (case-insensitive APFS) fail via move_note and create duplicate index entries

1 participant