From 5a5cfbdaa06d57ca85c55a0afb6f56baffbf0ec3 Mon Sep 17 00:00:00 2001 From: Scott Werner Date: Mon, 17 Aug 2026 16:36:40 -0400 Subject: [PATCH] Parse template dependencies whose paths collide with discovery roots Batched dependency discovery pre-seeded roots into the path-keyed result map and reused that map as the traversal-dedup set, so a loaded include target whose path matched a root (e.g. a goal template including the graph file that anchors an inline prompt) was recorded but never parsed, silently accepting invalid template content that per-root discovery used to reject. Dedup traversal on the full (path, root, content) occurrence instead, which also stops re-parsing identical duplicate roots. Co-Authored-By: Claude Fable 5 --- .../fabro-workflow-version/src/lib.rs | 31 +++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/lib/components/fabro-workflow-version/src/lib.rs b/lib/components/fabro-workflow-version/src/lib.rs index 27edad510..226a1d2e3 100644 --- a/lib/components/fabro-workflow-version/src/lib.rs +++ b/lib/components/fabro-workflow-version/src/lib.rs @@ -685,6 +685,37 @@ mod tests { )); } + #[test] + fn validates_graph_files_included_from_goal_templates() { + // The graph file's inline prompt anchors a template root at the graph + // path; that root must not shadow the raw graph content when a goal + // template includes the graph file itself. + let error = version_with( + [ + ( + "workflow.fabro", + r#"digraph W { + graph [goal="@goal.md"] + step [prompt="hello", note="{% include 'missing.md' %}"] + }"#, + ), + ("goal.md", r#"{% include "workflow.fabro" %}"#), + ], + [], + ) + .unwrap_err(); + + assert!(matches!( + error, + WorkflowVersionError::Template { path: source_path, source } + if source_path == path("workflow.fabro") + && matches!( + source.as_ref(), + TemplateDiscoveryError::Missing { reference, .. } if reference == "missing.md" + ) + )); + } + #[test] fn accepts_root_config_and_all_dockerfile_path_sources() { let version = version_with(