Files
llm-wiki/harness/runtime/template_renderer.py
T

91 lines
3.6 KiB
Python

#!/usr/bin/env python3
"""Strict, dependency-free rendering for deterministic harness templates."""
from __future__ import annotations
import hashlib
from pathlib import Path
import re
from typing import Mapping
PLACEHOLDER_RE = re.compile(r"\{\{([a-z][a-z0-9_]*)\}\}")
REGION_START = "<!-- RUNTIME-TEMPLATE: branch-from-project:start -->"
REGION_END = "<!-- RUNTIME-TEMPLATE: branch-from-project:end -->"
GENERATED_START = "<!-- GENERATED: branch-contract:start -->"
GENERATED_END = "<!-- GENERATED: branch-contract:end -->"
class TemplateRenderError(ValueError):
def __init__(self, code: str, message: str) -> None:
self.code = code
super().__init__(message)
def extract_region(text: str, start: str, end: str) -> str:
"""Return one ordered marker region, excluding the marker lines."""
if text.count(start) != 1 or text.count(end) != 1:
raise TemplateRenderError("INVALID_TEMPLATE_REGION", f"expected one marker pair: {start} / {end}")
start_at = text.index(start) + len(start)
end_at = text.index(end, start_at)
if start_at >= end_at:
raise TemplateRenderError("INVALID_TEMPLATE_REGION", "template region markers are reversed")
return text[start_at:end_at].strip("\n") + "\n"
def generated_region(text: str) -> str:
"""Return the runtime-owned branch contract including its marker lines."""
if text.count(GENERATED_START) != 1 or text.count(GENERATED_END) != 1:
raise TemplateRenderError("GENERATED_REGION_DRIFT", "generated branch-contract markers must occur exactly once")
start_at = text.index(GENERATED_START)
end_at = text.index(GENERATED_END, start_at) + len(GENERATED_END)
if start_at >= end_at:
raise TemplateRenderError("GENERATED_REGION_DRIFT", "generated branch-contract markers are reversed")
return text[start_at:end_at]
def generated_sha256(text: str) -> str:
return hashlib.sha256(generated_region(text).encode("utf-8")).hexdigest()
def render(text: str, values: Mapping[str, str], *, allowed: set[str]) -> str:
"""Render only allowlisted placeholders and reject missing or extra input."""
discovered = set(PLACEHOLDER_RE.findall(text))
unknown = sorted(discovered - allowed)
if unknown:
raise TemplateRenderError("UNKNOWN_PLACEHOLDER", f"template contains non-allowlisted placeholders: {unknown}")
missing = sorted(discovered - set(values))
if missing:
raise TemplateRenderError("UNRESOLVED_PLACEHOLDER", f"placeholder values are missing: {missing}")
extra = sorted(set(values) - allowed)
if extra:
raise TemplateRenderError("UNEXPECTED_TEMPLATE_VALUE", f"values were supplied for unknown placeholders: {extra}")
rendered = PLACEHOLDER_RE.sub(lambda match: values[match.group(1)], text)
unresolved = sorted(set(PLACEHOLDER_RE.findall(rendered)))
if unresolved:
raise TemplateRenderError("UNRESOLVED_PLACEHOLDER", f"unresolved placeholders remain: {unresolved}")
return rendered
def render_branch_note(template_path: Path, values: Mapping[str, str]) -> str:
"""Render the deterministic branch-from-project region of the branch template."""
template = template_path.read_text(encoding="utf-8")
body = extract_region(template, REGION_START, REGION_END)
allowed = {
"branch_slug",
"branch_id",
"project",
"project_parent_link",
"work_item",
"inherits_yaml",
"depends_on_yaml",
"created",
"contract_packet_sha256",
"project_revision",
"completion",
"inherited_rows",
"dependency_display",
}
return render(body, values, allowed=allowed)