tenseleyflow/loader / 9dceae8

Browse files

Filter mirrored output targets

Authored by mfwolffe <wolffemf@dukes.jmu.edu>
SHA
9dceae883989209abca0ec51205c5bb3e9038c71
Parents
1f4baad
Tree
c90f266

2 changed files

StatusFile+-
M src/loader/runtime/workflow.py 49 0
M tests/test_workflow.py 52 0
src/loader/runtime/workflow.pymodified
@@ -936,6 +936,7 @@ def infer_pending_todo_output_target(
936936
         project_root=root,
937937
         max_paths=12,
938938
     )
939
+    planned_output_roots = _planned_output_roots_from_targets(planned_targets)
939940
     planned_files = [
940941
         target
941942
         for target, expect_directory in planned_targets
@@ -953,6 +954,15 @@ def infer_pending_todo_output_target(
953954
             for path in dod.touched_files
954955
             if str(path).strip()
955956
         ]
957
+        if planned_output_roots:
958
+            touched_paths = [
959
+                touched
960
+                for touched in touched_paths
961
+                if _path_within_planned_output_roots(
962
+                    touched.expanduser().resolve(strict=False),
963
+                    planned_output_roots,
964
+                )
965
+            ]
956966
 
957967
         for candidate in candidates:
958968
             candidate_str = str(candidate)
@@ -1136,6 +1146,7 @@ def _pending_item_html_sources(
11361146
         project_root=project_root,
11371147
         max_paths=12,
11381148
     )
1149
+    planned_output_roots = _planned_output_roots_from_targets(planned_targets)
11391150
     html_sources: list[Path] = []
11401151
     seen: set[str] = set()
11411152
 
@@ -1143,6 +1154,11 @@ def _pending_item_html_sources(
11431154
         path = Path(raw_path).expanduser().resolve(strict=False)
11441155
         if path.suffix.lower() not in {".html", ".htm"}:
11451156
             continue
1157
+        if planned_output_roots and not _path_within_planned_output_roots(
1158
+            path,
1159
+            planned_output_roots,
1160
+        ):
1161
+            continue
11461162
         key = str(path)
11471163
         if key in seen:
11481164
             continue
@@ -1161,6 +1177,39 @@ def _pending_item_html_sources(
11611177
     return html_sources
11621178
 
11631179
 
1180
+def _planned_output_roots_from_targets(
1181
+    planned_targets: list[tuple[Path, bool]],
1182
+) -> tuple[Path, ...]:
1183
+    roots: list[Path] = []
1184
+    seen: set[str] = set()
1185
+    for target, expect_directory in planned_targets:
1186
+        root = (
1187
+            target.expanduser().resolve(strict=False)
1188
+            if expect_directory
1189
+            else target.expanduser().resolve(strict=False).parent
1190
+        )
1191
+        key = str(root)
1192
+        if key in seen:
1193
+            continue
1194
+        seen.add(key)
1195
+        roots.append(root)
1196
+    return tuple(roots)
1197
+
1198
+
1199
+def _path_within_planned_output_roots(
1200
+    candidate: Path,
1201
+    roots: tuple[Path, ...],
1202
+) -> bool:
1203
+    normalized = candidate.expanduser().resolve(strict=False)
1204
+    for root in roots:
1205
+        try:
1206
+            normalized.relative_to(root)
1207
+            return True
1208
+        except ValueError:
1209
+            continue
1210
+    return False
1211
+
1212
+
11641213
 def _normalize_pending_output_label(value: str) -> str:
11651214
     text = " ".join(str(value).strip().split()).lower()
11661215
     if not text:
tests/test_workflow.pymodified
@@ -1152,6 +1152,58 @@ def test_infer_pending_todo_output_target_maps_aggregate_chapter_step_to_next_de
11521152
     assert target == chapter_two.resolve(strict=False)
11531153
 
11541154
 
1155
+def test_infer_pending_todo_output_target_ignores_workspace_mirror_touched_paths(
1156
+    tmp_path: Path,
1157
+) -> None:
1158
+    dod = create_definition_of_done("Create a multi-file nginx guide.")
1159
+    nginx_root = tmp_path / "Loader" / "guides" / "nginx"
1160
+    chapters = nginx_root / "chapters"
1161
+    chapters.mkdir(parents=True)
1162
+    index_path = nginx_root / "index.html"
1163
+    chapter_one = chapters / "01-introduction.html"
1164
+    chapter_two = chapters / "02-installation.html"
1165
+    repo_mirror = tmp_path / "workspace-mirror" / "chapters" / "01-introduction.html"
1166
+    repo_mirror.parent.mkdir(parents=True)
1167
+    repo_mirror.write_text("<h1>Wrong Root</h1>\n")
1168
+
1169
+    index_path.write_text(
1170
+        "\n".join(
1171
+            [
1172
+                "<html>",
1173
+                '<a href="chapters/01-introduction.html">Chapter 1: Introduction to Nginx</a>',
1174
+                '<a href="chapters/02-installation.html">Chapter 2: Installation and Setup</a>',
1175
+                "</html>",
1176
+            ]
1177
+        )
1178
+        + "\n"
1179
+    )
1180
+    chapter_one.write_text("<h1>Introduction</h1>\n")
1181
+
1182
+    implementation_plan = tmp_path / "implementation.md"
1183
+    implementation_plan.write_text(
1184
+        "\n".join(
1185
+            [
1186
+                "# Implementation Plan",
1187
+                "",
1188
+                "## File Changes",
1189
+                f"- `{nginx_root / 'index.html'}`",
1190
+                f"- `{chapters}/`",
1191
+                "",
1192
+            ]
1193
+        )
1194
+    )
1195
+    dod.implementation_plan = str(implementation_plan)
1196
+    dod.touched_files.extend([str(index_path), str(chapter_one), str(repo_mirror)])
1197
+
1198
+    target = infer_pending_todo_output_target(
1199
+        dod,
1200
+        "Create chapter files following the established pattern",
1201
+        project_root=tmp_path,
1202
+    )
1203
+
1204
+    assert target == chapter_two.resolve(strict=False)
1205
+
1206
+
11551207
 def test_preferred_pending_todo_item_keeps_setup_step_when_missing_file_parent_absent(
11561208
     tmp_path: Path,
11571209
 ) -> None: