audit-log trailer + pre-commit stash/restore: silent staged-file swap on conventional-commits retry #1

Open
opened 2026-05-23 20:53:56 +00:00 by coilysiren · 0 comments
Owner

Originally filed by @coilysiren on 2026-05-23T06:18:38Z - https://github.com/coilysiren/coily/issues/333

Problem

In an agentic-os-kai session today, pre-commit's stash/restore-unstaged dance interacted badly with the coily audit-log trailer hook and silently swapped which file ended up committed.

Sequence:

  1. Working tree had two changes: a staged file (.agents/skills/coding-otel-a2a-relay/SKILL.md, new) and an unstaged file (config/mcporter.json, modified).
  2. coily git commit -F msg.txt ran. pre-commit stashed the unstaged config/mcporter.json, ran all hooks against the staged skill, all passed.
  3. conventional-commits hook rejected the commit message subject ("skill:" not in the allowed type list).
  4. pre-commit restored the stashed config/mcporter.json to the working tree.
  5. Retry with a fixed message ("docs(skills):"). All hooks passed. Commit landed.
  6. Final commit contained config/mcporter.json only. The skill was back to untracked.

End state: 9b7ccb1 closed #757 with the wrong file. The skill stayed uncommitted on disk. Required #759 as a re-do.

Hypothesis

The coily audit-log trailer hook runs in the second pass and may re-stage the working tree (to append its trailer to the commit). When it re-stages, it picks up whatever is in the working tree, not what was originally staged. After the restore, the working tree had the mcporter.json change unstaged plus the skill staged. Something in the second pass dropped the skill from staging and added mcporter.json instead.

Fix candidates

  • The audit-log trailer should be a commit-msg hook (operates only on the message), not a pre-commit hook that touches the index.
  • Or: the audit-log trailer must capture-and-restore the staged set explicitly, not re-derive from the working tree.
  • Or: detect this exact failure mode (staged-set diverges from pre-stash staged-set) and abort with a loud error.

Reproducer sketch

cd ~/projects/coilysiren/agentic-os-kai
# Create one staged change and one unstaged change
echo "test1" > /tmp/staged.md && git add /tmp/staged.md  # roughly
echo "test2" >> existing-file.md  # unstaged

# Commit with a deliberately-bad subject line so conventional-commits rejects
coily git commit -m "skill: bad subject"

# Then retry with a valid subject
coily git commit -m "docs: valid subject"

# Inspect: which file landed?
git show --stat HEAD

Why this matters

Silent file-swap during commit is the worst class of pre-commit bug. The user sees "Passed" and "[main hash] ...", trusts it, pushes. Two pushes later they notice the wrong content is on the wire. In this session: skill missed Windows-side Claude entirely, requiring a follow-up issue and a second commit.

Out of scope

  • The misleading commit message on 9b7ccb1 is already on the wire and not worth rewriting.
  • The actual skill landed via #759 in ea679d9.
_Originally filed by @coilysiren on 2026-05-23T06:18:38Z - [https://github.com/coilysiren/coily/issues/333](https://github.com/coilysiren/coily/issues/333)_ **Problem** In an agentic-os-kai session today, pre-commit's stash/restore-unstaged dance interacted badly with the `coily audit-log trailer` hook and silently swapped which file ended up committed. Sequence: 1. Working tree had two changes: a staged file (`.agents/skills/coding-otel-a2a-relay/SKILL.md`, new) and an unstaged file (`config/mcporter.json`, modified). 2. `coily git commit -F msg.txt` ran. pre-commit stashed the unstaged `config/mcporter.json`, ran all hooks against the staged skill, all passed. 3. `conventional-commits` hook rejected the commit message subject ("skill:" not in the allowed type list). 4. pre-commit restored the stashed `config/mcporter.json` to the working tree. 5. Retry with a fixed message ("docs(skills):"). All hooks passed. Commit landed. 6. Final commit contained `config/mcporter.json` only. The skill was back to untracked. End state: 9b7ccb1 closed #757 with the wrong file. The skill stayed uncommitted on disk. Required #759 as a re-do. **Hypothesis** The `coily audit-log trailer` hook runs in the second pass and may re-stage the working tree (to append its trailer to the commit). When it re-stages, it picks up whatever is in the working tree, not what was originally staged. After the restore, the working tree had the mcporter.json change unstaged plus the skill staged. Something in the second pass dropped the skill from staging and added mcporter.json instead. **Fix candidates** - The audit-log trailer should be a `commit-msg` hook (operates only on the message), not a `pre-commit` hook that touches the index. - Or: the audit-log trailer must capture-and-restore the staged set explicitly, not re-derive from the working tree. - Or: detect this exact failure mode (staged-set diverges from pre-stash staged-set) and abort with a loud error. **Reproducer sketch** ```sh cd ~/projects/coilysiren/agentic-os-kai # Create one staged change and one unstaged change echo "test1" > /tmp/staged.md && git add /tmp/staged.md # roughly echo "test2" >> existing-file.md # unstaged # Commit with a deliberately-bad subject line so conventional-commits rejects coily git commit -m "skill: bad subject" # Then retry with a valid subject coily git commit -m "docs: valid subject" # Inspect: which file landed? git show --stat HEAD ``` **Why this matters** Silent file-swap during commit is the worst class of pre-commit bug. The user sees "Passed" and "[main hash] ...", trusts it, pushes. Two pushes later they notice the wrong content is on the wire. In this session: skill missed Windows-side Claude entirely, requiring a follow-up issue and a second commit. **Out of scope** - The misleading commit message on 9b7ccb1 is already on the wire and not worth rewriting. - The actual skill landed via #759 in ea679d9.
Sign in to join this conversation.
No labels
P0
P1
P2
P3
P4
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
coilyco-bridge/coily#1
No description provided.