Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions .pdd/meta/sync_order_python.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
{
"pdd_version": "0.0.298.dev0",
"timestamp": "2026-07-10T06:09:15.384060+00:00",
"command": "fix",
"prompt_hash": "2aefd06c3a9bdf2c2fc30dcafd174edf7820c33fa56ff40c9826009008c644bb",
"code_hash": "22dec5209b81b3ab64575f541212c85b86b534844db64259a71d8d8d18daf750",
"example_hash": null,
"test_hash": null,
"test_files": null,
"include_deps": {
"context/agentic_common_example.py": "c1538a115a3a33da228e7f6cc048f2a99023cd2c83a0efef382e49c477b24ad3",
"context/architecture_sync_example.py": "b6158dbc7ca3fd32665528562225f6739ec521a071d845acab0f7caaea221ae0",
"context/auto_include_example.py": "0c278ed716298149777bc7082827eb8aa6d80dac264c66b6cded6871232f2df7",
"context/python_preamble.prompt": "0388ed131bf986f8752e1bc4c81e4da0460cfe2908ec8c60b1314edbab768254"
}
}
2 changes: 1 addition & 1 deletion pdd/prompts/sync_order_python.prompt
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ A utility module that parses `<include>` tags from prompt files, builds a depend
- bare body: `<include>path</include>`
- attributed body: `<include query="...">path</include>` (or any other attribute — discovery doesn't inspect them, just allows them on the opening tag)
- self-closing: `<include path="..." />`
- Body-form regex: `r'<include(?:\s+([^>]*?))?(?<!/)>(.*?)</include>'` with `re.DOTALL`, capturing the optional attribute string so the parser can honor a `path=` attribute on attributed body includes. The `(?<!/)>` negative lookbehind is required so a self-closing `<include path="..." />` is NOT picked up by the body-form regex (without it, the body-form would absorb everything between the self-closing tag and the next `</include>`, dropping inner includes entirely).
- Body-form regex: `r'<include(?:\s+([^>]*?))?(?<!/)>((?:(?!<include\b).)*?)</include>'` with `re.DOTALL`, capturing the optional attribute string so the parser can honor a `path=` attribute on attributed body includes. The `(?<!/)>` negative lookbehind is required so a self-closing `<include path="..." />` is NOT picked up by the body-form regex. The tempered body MUST reject any match that crosses another `<include` opener; otherwise an inline documentation token such as `` `<include>` `` can pair with a later real closing tag and turn hundreds of lines of prompt prose into a bogus path.
- Self-closing regex: `r'<include\s+([^>]*?)\s*/>'` with `re.DOTALL`, then extract the `path` attribute via `r'path\s*=\s*["\']([^"\']+)["\']'` from the captured attrs.
- `<include-many>` body regex: `r'<include-many(?:\s+[^>]*?)?>(.*?)</include-many>'` (attributes optional on the opening tag).
- For `<include-many>` payload, mirror `pdd.preprocess.process_include_many_tags`: split on newlines first, then on commas within each line, strip each piece, drop empties. Both separators must be supported because real prompts write the list either inline (`<include-many>a, b, c</include-many>`) or one-per-line (`<include-many>\na\nb\nc\n</include-many>`).
Expand Down
12 changes: 10 additions & 2 deletions pdd/sync_order.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,13 @@ def extract_includes_from_file(file_path: Path) -> Set[str]:
# - self-closing: ``<include path="..." />``
# Real prompts use all three; missing any one means a real PDD
# include form sits outside #739's safety net.
# Body-form must exclude self-closing `<include ... />`. Without
# Body-form must exclude self-closing `<include ... />` and must not
# cross another include opener. Without the tempered body match, an
# inline documentation token such as `` `<include>` `` can consume
# everything through a later real ``</include>`` and surface hundreds
# of lines as a bogus filesystem path.
#
# Without
# the `(?<!/)>` lookbehind, the body-form regex would absorb the
# entire `<include path="..." />\n<include>foo.md` span as one
# match, losing the inner include and producing garbage.
Expand All @@ -68,7 +74,9 @@ def extract_includes_from_file(file_path: Path) -> Set[str]:
# actually resolves it as ``docs/source.md`` — and the scope
# guard's allowlist would diverge from the real include graph.
single_matches = re.findall(
r'<include(?:\s+([^>]*?))?(?<!/)>(.*?)</include>', content, re.DOTALL
r'<include(?:\s+([^>]*?))?(?<!/)>((?:(?!<include\b).)*?)</include>',
content,
re.DOTALL,
)
for attrs, body in single_matches:
path_value: Optional[str] = None
Expand Down
14 changes: 14 additions & 0 deletions tests/test_sync_order.py
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,20 @@ def test_extract_includes_body_form_no_path_attr_uses_body(tmp_path):
includes = sync_order.extract_includes_from_file(f)
assert includes == {"docs/source.md"}

def test_extract_includes_does_not_span_inline_documentation_token(tmp_path):
"""An inline `<include>` mention must not consume a later real include."""
f = tmp_path / "test.prompt"
f.write_text(
"Use `<include>` tags to expand context.\n"
"<pdd.context>\n"
" <include select=\"def:example\">context/example.py</include>\n"
"</pdd.context>\n",
encoding="utf-8",
)

includes = sync_order.extract_includes_from_file(f)
assert includes == {"context/example.py"}

# ==============================================================================
# Unit Tests: extract_module_from_include
# ==============================================================================
Expand Down
Loading