@@ -169,6 +169,56 @@ def test_read_only_modes_deny_mutating_and_dynamic_tools(self):
169169 main ._execution_capability_token = previous_token
170170 main ._set_workspace_root (previous_root )
171171
172+ def test_accepted_plan_rejects_unrelated_mutation_before_execution (self ):
173+ previous = (
174+ main .tasks , main ._interaction_controller , main ._execution_capability_token ,
175+ main ._get_workspace_root (),
176+ )
177+ with tempfile .TemporaryDirectory () as directory :
178+ root = Path (directory )
179+ store = EventStore (root / "state.sqlite3" )
180+ manager = TaskManager (store , workspace_id = "bound" , session_id = "surface:tui" )
181+ controller = InteractionController (
182+ store , workspace_id = "bound" , session_id = "surface:tui" ,
183+ )
184+ proposal = controller .propose_plan ({
185+ "title" : "Create page" , "summary" : "Create only the accepted page." ,
186+ "assumptions" : [], "steps" : [{
187+ "id" : "page" , "title" : "Create index.html" ,
188+ "description" : "Write index.html with the requested markup." ,
189+ "acceptance" : ["index.html exists" ],
190+ }],
191+ })
192+ controller .accept_plan (
193+ manager , plan_id = proposal ["plan_id" ], version = proposal ["version" ],
194+ )
195+ main .tasks = manager
196+ main ._interaction_controller = controller
197+ main ._set_workspace_root (root )
198+ main ._execution_capability_token = issue_capability_token (
199+ "test:accepted-plan" , frozenset ({"write" }),
200+ )
201+ try :
202+ operations : set [str ] = set ()
203+ rejected = main ._execute_turn_action (
204+ "write_file" , "README.md|wrong" , operation_scope = "bound" ,
205+ successful_operations = operations ,
206+ )
207+ self .assertFalse (rejected .success )
208+ self .assertEqual (rejected .failure , "plan_action_mismatch" )
209+ self .assertFalse ((root / "README.md" ).exists ())
210+
211+ accepted = main ._execute_turn_action (
212+ "write_file" , "index.html|ready" , operation_scope = "bound" ,
213+ successful_operations = operations ,
214+ )
215+ self .assertTrue (accepted .success , accepted .result )
216+ self .assertEqual ((root / "index.html" ).read_text (encoding = "utf-8" ), "ready" )
217+ finally :
218+ (main .tasks , main ._interaction_controller , main ._execution_capability_token ,
219+ previous_root ) = previous
220+ main ._set_workspace_root (previous_root )
221+
172222 def test_mocked_provider_flow_questions_revision_acceptance_and_agent_receipt (self ):
173223 class LearningStub :
174224 def feedback_signal (self , _text ): return None
0 commit comments