tenseleyflow/loader / 5769ad8

Browse files

Keep repair globbing in scope

Authored by mfwolffe <wolffemf@dukes.jmu.edu>
SHA
5769ad82c7f3257da55c819baf8dd2efb7129237
Parents
c968327
Tree
5c2d6cb

2 changed files

StatusFile+-
M src/loader/runtime/hooks.py 0 3
M tests/test_permissions.py 61 0
src/loader/runtime/hooks.pymodified
@@ -775,9 +775,6 @@ class ActiveRepairScopeHook(BaseToolHook):
775
                 return HookResult()
775
                 return HookResult()
776
             if source_of_truth_scope:
776
             if source_of_truth_scope:
777
                 return HookResult()
777
                 return HookResult()
778
-            if context.tool_call.name in {"glob", "grep", "bash"} and repair.allowed_roots:
779
-                if all(path_within_allowed_roots(path, repair.allowed_roots) for path in observed_paths):
780
-                    return HookResult()
781
 
778
 
782
             allowed_preview = ", ".join(f"`{path}`" for path in repair.allowed_paths[:3])
779
             allowed_preview = ", ".join(f"`{path}`" for path in repair.allowed_paths[:3])
783
             if len(repair.allowed_paths) > 3:
780
             if len(repair.allowed_paths) > 3:
tests/test_permissions.pymodified
@@ -904,6 +904,67 @@ async def test_active_repair_scope_hook_blocks_local_rereads_outside_concrete_re
904
     assert str(stylesheet) in result.message
904
     assert str(stylesheet) in result.message
905
 
905
 
906
 
906
 
907
+@pytest.mark.asyncio
908
+async def test_active_repair_scope_hook_blocks_broad_glob_during_concrete_repair(
909
+    temp_dir: Path,
910
+) -> None:
911
+    registry = create_default_registry(temp_dir)
912
+    policy = build_permission_policy(
913
+        active_mode=PermissionMode.WORKSPACE_WRITE,
914
+        workspace_root=temp_dir,
915
+        tool_requirements=registry.get_tool_requirements(),
916
+    )
917
+    dod_store = DefinitionOfDoneStore(temp_dir)
918
+    dod = create_definition_of_done("Repair the generated guide")
919
+    dod.status = "fixing"
920
+    dod_path = dod_store.save(dod)
921
+    guide_root = temp_dir / "guide"
922
+    chapters = guide_root / "chapters"
923
+    chapters.mkdir(parents=True)
924
+    repair_target = guide_root / "index.html"
925
+    repair_target.write_text("<h1>Guide</h1>\n")
926
+    (chapters / "01-introduction.html").write_text("<h1>Intro</h1>\n")
927
+    session = FakeSession(
928
+        active_dod_path=str(dod_path),
929
+        messages=[
930
+            Message(
931
+                role=Role.ASSISTANT,
932
+                content=(
933
+                    "Repair focus:\n"
934
+                    f"- Improve `{repair_target}`: insufficient structured content.\n"
935
+                    f"- Immediate next step: edit `{repair_target}`.\n"
936
+                    "- Do not reread unrelated reference materials or restart discovery while this concrete repair target is unresolved.\n"
937
+                ),
938
+            )
939
+        ],
940
+    )
941
+    hook = ActiveRepairScopeHook(
942
+        dod_store=dod_store,
943
+        project_root=temp_dir,
944
+        session=session,
945
+    )
946
+
947
+    result = await hook.pre_tool_use(
948
+        HookContext(
949
+            tool_call=ToolCall(
950
+                id="glob-1",
951
+                name="glob",
952
+                arguments={"path": str(guide_root), "pattern": "**/*.html"},
953
+            ),
954
+            tool=registry.get("glob"),
955
+            registry=registry,
956
+            permission_policy=policy,
957
+            source="native",
958
+        )
959
+    )
960
+
961
+    assert result.decision == HookDecision.DENY
962
+    assert result.terminal_state == "blocked"
963
+    assert result.message is not None
964
+    assert "active repair scope" in result.message
965
+    assert str(repair_target) in result.message
966
+
967
+
907
 @pytest.mark.asyncio
968
 @pytest.mark.asyncio
908
 async def test_active_repair_scope_hook_blocks_repair_audit_loop_after_repeated_source_reads(
969
 async def test_active_repair_scope_hook_blocks_repair_audit_loop_after_repeated_source_reads(
909
     temp_dir: Path,
970
     temp_dir: Path,