11-- post_tool_handle_spec.lua — Smoke tests for the in-process post-tool.
22--
33-- post_tool.handle's contract:
4- -- * Bash: clears `deleted` / `bash_modified` / `bash_created` markers from
5- -- the changes registry; never touches `modified` / `created` markers
6- -- from concurrent Edit/Write/ApplyPatch proposals.
4+ -- * Bash: clears the markers for the files THIS command touched (re-detected
5+ -- from the post payload), never every bash-owned marker — so concurrent
6+ -- pending Bash commands keep their markers (issue #83), and `modified` /
7+ -- `created` markers from concurrent Edit/Write/ApplyPatch are untouched.
78-- * ApplyPatch: closes one preview per file referenced in the patch text.
89-- * Other tools: closes the single preview keyed by file_path.
910-- * Always returns "" (no backend reads post-tool stdout).
@@ -25,10 +26,10 @@ describe("post_tool.handle (Bash status clear)", function()
2526 assert .is_nil (changes .get (" /proj/gone.txt" ))
2627 end )
2728
28- it (" clears bash_modified / bash_created markers" , function ()
29+ it (" clears the bash_modified / bash_created markers for files it touched " , function ()
2930 changes .set (" /proj/a.txt" , " bash_modified" )
3031 changes .set (" /proj/b.txt" , " bash_created" )
31- post_tool .handle (payload (" Bash" , { command = " echo x > a.txt" }), " claudecode" )
32+ post_tool .handle (payload (" Bash" , { command = " echo x > a.txt; echo y > b.txt " }), " claudecode" )
3233 assert .is_nil (changes .get (" /proj/a.txt" ))
3334 assert .is_nil (changes .get (" /proj/b.txt" ))
3435 end )
@@ -44,6 +45,28 @@ describe("post_tool.handle (Bash status clear)", function()
4445 assert .equals (" created" , changes .get (" /proj/new.lua" ))
4546 assert .is_nil (changes .get (" /proj/gone.txt" ))
4647 end )
48+
49+ it (" accepting one Bash delete keeps other pending Bash deletes marked" , function ()
50+ -- Regression for #83: two separate Bash deletes are pending, each with its
51+ -- own `deleted` marker. Accepting the first command (its PostToolUse fires)
52+ -- must clear ONLY that command's file; the still-pending command's marker
53+ -- survives. The old global status sweep wiped both.
54+ changes .set (" /proj/a.txt" , " deleted" )
55+ changes .set (" /proj/b.txt" , " deleted" )
56+ post_tool .handle (payload (" Bash" , { command = " rm a.txt" }), " claudecode" )
57+ assert .is_nil (changes .get (" /proj/a.txt" ))
58+ assert .equals (" deleted" , changes .get (" /proj/b.txt" ))
59+ end )
60+
61+ it (" scoped clear covers bash writes, not just deletes" , function ()
62+ -- A pending shell write (`echo x > b.txt` → bash_created/modified) must
63+ -- survive accepting a different command's write.
64+ changes .set (" /proj/a.txt" , " bash_modified" )
65+ changes .set (" /proj/b.txt" , " bash_created" )
66+ post_tool .handle (payload (" Bash" , { command = " echo x > a.txt" }), " claudecode" )
67+ assert .is_nil (changes .get (" /proj/a.txt" ))
68+ assert .equals (" bash_created" , changes .get (" /proj/b.txt" ))
69+ end )
4770end )
4871
4972describe (" post_tool.handle (return value)" , function ()
0 commit comments