Implement offline catalog lane scaffold generation

Assistant: codex
Assistant-Model: gpt-6.1-sol
Assistant-Session: 01a10391-6355-7b41-b0fa-3e14e874f091
This commit is contained in:
tegwick 2026-10-04 02:24:40 +02:00
parent 1b274bb136
commit dfe436e4e0
4 changed files with 424 additions and 0 deletions

View file

@ -702,6 +702,26 @@ def _print_entry_table(
console.print(table)
@route_app.command("scaffold")
def route_scaffold(
input_path: Annotated[Path, typer.Option("--input", help="Non-secret draft request YAML")],
output_dir: Annotated[Path, typer.Option("--output-dir", help="New staging directory")],
) -> None:
"""Generate validated draft catalog/playbook pointers offline."""
from warden.routing.scaffold import ScaffoldError, scaffold
from warden.routing.catalog import CatalogError
try:
paths = scaffold(input_path, output_dir)
except ScaffoldError as exc:
err.print(f"[red]Scaffold refused:[/red] {exc}")
raise typer.Exit(1)
except CatalogError:
err.print("[red]Scaffold refused:[/red] existing routing catalog is invalid")
raise typer.Exit(1)
console.print("Draft staging created: " + ", ".join(path.name for path in paths))
@route_app.command("list")
def route_list(
output_json: Annotated[bool, typer.Option("--json", help="Output JSON")] = False,

View file

@ -0,0 +1,198 @@
"""Offline, draft-only catalog scaffolding (WARDEN-WP-0041)."""
from __future__ import annotations
from copy import deepcopy
from datetime import date
from pathlib import Path
import re
import shutil
import yaml
from warden.routing.catalog import (
Catalog,
CatalogError,
_assert_no_secret_material,
_parse_entry,
load_catalog,
)
class ScaffoldError(ValueError):
"""Safe diagnostic: field names only, never supplied values."""
FIELDS = frozenset(
{
"schema_version",
"lane_id",
"title",
"need_keywords",
"consumer_repo",
"owner_repo",
"workload_ref",
"risk",
"wiki_ref",
"canon_ref",
"platform_request_ref",
"reviewed",
}
)
WORKLOAD_FIELDS = frozenset(
{
"applicability",
"rapp_id",
"name",
"deployable",
"declaration_ref",
"reason",
"unknown_reason",
}
)
SLUG = re.compile(r"[a-z][a-z0-9]*(?:-[a-z0-9]+)*\Z")
def _screen(value: object, field: str) -> None:
if isinstance(value, dict):
for key, item in value.items():
_screen(item, field)
elif isinstance(value, list):
for item in value:
_screen(item, field)
elif isinstance(value, str):
try:
_assert_no_secret_material("draft", field, value, prose=False)
except CatalogError:
raise ScaffoldError(f"{field}: suspected secret material") from None
def render(request: object, catalog: Catalog) -> dict[str, str]:
"""Validate before any write; preserve explicit owner metadata."""
if not isinstance(request, dict) or set(request) != FIELDS:
raise ScaffoldError(
"request: required fields missing or unknown fields present"
)
if type(request["schema_version"]) is not int or request["schema_version"] != 1:
raise ScaffoldError("schema_version: expected 1")
for field in FIELDS - {"schema_version", "need_keywords", "workload_ref"}:
value = request[field]
if (
not isinstance(value, str)
or not value.strip()
or any(c in value for c in "\r\n\x00")
):
raise ScaffoldError(f"{field}: expected nonempty single-line text")
_screen(value, field)
for field in ("lane_id", "consumer_repo", "owner_repo"):
if not SLUG.fullmatch(request[field]):
raise ScaffoldError(f"{field}: expected repository/lane slug")
lane = request["lane_id"]
if catalog.get(lane) is not None:
raise ScaffoldError("lane_id: already exists in routing catalog")
if request["owner_repo"] != "railiance-platform":
raise ScaffoldError(
"owner_repo: this scaffold supports railiance-platform KV read pointers"
)
if request["risk"] not in ("standard", "high", "ungraded"):
raise ScaffoldError("risk: expected explicit standard, high or ungraded")
if not re.fullmatch(r"[0-9]{4}-[0-9]{2}-[0-9]{2}", request["reviewed"]):
raise ScaffoldError("reviewed: expected ISO date")
try:
date.fromisoformat(request["reviewed"])
except ValueError:
raise ScaffoldError("reviewed: expected ISO date") from None
keywords = request["need_keywords"]
if (
not isinstance(keywords, list)
or not keywords
or any(
not isinstance(k, str) or not k.strip() or any(c in k for c in "\r\n\x00")
for k in keywords
)
):
raise ScaffoldError("need_keywords: expected nonempty text list")
_screen(keywords, "need_keywords")
workload = request["workload_ref"]
if not isinstance(workload, dict) or not set(workload) <= WORKLOAD_FIELDS:
raise ScaffoldError("workload_ref: expected known catalog fields")
if any(
not isinstance(v, str) or not v.strip() or any(c in v for c in "\r\n\x00")
for v in workload.values()
):
raise ScaffoldError("workload_ref: expected nonempty text values")
_screen(workload, "workload_ref")
entry = {
"id": lane,
"title": request["title"],
"need_keywords": deepcopy(keywords),
"owner_repo": request["owner_repo"],
"subsystem": "openbao",
"warden_executes": False,
"wiki_ref": request["wiki_ref"],
"canon_ref": request["canon_ref"],
"reviewed": request["reviewed"],
"status": "draft",
"workload_ref": deepcopy(workload),
"risk": request["risk"],
"lane": "secret",
"exec_capable": False,
}
try:
_parse_entry(entry, 0)
except CatalogError:
raise ScaffoldError(
"workload_ref: invalid catalog workload declaration"
) from None
playbook = f"""# {request["title"]}
Draft lane: `{lane}`. Consumer: `{request["consumer_repo"]}`.
Owner: `{request["owner_repo"]}`.
Platform request: `{request["platform_request_ref"]}`.
Authoritative reference: `{request["canon_ref"]}`.
## Worker checklist
- Obtain platform review and confirmed workload/auth bindings.
- Review delegated frontdoor and whole-path rotation requirements with owners.
- Use the platform owner's CCR lifecycle and evidence procedure.
- Record approved metadata, attended values and deployment receipts separately.
- Obtain positive/negative capability evidence without reading secret data.
- Review catalog-lane-promotion criteria before any activation.
This draft supplies no executable fetch, approval, grant or runtime readiness.
"""
return {
"catalog.yaml": yaml.safe_dump({"entries": [entry]}, sort_keys=False),
"playbook.md": playbook,
"request.yaml": yaml.safe_dump(deepcopy(request), sort_keys=True),
}
def scaffold(
input_path: Path, output_dir: Path, *, catalog: Catalog | None = None
) -> list[Path]:
"""Publish a new staging directory; never replace existing destinations."""
try:
request = yaml.safe_load(input_path.read_text())
except (OSError, yaml.YAMLError):
raise ScaffoldError("input: unreadable or invalid YAML") from None
outputs = render(request, catalog if catalog is not None else load_catalog())
if output_dir.exists() or output_dir.is_symlink():
raise ScaffoldError("output_dir: destination already exists")
if not output_dir.parent.is_dir() or output_dir.parent.is_symlink():
raise ScaffoldError(
"output_dir: parent must be an existing nonsymlink directory"
)
try:
output_dir.mkdir() # exclusive creation; collision races fail before writes
except OSError:
raise ScaffoldError("output_dir: cannot create new destination") from None
try:
for name, content in outputs.items():
with (output_dir / name).open("x") as stream:
stream.write(content)
except OSError:
shutil.rmtree(output_dir)
raise ScaffoldError("output_dir: publication failed") from None
return [output_dir / name for name in outputs]