Skill Tracer
Skills › Scientific Agent Skills › peer-review › generate_review_scaffold.py

generate_review_scaffold.py

81 lines · skills/peer-review/scripts/generate_review_scaffold.py at 5f2bc03

1#!/usr/bin/env python3
2"""Generate a local structured peer-review draft scaffold from validated intake."""
3
4from __future__ import annotations
5
6import argparse
7import re
8from pathlib import Path
9from typing import Any
11from _common import (
12 ValidationError,
13 error_exit,
14 read_markdown,
15 write_markdown,
17from validate_review_intake import validate_intake
19TEMPLATE_PATH = (
20 Path(__file__).resolve().parents[1] / "assets" / "review_scaffold_template.md"
22PLACEHOLDER_RE = re.compile(r"\{\{[A-Z0-9_]+\}\}")
25def generate(payload: Any, template_path: Path = TEMPLATE_PATH) -> str:
26 """Render only safe intake identifiers; never interpolate manuscript prose."""
27 report = validate_intake(payload)
28 if not report["valid"]:
29 codes = sorted({item["code"] for item in report["errors"]})
30 raise ValidationError(
31 "intake is blocked; resolve these controls first: " + ", ".join(codes)
32 )
33 template = read_markdown(template_path)
34 replacements = {
35 "{{REVIEW_ID}}": report["review_id"],
36 "{{REVIEWER_CAPACITY}}": report["normalized_scope"]["capacity"],
37 "{{PEER_REVIEW_MODEL}}": report["normalized_scope"]["peer_review_model"],
38 "{{AI_PLAN}}": report["normalized_scope"]["ai_plan"],
39 }
40 rendered = template
41 for placeholder, value in replacements.items():
42 rendered = rendered.replace(placeholder, value)
43 unresolved = sorted(set(PLACEHOLDER_RE.findall(rendered)))
44 if unresolved:
45 raise ValidationError(
46 "scaffold template contains unresolved placeholders: "
47 + ", ".join(unresolved)
48 )
49 return rendered
52def build_parser() -> argparse.ArgumentParser:
53 parser = argparse.ArgumentParser(
54 description=(
55 "Generate a private local Markdown review scaffold after intake "
56 "authorization and conflict controls pass."
57 )
58 )
59 parser.add_argument("intake", help="Local review intake JSON")
60 parser.add_argument("-o", "--output", required=True, help="Output Markdown path")
61 parser.add_argument(
62 "--force", action="store_true", help="Replace an existing output file"
63 )
64 return parser
67def main() -> int:
68 args = build_parser().parse_args()
69 try:
70 from _common import read_json
72 rendered = generate(read_json(args.intake))
73 destination = write_markdown(rendered, args.output, force=args.force)
74 print(f"Created local review scaffold: {destination}")
75 return 0
76 except ValidationError as exc:
77 return error_exit(exc)
80if __name__ == "__main__":
81 raise SystemExit(main())