diff --git a/scripts/generate-template.py b/scripts/generate-template.py
new file mode 100644
index 0000000..5b4f247
--- /dev/null
+++ b/scripts/generate-template.py
@@ -0,0 +1,136 @@
+#!/usr/bin/env python3
+"""Generate a quality-verified HTML artifact from a pre-built template YAML."""
+
+from __future__ import annotations
+import argparse, json, subprocess, sys, tempfile, re
+from pathlib import Path
+
+ROOT = Path(__file__).resolve().parents[1]
+TEMPLATES_DIR = ROOT / "templates" / "prebuilt"
+
+def validate(data: dict, schema: dict) -> list[str]:
+ errors = []
+ for key, spec in schema.items():
+ if spec.get("required") and key not in data:
+ errors.append(f"Missing required field: {key}")
+ if key in data:
+ if spec["type"] == "array" and not isinstance(data[key], list):
+ errors.append(f"{key} must be an array")
+ if spec["type"] == "integer" and not isinstance(data[key], int):
+ errors.append(f"{key} must be an integer")
+ return errors
+
+def generate(artifact_path: Path, template_data: dict) -> str:
+ """Generate HTML from template data. Returns artifact path."""
+ title = template_data.get("title", "Generated Artifact")
+ options = template_data.get("options", [])
+ recommendation = template_data.get("recommendation", "")
+ next_action = template_data.get("next_action", "")
+
+ rows = ""
+ for opt in options:
+ risk_list = "
" + "".join(f"- {r}
" for r in opt.get("risks", [])) + "
"
+ evidence_list = "" + "".join(f"- {e}
" for e in opt.get("evidence", [])) + "
"
+ rows += f"""
+ | {opt.get("name", "")} |
+ {opt.get("score", 0)} |
+ {risk_list} |
+ {evidence_list} |
+
"""
+
+ html = f"""
+
+
+
+
+{title}
+
+
+
+
+
+
+| Option | Score | Risks | Evidence |
+{rows}
+
+
+Recommendation: {recommendation}
+
+
+Next action: {next_action}
+
+
+"""
+
+ out = artifact_path or Path(tempfile.mktemp(suffix=".html", prefix="template-"))
+ out.write_text(html)
+ return out
+
+def main() -> int:
+ parser = argparse.ArgumentParser(description="Generate artifact from pre-built template YAML")
+ parser.add_argument("action", choices=["generate", "validate", "list-templates"])
+ parser.add_argument("template", nargs="?", help="Template name (e.g. decision-deck)")
+ parser.add_argument("data", nargs="?", help="Path to data YAML file")
+ args = parser.parse_args()
+
+ if args.action == "list-templates":
+ for f in sorted(TEMPLATES_DIR.glob("*.yaml")):
+ data = json.loads(f.read_text())
+ print(f" {data[\"name\"]}: {data[\"description\"]}")
+ return 0
+
+ if args.action == "validate":
+ if not args.template or not args.data:
+ print("Usage: generate-template.py validate ")
+ return 1
+ tmpl_path = TEMPLATES_DIR / f"{args.template}.yaml"
+ if not tmpl_path.exists():
+ print(f"Template not found: {args.template}")
+ return 1
+ tmpl = json.loads(tmpl_path.read_text())
+ data = json.loads(Path(args.data).read_text())
+ errors = validate(data, tmpl.get("schema", {}))
+ if errors:
+ for e in errors: print(f"ERROR: {e}")
+ return 1
+ print("Validation passed")
+ return 0
+
+ if args.action == "generate":
+ if not args.template or not args.data:
+ print("Usage: generate-template.py generate ")
+ return 1
+ tmpl_path = TEMPLATES_DIR / f"{args.template}.yaml"
+ if not tmpl_path.exists():
+ print(f"Template not found: {args.template}")
+ return 1
+ tmpl = json.loads(tmpl_path.read_text())
+ data = json.loads(Path(args.data).read_text())
+ errors = validate(data, tmpl.get("schema", {}))
+ if errors:
+ for e in errors: print(f"ERROR: {e}")
+ return 1
+ out_path = generate(None, data)
+ # Run deliver-artifact.py
+ result = subprocess.run([sys.executable, str(ROOT/"scripts/deliver-artifact.py"),
+ str(out_path), "--json"], capture_output=True, text=True)
+ score = json.loads(result.stdout)["score"] if result.returncode == 0 else 0
+ print(f"Generated: {out_path} (score: {score}/100)")
+ return 0
+
+ return 0
+
+if __name__ == "__main__":
+ raise SystemExit(main())
\ No newline at end of file
diff --git a/scripts/validate-templates.sh b/scripts/validate-templates.sh
new file mode 100644
index 0000000..6826163
--- /dev/null
+++ b/scripts/validate-templates.sh
@@ -0,0 +1,17 @@
+#!/bin/bash
+# Validate all pre-built templates
+set -e
+SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
+ROOT="$(dirname "$SCRIPT_DIR")"
+TEMPLATES="$ROOT/templates/prebuilt"
+
+echo "Validating pre-built templates..."
+
+for yaml in "$TEMPLATES"/*.yaml; do
+ name=$(basename "$yaml" .yaml)
+ echo "Checking: $name"
+ # Verify template has required schema fields
+ python3 -c "import json,sys; d=json.load(open('$yaml')); assert 'name' in d; assert 'schema' in d; print(f' OK: {d[\"name\"]} - {d[\"description\"]}')"
+done
+
+echo "All templates validated."
\ No newline at end of file
diff --git a/templates/prebuilt/architecture-review.yaml b/templates/prebuilt/architecture-review.yaml
new file mode 100644
index 0000000..7ad7ff4
--- /dev/null
+++ b/templates/prebuilt/architecture-review.yaml
@@ -0,0 +1,46 @@
+name: architecture-review
+version: "1.0"
+description: "System or module architecture with connections, boundaries, and trade-offs"
+
+input_schema:
+ title:
+ type: string
+ required: true
+ components:
+ type: array
+ required: true
+ items: string
+ connections:
+ type: array
+ required: true
+ items: string
+ trade_offs:
+ type: array
+ required: true
+ items: string
+ next_action:
+ type: string
+ required: true
+
+example:
+ title: "E-commerce Platform Architecture v2"
+ components:
+ - "Frontend (React, CDN-distributed)"
+ - "API Gateway (Kong, handles auth/rate-limit)"
+ - "Order Service (Node.js, event-driven)"
+ - "Inventory Service (Go, CRDT-based)"
+ - "Payment Service (External, PCI-DSS compliant)"
+ - "Postgres (primary DB, async replication)"
+ - "Redis (cache + session store)"
+ connections:
+ - "Frontend -> API Gateway (HTTPS, REST)"
+ - "API Gateway -> Order Service (HTTP, REST)"
+ - "Order Service -> Inventory Service (Event bus: Kafka)"
+ - "Order Service -> Payment Service (gRPC, sync)"
+ - "Order Service -> Postgres (SQL)"
+ - "All services -> Redis (caching)"
+ trade_offs:
+ - "Event-driven inventory (consistency) vs eventual consistency model"
+ - "Microservices (scalability) vs operational complexity"
+ - "PCI-DSS external payment (compliance) vs vendor lock-in risk"
+ next_action: "Add integration tests for Order -> Inventory event flow before Q3 launch"
\ No newline at end of file
diff --git a/templates/prebuilt/audit-report.yaml b/templates/prebuilt/audit-report.yaml
new file mode 100644
index 0000000..3234488
--- /dev/null
+++ b/templates/prebuilt/audit-report.yaml
@@ -0,0 +1,38 @@
+name: audit-report
+version: "1.0"
+description: "Security or process audit with findings, severity levels, and recommended actions"
+
+input_schema:
+ title:
+ type: string
+ required: true
+ scope:
+ type: string
+ required: true
+ findings:
+ type: array
+ required: true
+ items: string
+ severity:
+ type: string
+ required: true
+ description: "Overall severity: critical/high/medium/low"
+ recommended_actions:
+ type: array
+ required: true
+ items: string
+
+example:
+ title: "Q2 Security Audit: Authentication System"
+ scope: "Auth service, token storage, session management, OAuth flows"
+ findings:
+ - "Refresh token rotation not implemented - tokens valid for 30 days without rotation"
+ - "Session invalidation endpoint missing - no way to revoke sessions on password change"
+ - "Rate limiting on /auth endpoint is 1000 req/min per IP - no per-user limiting"
+ - "Token storage uses local filesystem - not suitable for multi-instance deployments"
+ severity: "high"
+ recommended_actions:
+ - "Implement refresh token rotation within 2 weeks (CRITICAL)"
+ - "Add session invalidation endpoint before next release"
+ - "Add per-user rate limiting as defense-in-depth"
+ - "Move token storage to Redis for multi-instance support"
\ No newline at end of file
diff --git a/templates/prebuilt/decision-deck.yaml b/templates/prebuilt/decision-deck.yaml
new file mode 100644
index 0000000..e6f8349
--- /dev/null
+++ b/templates/prebuilt/decision-deck.yaml
@@ -0,0 +1,49 @@
+name: decision-deck
+version: "1.0"
+description: "Compare 2-4 options with scores, risks, evidence, and a recommendation"
+
+input_schema:
+ title:
+ type: string
+ required: true
+ description: "Decision question or title"
+ options:
+ type: array
+ required: true
+ min_items: 2
+ max_items: 4
+ items:
+ name: string
+ score: integer
+ risks: list
+ evidence: list
+ recommendation:
+ type: string
+ required: true
+ description: "Recommended option with rationale"
+ next_action:
+ type: string
+ required: true
+ description: "What to do next"
+
+example:
+ title: "Which deployment strategy for the new API gateway?"
+ options:
+ - name: "Blue-green"
+ score: 88
+ risks:
+ - "Cost of standby environment"
+ - "Traffic routing complexity"
+ evidence:
+ - "Industry standard for zero-downtime"
+ - "Netflix uses this"
+ - name: "Canary"
+ score: 92
+ risks:
+ - "Requires traffic splitting infrastructure"
+ - "Harder to roll back quickly"
+ evidence:
+ - "GitHub uses canary"
+ - "Lower blast radius on failure"
+ recommendation: "Use canary - lower risk, better observability, fits our infra"
+ next_action: "Create infra ticket for traffic splitting, estimate 2 weeks"
\ No newline at end of file
diff --git a/templates/prebuilt/diff-review.yaml b/templates/prebuilt/diff-review.yaml
new file mode 100644
index 0000000..b480384
--- /dev/null
+++ b/templates/prebuilt/diff-review.yaml
@@ -0,0 +1,45 @@
+name: diff-review
+version: "1.0"
+description: "PR or change review with annotated files, inline comments, and approval signals"
+
+input_schema:
+ title:
+ type: string
+ required: true
+ description: "PR title or change description"
+ pr_number:
+ type: integer
+ required: true
+ description: "Pull request or issue number"
+ files_changed:
+ type: array
+ required: true
+ description: "List of files modified"
+ items: string
+ summary:
+ type: string
+ required: true
+ description: "What this change does"
+ risks:
+ type: array
+ required: true
+ description: "Potential risks or concerns"
+ items: string
+ approval:
+ type: string
+ required: true
+ description: "Approval recommendation"
+
+example:
+ title: "Refactor auth module to support OAuth 2.0"
+ pr_number: 142
+ files_changed:
+ - "src/auth/index.ts"
+ - "src/auth/oauth.ts"
+ - "src/auth/token-store.ts"
+ - "tests/auth/oauth.test.ts"
+ summary: "Replaces legacy session-based auth with OAuth 2.0 + refresh token rotation. Maintains backward compatibility via feature flag."
+ risks:
+ - "Token rotation logic is complex - edge cases around concurrent refresh requests"
+ - "Session invalidation may affect existing logged-in users during rollout"
+ approval: "LGTM with conditions: (1) Add integration test for concurrent refresh, (2) Staged rollout via feature flag, (3) Monitor error rate for first 24h"
\ No newline at end of file
diff --git a/templates/prebuilt/project-recap.yaml b/templates/prebuilt/project-recap.yaml
new file mode 100644
index 0000000..cdb62c7
--- /dev/null
+++ b/templates/prebuilt/project-recap.yaml
@@ -0,0 +1,47 @@
+name: project-recap
+version: "1.0"
+description: "Sprint or milestone recap with progress, blockers, and next steps"
+
+input_schema:
+ title:
+ type: string
+ required: true
+ description: "Project or team name"
+ sprint:
+ type: string
+ required: true
+ description: "Sprint or time period"
+ completed:
+ type: array
+ required: true
+ description: "Things completed this period"
+ items: string
+ blocked:
+ type: array
+ required: true
+ description: "Things blocked or at risk"
+ items: string
+ next_steps:
+ type: array
+ required: true
+ description: "Next actions for next period"
+ items: string
+ team:
+ type: string
+ required: false
+ description: "Team or team members"
+
+example:
+ title: "Platform Engineering Q2 Recap"
+ sprint: "Sprint 14-15 (June 2026)"
+ completed:
+ - "Migrated auth service to OAuth 2.0"
+ - "Reduced API p99 latency from 450ms to 120ms"
+ - "Deployed feature flag infrastructure"
+ blocked:
+ - "Database migration blocked by vendor dependency (ETA: July)"
+ - "Mobile SDK v3 delayed by iOS signing certificate issues"
+ next_steps:
+ - "Complete database migration in Sprint 16"
+ - "Ship mobile SDK v3 by July 15"
+ team: "Platform Engineering (5 engineers)"
\ No newline at end of file