fix(prompts): fix three infrastructure bugs in prompt dependency resolution

- ContentMacro: add __post_init__ to auto-derive raw_text when built
  programmatically, preventing str.replace("", X) corruption
- MacroParser: add @{target} shorthand syntax support mapped to REQUIRED kind,
  updating parse, has_macros, count_macros, and find_macro_positions
- Artifact: store content in model and SQLite DB, replace resolver placeholder
  with actual artifact content, add migration for existing databases

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-02-11 20:53:02 +01:00
parent 01b9596ce6
commit 706981c39f
9 changed files with 191 additions and 10 deletions

View File

@@ -97,7 +97,7 @@ class TestContextCompiler:
# Macro should be replaced with resolved content
assert "{{require:intro}}" not in compiled.content
assert "[Content of intro from space-1]" in compiled.content
assert "Introduction text" in compiled.content
assert "intro" in compiled.dependency_digests
def test_compile_with_optional_macros_substitutes_empty(
@@ -126,7 +126,7 @@ class TestContextCompiler:
# Optional missing macro should be removed
assert "{{optional:missing}}" not in compiled.content
assert compiled.content == "Start [Content of present from space-1] middle end"
assert compiled.content == "Start Present content middle end"
def test_compile_failed_resolution_raises_error(
self, compiler, analyzer, resolver
@@ -257,3 +257,32 @@ class TestContextCompiler:
assert info["dependency_count"] == 1
assert "dep" in info["dependencies"]
assert info["is_partial"] is False
def test_compile_with_programmatic_macros_no_corruption(
self, compiler, analyzer, resolver, artifact_service
):
"""Test that compilation with programmatic macros doesn't corrupt content."""
artifact_service.create_artifact(
space_id="space-1",
name="dep",
content="Dependency content",
)
# Use @{target} shorthand syntax (parsed into programmatic macros)
content = "Before @{dep} after"
template = PromptTemplate.create(
space_id="space-1",
name="test",
content=content,
)
analyzer.analyze(template, content)
config = ResolutionConfig(space_id="space-1")
result = resolver.resolve_template(template, config)
compiled = compiler.compile(template, content, result)
# Content should be cleanly substituted, not corrupted
assert "@{dep}" not in compiled.content
assert "Dependency content" in compiled.content
assert compiled.content == "Before Dependency content after"