Filter mirrored output targets
Authored by
mfwolffe <wolffemf@dukes.jmu.edu>
- SHA
9dceae883989209abca0ec51205c5bb3e9038c71- Parents
-
1f4baad - Tree
c90f266
9dceae8
9dceae883989209abca0ec51205c5bb3e9038c711f4baad
c90f266| Status | File | + | - |
|---|---|---|---|
| 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( | ||
| 936 | 936 | project_root=root, |
| 937 | 937 | max_paths=12, |
| 938 | 938 | ) |
| 939 | + planned_output_roots = _planned_output_roots_from_targets(planned_targets) | |
| 939 | 940 | planned_files = [ |
| 940 | 941 | target |
| 941 | 942 | for target, expect_directory in planned_targets |
@@ -953,6 +954,15 @@ def infer_pending_todo_output_target( | ||
| 953 | 954 | for path in dod.touched_files |
| 954 | 955 | if str(path).strip() |
| 955 | 956 | ] |
| 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 | + ] | |
| 956 | 966 | |
| 957 | 967 | for candidate in candidates: |
| 958 | 968 | candidate_str = str(candidate) |
@@ -1136,6 +1146,7 @@ def _pending_item_html_sources( | ||
| 1136 | 1146 | project_root=project_root, |
| 1137 | 1147 | max_paths=12, |
| 1138 | 1148 | ) |
| 1149 | + planned_output_roots = _planned_output_roots_from_targets(planned_targets) | |
| 1139 | 1150 | html_sources: list[Path] = [] |
| 1140 | 1151 | seen: set[str] = set() |
| 1141 | 1152 | |
@@ -1143,6 +1154,11 @@ def _pending_item_html_sources( | ||
| 1143 | 1154 | path = Path(raw_path).expanduser().resolve(strict=False) |
| 1144 | 1155 | if path.suffix.lower() not in {".html", ".htm"}: |
| 1145 | 1156 | continue |
| 1157 | + if planned_output_roots and not _path_within_planned_output_roots( | |
| 1158 | + path, | |
| 1159 | + planned_output_roots, | |
| 1160 | + ): | |
| 1161 | + continue | |
| 1146 | 1162 | key = str(path) |
| 1147 | 1163 | if key in seen: |
| 1148 | 1164 | continue |
@@ -1161,6 +1177,39 @@ def _pending_item_html_sources( | ||
| 1161 | 1177 | return html_sources |
| 1162 | 1178 | |
| 1163 | 1179 | |
| 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 | + | |
| 1164 | 1213 | def _normalize_pending_output_label(value: str) -> str: |
| 1165 | 1214 | text = " ".join(str(value).strip().split()).lower() |
| 1166 | 1215 | 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 | ||
| 1152 | 1152 | assert target == chapter_two.resolve(strict=False) |
| 1153 | 1153 | |
| 1154 | 1154 | |
| 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 | + | |
| 1155 | 1207 | def test_preferred_pending_todo_item_keeps_setup_step_when_missing_file_parent_absent( |
| 1156 | 1208 | tmp_path: Path, |
| 1157 | 1209 | ) -> None: |