Files
yao-meta-skill/scripts/review_studio_actions.py
T
2026-06-15 17:42:51 +08:00

343 lines
22 KiB
Python

import html
import os
from pathlib import Path
from typing import Any
from review_studio_action_evidence import render_action_evidence_steps, world_class_action_steps
from review_studio_gates import status_label
SCRIPT_INTERFACE = "internal-module"
SCRIPT_INTERFACE_REASON = "Imported by render_review_studio.py to keep Review Studio action guidance and source refs out of HTML rendering."
def link_from(output_html: Path, target: Path) -> str:
return os.path.relpath(target.resolve(), output_html.parent.resolve())
def find_line(path: Path, patterns: list[str] | None = None) -> int | None:
if not path.exists():
return None
if not patterns:
return 1
try:
lines = path.read_text(encoding="utf-8", errors="replace").splitlines()
except OSError:
return 1
for pattern in patterns:
for index, line in enumerate(lines, start=1):
if pattern in line:
return index
return 1
def source_refs(
skill_dir: Path,
output_html: Path,
specs: list[dict[str, Any]],
) -> list[dict[str, Any]]:
refs: list[dict[str, Any]] = []
for spec in specs:
rel_path = str(spec.get("path", "")).strip()
if not rel_path:
continue
path = skill_dir / rel_path
exists = path.exists()
line = find_line(path, spec.get("patterns", []))
refs.append(
{
"path": rel_path,
"label": str(spec.get("label", rel_path)),
"kind": str(spec.get("kind", "source")),
"line": line,
"exists": exists,
"link": link_from(output_html, path) if exists else "",
}
)
return refs
ACTION_GUIDANCE: dict[str, dict[str, Any]] = {
"intent-canvas": {
"summary": "收紧真实任务、输入、输出、排除项和成功标准。",
"why": "低 intent confidence 会让后续 Skill IR、输出评测和 Review Studio 结论建立在模糊意图上。",
"source_fix": "reports/intent-dialogue.md + reports/intent-confidence.md",
"source_paths": [
{"path": "reports/intent-dialogue.md", "label": "intent dialogue", "kind": "report", "patterns": ["# Intent"]},
{"path": "reports/intent-confidence.md", "label": "intent confidence", "kind": "report", "patterns": ["# Intent"]},
],
"verification": "python3 scripts/yao.py intent-confidence .",
},
"trigger-lab": {
"summary": "修正 route scorecard 中的误触发、漏触发或 ambiguous case。",
"why": "触发错误会让正确 Skill 失活,或让相邻 Skill 被错误调用。",
"source_fix": "SKILL.md frontmatter description + evals/*/trigger_cases.json",
"source_paths": [
{"path": "SKILL.md", "label": "frontmatter description", "kind": "source", "patterns": ["description:"]},
{"path": "evals/trigger_cases.json", "label": "trigger eval cases", "kind": "eval", "patterns": ["should_trigger"]},
{"path": "reports/route_scorecard.md", "label": "route scorecard", "kind": "report", "patterns": ["# Route"]},
],
"verification": "python3 scripts/build_confusion_matrix.py",
},
"output-lab": {
"summary": "补足 output eval 覆盖、execution evidence、blind A/B 和 reviewer adjudication。",
"why": "没有输出质量和人工盲评证据时,Skill 只能证明会触发,不能证明输出真的更好且经得起审查。",
"source_fix": "evals/output/cases.jsonl + reports/output_quality_scorecard.md + reports/output_review_kit.html + reports/output_review_adjudication.md",
"source_paths": [
{"path": "evals/output/cases.jsonl", "label": "output eval cases", "kind": "eval", "patterns": ["case_id"]},
{"path": "reports/output_quality_scorecard.md", "label": "output scorecard", "kind": "report", "patterns": ["# Output"]},
{"path": "reports/output_execution_runs.md", "label": "output execution runs", "kind": "report", "patterns": ["# Output Execution"]},
{"path": "reports/output_blind_review_pack.md", "label": "blind A/B review pack", "kind": "report", "patterns": ["# Output Blind"]},
{"path": "reports/output_review_kit.html", "label": "reviewer cockpit", "kind": "report", "patterns": ["Output Review Kit", "Variant A"]},
{"path": "reports/output_review_adjudication.md", "label": "review adjudication", "kind": "report", "patterns": ["# Output Review"]},
],
"verification": "python3 scripts/adjudicate_output_review.py --write-template && python3 scripts/yao.py output-review",
},
"context-budget": {
"summary": "压缩或拆分高成本 deferred resources,保留最小可路由上下文。",
"why": "初始加载可以安全,但后续 references、scripts、evals 体量过大时,reviewer 仍需要看到维护和读取成本。",
"source_fix": "SKILL.md + references/ + scripts/ + evals/",
"source_paths": [
{"path": "SKILL.md", "label": "entrypoint", "kind": "source", "patterns": ["# Yao Meta Skill"]},
{"path": "reports/context_budget.md", "label": "context budget", "kind": "report", "patterns": ["# Context"]},
{"path": "scripts/resource_boundary_check.py", "label": "resource boundary checker", "kind": "source", "patterns": ["DEFERRED_RESOURCE"]},
{"path": "references/skill-engineering-method.md", "label": "skill engineering method", "kind": "method", "patterns": ["Design Principle"]},
],
"verification": "python3 scripts/render_context_reports.py",
},
"runtime-matrix": {
"summary": "修复目标端结构、metadata、相对路径、fallback 或 adapter target 声明。",
"why": "runtime conformance 失败意味着包可能被目标客户端错误加载或静默降级。",
"source_fix": "agents/interface.yaml + reports/conformance_matrix.md",
"source_paths": [
{"path": "agents/interface.yaml", "label": "portable interface", "kind": "source", "patterns": ["adapter_targets"]},
{"path": "reports/conformance_matrix.md", "label": "conformance matrix", "kind": "report", "patterns": ["# Runtime"]},
],
"verification": "python3 scripts/run_conformance_suite.py .",
},
"trust-report": {
"summary": "处理脚本 help surface、依赖 pin、network policy、secret 和权限声明。",
"why": "团队分发时,脚本和依赖是主要供应链风险面,warning 必须有明确处置。",
"source_fix": "reports/security_trust_report.md + security/*.md + scripts/",
"source_paths": [
{"path": "reports/security_trust_report.md", "label": "trust report", "kind": "report", "patterns": ["# Security"]},
{"path": "security/script_policy.md", "label": "script policy", "kind": "policy", "patterns": ["# Script"]},
{"path": "security/network_policy.md", "label": "network policy", "kind": "policy", "patterns": ["# Network"]},
],
"verification": "python3 scripts/trust_check.py .",
},
"python-compat": {
"summary": "修复 Python 3.11 语法兼容问题,尤其是 f-string 表达式内的反斜杠转义。",
"why": "目标运行环境可能仍停留在 Python 3.11,语法漂移会让 CLI、报告生成和 CI 在发布后直接失败。",
"source_fix": "reports/python_compatibility.md + scripts/*.py + tests/*.py",
"source_paths": [
{"path": "reports/python_compatibility.md", "label": "Python compatibility", "kind": "report", "patterns": ["# Python"]},
{"path": "scripts/python_compat_check.py", "label": "compatibility checker", "kind": "source", "patterns": ["SCRIPT_INTERFACE"]},
{"path": ".github/workflows/test.yml", "label": "CI test workflow", "kind": "ci", "patterns": ["python"]},
],
"verification": "python3 scripts/yao.py python-compat .",
},
"architecture-maintainability": {
"summary": "处理大文件和 CLI command surface 的维护性热点,优先拆分稳定职责边界。",
"why": "Meta Skill 的门禁、报告和 CLI 会持续增长;如果不把架构债纳入审查,后续能力会越来越难验证和迁移。",
"source_fix": "reports/architecture_maintainability.md + scripts/yao.py + scripts/render_review_studio.py",
"source_paths": [
{"path": "reports/architecture_maintainability.md", "label": "architecture maintainability", "kind": "report", "patterns": ["# Architecture"]},
{"path": "scripts/yao.py", "label": "Yao CLI orchestrator", "kind": "source", "patterns": ["def command_"]},
{"path": "scripts/render_review_studio.py", "label": "Review Studio renderer", "kind": "source", "patterns": ["def render_html"]},
{"path": "scripts/review_studio_actions.py", "label": "Review Studio actions", "kind": "source", "patterns": ["ACTION_GUIDANCE"]},
{"path": "scripts/render_review_viewer.py", "label": "review viewer renderer", "kind": "source", "patterns": ["def "]},
],
"verification": "python3 scripts/yao.py architecture-audit .",
},
"permission-gates": {
"summary": "补齐高权限能力的 reviewer、scope、reason、expires_at 和目标端 enforcement 说明。",
"why": "权限契约只有在批准人、有效期和目标端处置方式明确时,才能支撑 governed release。",
"source_fix": "security/permission_policy.json + security/permission_policy.md",
"source_paths": [
{"path": "security/permission_policy.json", "label": "permission approvals", "kind": "policy", "patterns": ["approved"]},
{"path": "security/permission_policy.md", "label": "permission method", "kind": "policy", "patterns": ["# Permission"]},
],
"verification": "python3 scripts/trust_check.py .",
},
"permission-runtime": {
"summary": "生成并修复目标包的 runtime permission probe 报告。",
"why": "目标端即使只能提供 metadata fallback,也必须明确 native enforcement 缺口、表示位置和 operator note。",
"source_fix": "dist/targets/*/adapter.json + reports/runtime_permission_probes.md",
"source_paths": [
{"path": "reports/runtime_permission_probes.md", "label": "runtime permission probes", "kind": "report", "patterns": ["# Runtime"]},
{"path": "dist/targets/openai/adapter.json", "label": "OpenAI adapter", "kind": "package", "patterns": ["target_permission_contract"]},
{"path": "dist/targets/claude/adapter.json", "label": "Claude adapter", "kind": "package", "patterns": ["target_permission_contract"]},
{"path": "dist/targets/generic/adapter.json", "label": "generic adapter", "kind": "package", "patterns": ["target_permission_contract"]},
],
"verification": "python3 scripts/probe_runtime_permissions.py . --package-dir dist",
},
"skill-atlas": {
"summary": "处理 portfolio 里的路由冲突、owner 缺口、stale skill 和重复能力。",
"why": "单个 Skill 质量很高仍可能在团队 skill library 中被相邻 Skill 冲突削弱。",
"source_fix": "reports/skill_atlas.html + skill_atlas/catalog.json",
"source_paths": [
{"path": "skill_atlas/catalog.json", "label": "skill atlas catalog", "kind": "atlas", "patterns": ["summary"]},
{"path": "skill_atlas/policy.json", "label": "atlas scope policy", "kind": "policy", "patterns": ["scope"]},
{"path": "reports/skill_atlas.html", "label": "skill atlas report", "kind": "report", "patterns": ["Skill Atlas"]},
],
"verification": "python3 scripts/build_skill_atlas.py --workspace-root .",
},
"operations-loop": {
"summary": "记录 metadata-only 使用事件,或明确当前 release 缺少真实使用信号。",
"why": "没有运营回路时,reviewer 无法判断采用率、误触发、坏输出和 review overdue 的真实影响。",
"source_fix": "reports/adoption_drift_report.md",
"source_paths": [
{"path": "reports/adoption_drift_report.md", "label": "adoption drift report", "kind": "report", "patterns": ["# Adoption"]},
{"path": "references/telemetry-drift-method.md", "label": "telemetry method", "kind": "method", "patterns": ["# Telemetry"]},
],
"verification": "python3 scripts/render_adoption_drift_report.py . --record-event skill_activation --activation-type explicit --outcome accepted",
},
"review-waivers": {
"summary": "对保留的 warning 写入 reviewer、理由、范围和到期时间,或修掉 warning。",
"why": "warning 可以被接受,但必须可审计、会过期,并且不能掩盖 blocker。",
"source_fix": "reports/review_waivers.md",
"source_paths": [
{"path": "reports/review_waivers.md", "label": "waiver ledger", "kind": "report", "patterns": ["# Review"]},
{"path": "references/review-waiver-method.md", "label": "waiver method", "kind": "method", "patterns": ["# Review"]},
],
"verification": "python3 scripts/render_review_waivers.py .",
},
"world-class-evidence": {
"summary": "补齐 provider、真人盲评、原生权限执行和真实客户端遥测证据,或明确本次发布不声明 world-class 完成。",
"why": "世界级结论必须来自已接受的外部/人工证据;计划、metadata fallback、待评审和本地命令都不能替代完成证据。",
"source_fix": "reports/world_class_operator_runbook.html + reports/world_class_evidence_ledger.md + reports/world_class_evidence_intake.md + reports/world_class_submission_review.md",
"source_paths": [
{"path": "reports/world_class_evidence_ledger.md", "label": "world-class evidence ledger", "kind": "report", "patterns": ["# World-Class Evidence Ledger"]},
{"path": "reports/world_class_evidence_plan.md", "label": "world-class evidence plan", "kind": "report", "patterns": ["# World-Class Evidence Plan"]},
{"path": "reports/world_class_evidence_intake.md", "label": "world-class evidence intake", "kind": "report", "patterns": ["# World-Class Evidence Intake"]},
{"path": "reports/world_class_submission_review.md", "label": "world-class submission review", "kind": "report", "patterns": ["# World-Class Submission Review"]},
{"path": "reports/world_class_claim_guard.md", "label": "world-class claim guard", "kind": "report", "patterns": ["# World-Class Claim Guard"]},
{"path": "evidence/world_class/intake.schema.json", "label": "evidence intake schema", "kind": "schema", "patterns": ["Yao World-Class Evidence Intake"]},
{"path": "evidence/world_class/templates/provider-holdout.intake.json", "label": "provider intake template", "kind": "template", "patterns": ["provider-holdout"]},
{"path": "evidence/world_class/templates/human-adjudication.intake.json", "label": "human intake template", "kind": "template", "patterns": ["human-adjudication"]},
{"path": "evidence/world_class/templates/native-permission-enforcement.intake.json", "label": "permission intake template", "kind": "template", "patterns": ["native-permission-enforcement"]},
{"path": "evidence/world_class/templates/native-client-telemetry.intake.json", "label": "telemetry intake template", "kind": "template", "patterns": ["native-client-telemetry"]},
{"path": "reports/skill_os2_audit.md", "label": "Skill OS 2.0 audit", "kind": "report", "patterns": ["# Skill OS"]},
{"path": "reports/output_review_decisions.json", "label": "human review decisions", "kind": "report", "patterns": ["winner_variant"]},
{"path": "reports/runtime_permission_probes.md", "label": "runtime permission probes", "kind": "report", "patterns": ["# Runtime"]},
{"path": "reports/adoption_drift_report.md", "label": "adoption drift", "kind": "report", "patterns": ["# Adoption"]},
],
"verification": (
"python3 scripts/yao.py world-class-runbook . --submissions-dir evidence/world_class/submissions "
"&& python3 scripts/yao.py world-class-ledger . --submissions-dir evidence/world_class/submissions "
"&& python3 scripts/yao.py review-studio ."
),
},
"registry-audit": {
"summary": "补齐 registry package metadata、checksum、license、owner、review cadence 和 install evidence。",
"why": "分发元数据不完整时,团队无法安全安装、升级或追溯包体来源。",
"source_fix": "registry/ + reports/registry_audit.md",
"source_paths": [
{"path": "registry/packages/yao-meta-skill.json", "label": "registry package", "kind": "registry", "patterns": ["version"]},
{"path": "reports/registry_audit.md", "label": "registry audit", "kind": "report", "patterns": ["# Registry"]},
{"path": "reports/install_simulation.md", "label": "install simulation", "kind": "report", "patterns": ["# Install"]},
],
"verification": "python3 scripts/registry_audit.py .",
},
"release-notes": {
"summary": "确认 promotion、upgrade diff、breaking changes、migration guide 和 known limitations。",
"why": "发布说明不完整会让使用者无法判断升级风险和迁移动作。",
"source_fix": "reports/upgrade_check.md + docs/migration-v2.md",
"source_paths": [
{"path": "reports/upgrade_check.md", "label": "upgrade check", "kind": "report", "patterns": ["# Upgrade"]},
{"path": "docs/migration-v2.md", "label": "migration guide", "kind": "docs", "patterns": ["# Migration"]},
{"path": "reports/promotion_decisions.md", "label": "promotion decisions", "kind": "report", "patterns": ["# Promotion"]},
],
"verification": "python3 scripts/upgrade_check.py . --previous-package-json registry/examples/yao-meta-skill-1.0.0.json",
},
}
def build_review_actions(
gates: list[dict[str, str]],
skill_dir: Path,
output_html: Path,
data: dict[str, Any] | None = None,
) -> list[dict[str, Any]]:
actions: list[dict[str, Any]] = []
for gate_item in gates:
if gate_item["status"] == "pass":
continue
guidance = ACTION_GUIDANCE.get(
gate_item["key"],
{
"summary": "打开证据报告,修复当前 gate 暴露的问题。",
"why": "该 gate 未通过,release reviewer 需要明确处置动作。",
"source_fix": gate_item.get("evidence", ""),
"source_paths": [],
"verification": "python3 scripts/render_review_studio.py .",
},
)
refs = source_refs(skill_dir, output_html, guidance.get("source_paths", []))
actions.append(
{
"gate_key": gate_item["key"],
"label": gate_item["label"],
"status": gate_item["status"],
"priority": "blocker" if gate_item["status"] == "block" else "warning",
"summary": guidance["summary"],
"why": guidance["why"],
"source_fix": guidance["source_fix"],
"source_refs": refs,
"evidence": gate_item.get("evidence", ""),
"evidence_link": gate_item.get("link", ""),
"verification_command": guidance["verification"],
"evidence_steps": world_class_action_steps(data or {})
if gate_item["key"] == "world-class-evidence"
else [],
}
)
return actions
def render_action_source_refs(refs: list[dict[str, Any]]) -> str:
if not refs:
return "<p class='muted'>暂无结构化 source refs;请先打开证据报告。</p>"
items = []
for ref in refs:
line_suffix = f":{ref['line']}" if ref.get("line") else ""
label = f"{ref['path']}{line_suffix}"
if ref.get("exists") and ref.get("link"):
path_html = f"<a href='{html.escape(ref['link'])}'>{html.escape(label)}</a>"
else:
path_html = f"<span>{html.escape(label)} · missing</span>"
items.append(
"<li>"
f"{path_html}"
f"<small>{html.escape(ref.get('label', 'source'))} · {html.escape(ref.get('kind', 'source'))}</small>"
"</li>"
)
return "<ul class='source-ref-list'>" + "".join(items) + "</ul>"
def render_review_actions(actions: list[dict[str, Any]]) -> str:
if not actions:
return "<p class='muted'>当前没有 blocker 或 warning。保持现有证据链即可。</p>"
cards = []
for item in actions:
link_html = f"<a href='{html.escape(item['evidence_link'])}'>打开证据</a>" if item.get("evidence_link") else ""
source_refs_html = render_action_source_refs(item.get("source_refs", []))
evidence_steps_html = render_action_evidence_steps(item.get("evidence_steps", []))
card_class = "action-card " + html.escape(item["status"])
if item.get("evidence_steps"):
card_class += " with-evidence"
cards.append(
"<article class='" + card_class + "'>"
f"<div><span>{html.escape(status_label(item['status']))}</span><h3>{html.escape(item['label'])}</h3></div>"
f"<p>{html.escape(item['summary'])}</p>"
f"<small>{html.escape(item['why'])}</small>"
f"<dl><dt>修复位置</dt><dd>{html.escape(item['source_fix'])}</dd>"
f"<dt>验证命令</dt><dd><code>{html.escape(item['verification_command'])}</code></dd></dl>"
f"{evidence_steps_html}"
f"{source_refs_html}"
f"<footer>{html.escape(item['evidence'])} {link_html}</footer>"
"</article>"
)
return "".join(cards)