Skip to content

fix(aios-commit): --vault commits a staged rename as a rename - #188

Merged
chuycepeda merged 2 commits into
The-AIOS:mainfrom
matiasmacera:fix/vault-sweep-no-renames
Sep 29, 2026
Merged

chuycepeda merged 2 commits into
The-AIOS:mainfrom
matiasmacera:fix/vault-sweep-no-renames

Conversation

@matiasmacera

Copy link
Copy Markdown
Contributor

What

aios-commit --vault could commit a rename as a copy. When the rename was already staged in the real index, both names ended up in HEAD. That happens with git mv, or with an editor that stages its own moves.

The cause is that the sweep lists paths with git diff --name-only HEAD, and rename detection is on by default for git diff. A staged rename is therefore reported only under its new name. The commit is built in a throwaway index seeded from HEAD, so it added the new path and kept the old one. A plain mv with nothing staged was unaffected, because it shows as a deletion plus an untracked file, and that is why this hid.

Fix

--no-renames on both of the sweep's diff calls: the one against HEAD, and the --cached one for a repo with no commits yet. Both sides of a rename are then listed. The pathspec is unchanged, so a move into or out of vault/ contributes only the side inside it.

A vault that already holds a duplicate heals itself. The old file is no longer on disk, so the next --vault run reports it as a deletion.

Proof

tests/aios-commit.test.sh, which already runs in both CI lanes, gains four cases:

  • git mv then --vault: HEAD holds only the new name.
  • Plain mv then --vault: HEAD holds only the new name, the control.
  • The same git mv against the hook pinned at the pre-change commit: both names land in HEAD. This case is skipped with a message when that commit is absent.
  • A duplicate made by the old hook, followed by one run of the new one: the old name is removed.

Removing --no-renames fails the first case. tests/commit-scan-fail-closed.test.sh fails one sweep call through a git shim matched on the exact argv, so the shim now matches the new argv too. 34 pass under bash 5 and bash 3.2, and the full tests/ run passes.

Scope

hooks/aios-commit (two flags and a comment), the two tests, and the CHANGELOG entry. It closes the last aios-commit item from the hooks audit in the series announced in #158.

🤖 Generated with Claude Code

matiasmacera and others added 2 commits September 25, 2026 19:56
With rename detection on (git diff's default), a rename already staged
in the real index (`git mv`, an editor that stages its moves) listed
only its new name in the --vault sweep. The commit, built from HEAD,
added the new path and never removed the old one: both names landed in
HEAD. The sweep's diff calls now pass --no-renames, so both sides are
listed. A duplicate left by the old sweep is removed by the next
--vault commit, since the old file is no longer on disk.

tests/aios-commit.test.sh covers git mv, plain mv, the old hook pinned
by sha, and the healing run. The commit-scan test's git shim now also
matches the new argv.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@chuycepeda
chuycepeda merged commit 4f34a51 into The-AIOS:main Sep 29, 2026
16 checks passed
@chuycepeda

Copy link
Copy Markdown
Member

Thank you, Matías. This shipped in v0.8.6 (merged via #195). One follow-up on top: after a staged rename, the post-commit index sync tried to add the old path, which no longer exists, and printed a false warning. It now skips paths that are gone from both the disk and the index, and the test checks that no warning appears and the status is clean.

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