The nightly sweep’s dry run held a 190-file notes repo whole, and the only reason was 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, with no edit. 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 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. If the parser can’t read a command, the safe direction is to let it count as a claim rather than exempt it.

Checking it

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

An ownership check has two ways to be wrong. It can hold a repo over a command that only read a file, which is what happened here. It can also release a repo because it misread a command it could not parse. The fix settles the first by recognizing inspection verbs, and it settles the second by treating anything unparseable as a claim.