The nightly sweep’s dry run said it would commit nothing. It was holding a whole 190-file vault repo over one grep.

What the sweep was looking at

A live session’s subagent had grepped one of its own dirty files while researching. It was pure inspection, no edit. The dry run held the whole repo anyway.

The ownership check in sweep_attribution._owns() was a naive path-substring test. It asked whether a session’s shell command contained the dirty file’s path, and any mention counted as a claim. Reading a path is not a claim on it, but the check could not tell the difference.

A pipe inside the pattern

The grep’s regex argument contained a literal |. The naive path-substring check could not tell that from a real ownership claim. A naive split on | would also misparse it as a second pipeline stage and make the whole line look non-inspecting.

The fix: drop the statements that only look

_owns() now runs each Bash or PowerShell command through _mutating_text():

  • It splits the command into statements on &&, ||, ; and newline.
  • It tokenizes each statement with shlex, which is quote-aware, so a | inside a quoted pattern is not mistaken for a pipe.
  • It splits on a bare | into pipeline stages.
  • It drops any statement whose every stage is a pure-inspection verb: grep, cat, ls, awk, sort, tr, read-only git subcommands like diff, show, log and status, and a few more.

Only the text that survives gets searched for the dirty path. sed -i, mv, rm, git mv and running a script still count as ownership. A statement shlex cannot parse, such as one with an unbalanced quote, is kept as is. The safe direction is to let it count as a claim rather than exempt it.

Checking it

I reproduced the failure against a session’s real transcripts and the vault commit that cleaned up the false hold, and commit_all flipped back to True. I added two regression tests to the sweep attribution test file and confirmed both fail without the fix: a read-only grep no longer holds the repo, and sed -i still does.

Now a read-only grep no longer holds the repo, and sed -i still does.