Projects have their own name, description and goal; the template is shown only as origin
All checks were successful
CI Smoke / host-smoke (push) Successful in 0s
CI Smoke / container-smoke (push) Successful in 52s
Work Records / validate (push) Successful in 12s

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

Assistant: claude-code
Assistant-Model: sonnet
Assistant-Process: 295952@bnt-lap001
Assistant-Session: e93f64ad-516c-46eb-9666-aad8d300c477
This commit is contained in:
tegwick 2026-09-29 14:12:21 +02:00
parent 334e0d3eb6
commit f8ce4717a8
8 changed files with 125 additions and 24 deletions

View file

@ -212,8 +212,13 @@ def start_run(reg: Registry, doc: Doc, pack_id: str, operator: str, target: str
# ---- templates -----------------------------------------------------------------------------------------------
def instantiate(reg: Registry, template_id: str, project_id: str, environment: str,
values: dict[str, str] | None = None, title: str | None = None, root: Path | None = None) -> Path:
"""Write projects/<id>/project.yaml from a template. Required attributes may stay unset; `state()` lists them."""
values: dict[str, str] | None = None, title: str | None = None, root: Path | None = None,
description: str = "", goal: str = "") -> Path:
"""Write projects/<id>/project.yaml from a template. Required attributes may stay unset; `state()` lists them.
The project has its own name, description and goal. Nothing of the template's own title, goal or description
is copied: the project only records which template it came from.
"""
t = reg.templates.get(template_id)
if t is None or not t.ok:
raise ValueError(f"no valid template {template_id}")
@ -221,7 +226,11 @@ def instantiate(reg: Registry, template_id: str, project_id: str, environment: s
raise ValueError("project id must be lower-case letters, digits, dots and hyphens")
if environment not in R.ENVIRONMENTS:
raise ValueError(f"environment must be one of {', '.join(R.ENVIRONMENTS)}")
d = {k: v for k, v in t.data.items() if k not in ("spec", "id", "title")}
from .pack import SECRET_RE
for label, text in (("title", title or ""), ("description", description), ("goal", goal)):
if SECRET_RE.search(text):
raise ValueError(f"the {label} looks like a secret; projects are never secret")
d = {k: v for k, v in t.data.items() if k not in ("spec", "id", "title", "goal", "description")}
attrs = normalize(d.get("attributes"))
for k, v in (values or {}).items():
attrs.setdefault(k, {"value": None, "requirement": None})
@ -230,9 +239,15 @@ def instantiate(reg: Registry, template_id: str, project_id: str, environment: s
for k, a in attrs.items():
out_attrs[k] = a["value"] if a.get("value") is not None and not a.get("requirement") and not a.get("note") else \
{kk: vv for kk, vv in a.items() if vv is not None}
project = {"spec": "project/v0.1", "id": project_id, "title": title or t.data["title"],
**{k: v for k, v in d.items() if k not in ("attributes", "environment")}, "environment": environment,
"template": template_id}
if len((title or project_id).strip()) < 3:
raise ValueError("the project name needs at least 3 characters")
project = {"spec": "project/v0.1", "id": project_id, "title": (title or project_id).strip()}
if description.strip():
project["description"] = description.strip()
if goal.strip():
project["goal"] = goal.strip()
project.update({k: v for k, v in d.items() if k not in ("attributes", "environment")})
project.update({"environment": environment, "template": template_id})
if out_attrs:
project["attributes"] = out_attrs
home = Path(root) if root else t.path.parent.parent