#!/usr/bin/env python3 import argparse import html import json import os from pathlib import Path from typing import Any try: import yaml except ImportError: # pragma: no cover yaml = None ROOT = Path(__file__).resolve().parent.parent def load_json(path: Path) -> dict[str, Any]: if not path.exists(): return {} try: payload = json.loads(path.read_text(encoding="utf-8")) except json.JSONDecodeError: return {} return payload if isinstance(payload, dict) else {} def load_yaml(path: Path) -> dict[str, Any]: if not path.exists() or yaml is None: return {} payload = yaml.safe_load(path.read_text(encoding="utf-8")) or {} return payload if isinstance(payload, dict) else {} def parse_frontmatter(path: Path) -> dict[str, Any]: if not path.exists(): return {} lines = path.read_text(encoding="utf-8", errors="replace").splitlines() if not lines or lines[0].strip() != "---": return {} try: end_index = lines[1:].index("---") + 1 except ValueError: return {} text = "\n".join(lines[1:end_index]) if yaml is not None: payload = yaml.safe_load(text) or {} return payload if isinstance(payload, dict) else {} data = {} for line in text.splitlines(): if ":" in line: key, value = line.split(":", 1) data[key.strip()] = value.strip().strip('"') return data def display_path(skill_dir: Path, path: Path) -> str: try: return str(path.resolve().relative_to(skill_dir.resolve())) except ValueError: try: return str(path.resolve().relative_to(ROOT.resolve())) except ValueError: return str(path.resolve()) def link_from(output_html: Path, target: Path) -> str: return os.path.relpath(target.resolve(), output_html.parent.resolve()) def report_link(output_html: Path, skill_dir: Path, rel_path: str) -> str: return link_from(output_html, skill_dir / rel_path) def gate(key: str, label: str, status: str, detail: str, evidence: str, link: str = "") -> dict[str, str]: return { "key": key, "label": label, "status": status, "detail": detail, "evidence": evidence, "link": link, } def status_label(status: str) -> str: return {"pass": "通过", "warn": "关注", "block": "阻断"}.get(status, status) def add_blockers_from_gate(gates: list[dict[str, str]]) -> tuple[list[dict[str, str]], list[dict[str, str]]]: blockers = [item for item in gates if item["status"] == "block"] warnings = [item for item in gates if item["status"] == "warn"] return blockers, warnings def target_maturity(skill_dir: Path, overview: dict[str, Any]) -> str: manifest = load_json(skill_dir / "manifest.json") if manifest.get("maturity_tier"): return str(manifest["maturity_tier"]) metadata = overview.get("metadata", {}) if isinstance(overview, dict) else {} if metadata.get("maturity_tier"): return str(metadata["maturity_tier"]) return "scaffold" def min_output_cases(maturity: str) -> int: if maturity in {"library", "governed"}: return 5 if maturity == "production": return 3 return 1 def build_gates(skill_dir: Path, output_html: Path, data: dict[str, dict[str, Any]]) -> list[dict[str, str]]: overview = data["overview"] maturity = target_maturity(skill_dir, overview) gates: list[dict[str, str]] = [] intent = data["intent_confidence"] intent_score = int(intent.get("score", 0) or 0) intent_status = "pass" if intent.get("gate_passed") or intent_score >= 75 else "warn" gates.append( gate( "intent-canvas", "意图画布", intent_status, f"intent confidence {intent_score}/100; {intent.get('recommended_action', 'review current intent frame')}", "reports/intent-confidence.json", report_link(output_html, skill_dir, "reports/intent-confidence.md"), ) ) route = data["route_scorecard"] route_summary = route.get("summary", {}) misroutes = int(route_summary.get("misroute_count", len(route.get("misroutes", []))) or 0) ambiguous = int(route_summary.get("ambiguous_case_count", len(route.get("ambiguous_cases", []))) or 0) if not route: route_status = "warn" route_detail = "route scorecard is missing; run route-scorecard before release review" else: route_status = "block" if misroutes else ("warn" if ambiguous else "pass") route_detail = f"{route_summary.get('total_cases', 0)} trigger cases; {misroutes} misroutes; {ambiguous} ambiguous" gates.append( gate( "trigger-lab", "触发实验", route_status, route_detail, "reports/route_scorecard.json", report_link(output_html, skill_dir, "reports/route_scorecard.md"), ) ) output = data["output_quality"] output_summary = output.get("summary", {}) required_cases = min_output_cases(maturity) case_count = int(output_summary.get("case_count", 0) or 0) file_backed = int(output_summary.get("file_backed_case_count", 0) or 0) near_neighbor = int(output_summary.get("near_neighbor_case_count", 0) or 0) boundary = int(output_summary.get("boundary_case_count", 0) or 0) output_blocked = not output.get("ok", False) or not output_summary.get("gate_pass", False) or case_count < required_cases output_warn = file_backed == 0 or near_neighbor == 0 or boundary == 0 if not output: output_status = "warn" output_detail = "output eval scorecard is missing; generate it before production review" else: output_status = "block" if output_blocked else ("warn" if output_warn else "pass") output_detail = ( f"{case_count}/{required_cases} cases; with-skill {output_summary.get('with_skill_pass_rate', 0)}; " f"baseline {output_summary.get('baseline_pass_rate', 0)}; file-backed {file_backed}; near-neighbor {near_neighbor}" ) gates.append( gate( "output-lab", "输出实验", output_status, output_detail, "reports/output_quality_scorecard.json", report_link(output_html, skill_dir, "reports/output_quality_scorecard.md"), ) ) context = data["context_budget"] context_stats = context.get("stats", {}) context_status = "pass" if context.get("ok") else "block" if context.get("warnings"): context_status = "warn" if context_status == "pass" else context_status if not context: context_status = "warn" context_detail = ( f"initial load {context_stats.get('estimated_initial_load_tokens', 'n/a')}/" f"{context_stats.get('context_budget_limit', 'n/a')}; quality density {context_stats.get('quality_density', 'n/a')}" ) gates.append( gate( "context-budget", "上下文", context_status, context_detail, "reports/context_budget.json", report_link(output_html, skill_dir, "reports/context_budget.md"), ) ) conformance = data["conformance"] conformance_summary = conformance.get("summary", {}) fail_count = int(conformance_summary.get("fail_count", 0) or 0) if not conformance: conformance_status = "warn" conformance_detail = "runtime conformance matrix is missing" else: conformance_status = "block" if fail_count else "pass" conformance_detail = f"{conformance_summary.get('pass_count', 0)} / {conformance_summary.get('target_count', 0)} targets pass" gates.append( gate( "runtime-matrix", "运行矩阵", conformance_status, conformance_detail, "reports/conformance_matrix.json", report_link(output_html, skill_dir, "reports/conformance_matrix.md"), ) ) trust = data["trust"] trust_summary = trust.get("summary", {}) if not trust: trust_status = "warn" trust_detail = "security trust report is missing" else: trust_status = "block" if trust.get("failures") else ("warn" if trust.get("warnings") else "pass") trust_detail = ( f"{trust_summary.get('secret_findings', 0)} secrets; " f"{trust_summary.get('script_count', 0)} scripts; " f"{trust_summary.get('network_script_count', 0)} network-capable scripts" ) gates.append( gate( "trust-report", "信任报告", trust_status, trust_detail, "reports/security_trust_report.json", report_link(output_html, skill_dir, "reports/security_trust_report.md"), ) ) atlas = data["atlas"] atlas_summary = atlas.get("summary", {}) atlas_issues = int(atlas_summary.get("route_collision_count", 0) or 0) + int(atlas_summary.get("owner_gap_count", 0) or 0) if not atlas: atlas_status = "warn" atlas_detail = "skill atlas is missing; portfolio-level conflicts are unknown" else: atlas_status = "warn" if atlas_issues else "pass" atlas_detail = ( f"{atlas_summary.get('skill_count', 0)} skills; " f"{atlas_summary.get('route_collision_count', 0)} route collisions; " f"{atlas_summary.get('owner_gap_count', 0)} owner gaps; " f"{atlas_summary.get('stale_count', 0)} stale" ) gates.append( gate( "skill-atlas", "组合治理", atlas_status, atlas_detail, "reports/skill_atlas.json", report_link(output_html, skill_dir, "reports/skill_atlas.html"), ) ) promotion = data["promotion"] migration_path = ROOT / "docs" / "migration-v2.md" if promotion: promotion_summary = promotion.get("summary", {}) blocked = int(promotion_summary.get("blocked", 0) or 0) release_status = "block" if blocked else "pass" release_detail = f"{promotion_summary.get('promote', 0)} promote; {promotion_summary.get('keep_current', 0)} keep current; {blocked} blocked" else: release_status = "warn" release_detail = "promotion decisions are missing; release notes need reviewer confirmation" gates.append( gate( "release-notes", "发布路线", release_status, release_detail, "reports/promotion_decisions.json + docs/migration-v2.md", report_link(output_html, skill_dir, "reports/promotion_decisions.md") if promotion else str(migration_path), ) ) return gates def weighted_score(gates: list[dict[str, str]]) -> int: weights = { "trigger-lab": 15, "output-lab": 20, "context-budget": 10, "runtime-matrix": 10, "trust-report": 10, "skill-atlas": 10, "release-notes": 10, "intent-canvas": 10, } earned = 0.0 total = 0.0 for item in gates: weight = weights.get(item["key"], 5) total += weight if item["status"] == "pass": earned += weight elif item["status"] == "warn": earned += weight * 0.6 return int(round(earned / total * 100)) if total else 0 def evidence_paths(skill_dir: Path) -> dict[str, str]: rels = { "skill_overview": "reports/skill-overview.html", "review_viewer": "reports/review-viewer.html", "output_eval": "reports/output_quality_scorecard.md", "runtime_conformance": "reports/conformance_matrix.md", "trust_report": "reports/security_trust_report.md", "skill_atlas": "reports/skill_atlas.html", "migration": "docs/migration-v2.md", "skill_ir": "reports/skill-ir.json", } return {key: rel for key, rel in rels.items() if (skill_dir / rel).exists() or (ROOT / rel).exists()} def load_review_data(skill_dir: Path) -> dict[str, dict[str, Any]]: reports = skill_dir / "reports" return { "overview": load_json(reports / "skill-overview.json"), "intent_confidence": load_json(reports / "intent-confidence.json"), "intent_dialogue": load_json(reports / "intent-dialogue.json"), "route_scorecard": load_json(reports / "route_scorecard.json"), "output_quality": load_json(reports / "output_quality_scorecard.json"), "conformance": load_json(reports / "conformance_matrix.json"), "trust": load_json(reports / "security_trust_report.json"), "context_budget": load_json(reports / "context_budget.json"), "promotion": load_json(reports / "promotion_decisions.json"), "atlas": load_json(reports / "skill_atlas.json"), "manifest": load_json(skill_dir / "manifest.json"), "frontmatter": parse_frontmatter(skill_dir / "SKILL.md"), "interface": load_yaml(skill_dir / "agents" / "interface.yaml"), } def insight_cards(data: dict[str, dict[str, Any]]) -> list[dict[str, str]]: overview = data["overview"] output = data["output_quality"].get("summary", {}) conformance = data["conformance"].get("summary", {}) trust = data["trust"].get("summary", {}) atlas = data["atlas"].get("summary", {}) cards = [ { "label": "Skill IR", "value": str(overview.get("skill_ir", {}).get("schema_version", "missing")), "detail": f"{overview.get('skill_ir', {}).get('target_count', 0)} targets in platform-neutral contract", }, { "label": "Output Delta", "value": str(output.get("delta", "n/a")), "detail": f"{output.get('case_count', 0)} cases; {output.get('file_backed_case_count', 0)} file-backed", }, { "label": "Runtime", "value": f"{conformance.get('pass_count', 0)}/{conformance.get('target_count', 0)}", "detail": "target conformance pass rate", }, { "label": "Trust", "value": str(trust.get("secret_findings", 0)), "detail": f"{trust.get('script_count', 0)} scripts scanned; secrets found", }, { "label": "Atlas", "value": str(atlas.get("route_collision_count", 0)), "detail": f"{atlas.get('skill_count', 0)} scanned skills; route collisions", }, ] return cards def render_gate_list(gates: list[dict[str, str]]) -> str: items = [] for item in gates: link_html = f"证据" if item.get("link") else "" items.append( "
" f"
{html.escape(status_label(item['status']))}

