init: llm-wiki-haness 하네스 설계
This commit is contained in:
@@ -0,0 +1,90 @@
|
||||
#!/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)
|
||||
Reference in New Issue
Block a user