{html.escape(item['label'])}

" f"

{html.escape(item['detail'])}

" f"" "
" ) return "".join(items) def render_insights(cards: list[dict[str, str]]) -> str: return "".join( ( "
" f"{html.escape(item['label'])}" f"{html.escape(item['value'])}" f"

{html.escape(item['detail'])}

" "
" ) for item in cards ) def render_issue_list(title: str, items: list[dict[str, str]]) -> str: if not items: return f"

{html.escape(title)}

无。

" body = "".join( ( "
  • " f"{html.escape(item['label'])}" f"{html.escape(item['detail'])}" "
  • " ) for item in items ) return f"

    {html.escape(title)}

    " def render_html(report: dict[str, Any]) -> str: summary = report["summary"] gates = report["gates"] blockers = report["blockers"] warnings = report["warnings"] insights = insight_cards(report["data"]) overview = report["data"]["overview"] manifest = report["data"]["manifest"] frontmatter = report["data"]["frontmatter"] title = overview.get("display_name") or overview.get("title") or frontmatter.get("name") or manifest.get("name") or "Skill" description = overview.get("description") or frontmatter.get("description", "") nav = [ ("#overview", "审查总览"), ("#intent", "意图画布"), ("#trigger", "触发实验"), ("#output", "输出实验"), ("#runtime", "运行矩阵"), ("#trust", "信任报告"), ("#atlas", "组合治理"), ("#release", "发布路线"), ] nav_html = "".join(f"{label}" for href, label in nav) gates_html = render_gate_list(gates) metrics_html = render_insights(insights) blockers_html = render_issue_list("阻断事项", blockers) warnings_html = render_issue_list("关注事项", warnings) output_summary = report["data"]["output_quality"].get("summary", {}) conformance_summary = report["data"]["conformance"].get("summary", {}) trust_summary = report["data"]["trust"].get("summary", {}) atlas_summary = report["data"]["atlas"].get("summary", {}) evidence_html = "".join( f"
  • {html.escape(key)}{html.escape(value)}
  • " for key, value in report["evidence_paths"].items() ) return f""" {html.escape(str(title))} Review Studio 2.0
    Review Studio 2.0

    {html.escape(str(title))}

    {html.escape(str(description))}

    审查结论 {html.escape(summary['decision'])} Score {html.escape(str(summary['world_class_score']))}/100

    核心指标

    {metrics_html}

    审查闸门

    {gates_html}
    {blockers_html} {warnings_html}

    意图画布

    {html.escape(str(report['data']['intent_confidence'].get('anchor_sentence', description)))}

    证据路径

      {evidence_html}

    触发实验

    {html.escape(gates[1]['detail'])}

    组合治理

    {html.escape(str(atlas_summary))}

    输出实验

    {html.escape(str(output_summary))}

    发布标准

    Governed 和 Library 至少需要 5 个 output eval cases,并覆盖 file-backed、near-neighbor 和 boundary case。

    运行矩阵

    {html.escape(str(conformance_summary))}

    上下文

    {html.escape(gates[3]['detail'])}

    信任报告

    {html.escape(str(trust_summary))}

    安全边界

    高风险 secret、远程 inline execution、缺失依赖策略或无法解释的脚本接口应阻断 governed release。

    组合治理

    {html.escape(gates[6]['detail'])}

    下一动作

    优先处理真实 portfolio 中的 duplicate names、stale skills、owner gaps,再做 registry 或 telemetry。

    发布路线

    {html.escape(gates[7]['detail'])}

    世界级缺口

    下一阶段应继续推进 IR-first compiler、registry audit、telemetry drift loop,以及更严格的 governed trust gates。

    """ def render_review_studio(skill_dir: Path, output_html: Path | None = None, output_json: Path | None = None) -> dict[str, Any]: skill_dir = skill_dir.resolve() reports_dir = skill_dir / "reports" reports_dir.mkdir(parents=True, exist_ok=True) output_html = output_html or reports_dir / "review-studio.html" output_json = output_json or reports_dir / "review-studio.json" data = load_review_data(skill_dir) gates = build_gates(skill_dir, output_html, data) blockers, warnings = add_blockers_from_gate(gates) score = weighted_score(gates) decision = "blocked" if blockers else ("review" if warnings else "ready") report = { "schema_version": "2.0", "ok": True, "skill_dir": display_path(skill_dir, skill_dir), "summary": { "decision": decision, "world_class_score": score, "gate_count": len(gates), "blocker_count": len(blockers), "warning_count": len(warnings), }, "gates": gates, "blockers": blockers, "warnings": warnings, "evidence_paths": evidence_paths(skill_dir), "data": data, "artifacts": { "html": display_path(skill_dir, output_html), "json": display_path(skill_dir, output_json), }, } output_html.write_text(render_html(report), encoding="utf-8") output_json.write_text(json.dumps(report, ensure_ascii=False, indent=2) + "\n", encoding="utf-8") return {key: value for key, value in report.items() if key != "data"} def main() -> None: parser = argparse.ArgumentParser(description="Render Review Studio 2.0 for a skill package.") parser.add_argument("skill_dir", nargs="?", default=".") parser.add_argument("--output-html") parser.add_argument("--output-json") args = parser.parse_args() payload = render_review_studio( Path(args.skill_dir), output_html=Path(args.output_html).resolve() if args.output_html else None, output_json=Path(args.output_json).resolve() if args.output_json else None, ) print(json.dumps(payload, ensure_ascii=False, indent=2)) if __name__ == "__main__": main()