Files
yao-meta-skill/scripts/render_weekly_curator_report.py
T
YAO 31ce04c655 Split meta skill CLI and review gates
Merge the beta-ready Yao Meta Skill architecture, report, evidence gate, and release-boundary updates.\n\nRelease boundary: beta/public testing is allowed; formal world-class, fully reviewed, or superiority claims remain blocked until the pending evidence gates are accepted.
2026-06-17 18:43:02 +08:00

434 lines
17 KiB
Python

#!/usr/bin/env python3
from __future__ import annotations
import argparse
import json
from collections import Counter
from datetime import date, datetime, timezone
from pathlib import Path
from typing import Any
SCRIPT_INTERFACE = "cli"
SCRIPT_INTERFACE_REASON = "Renders a weekly SkillOps curator report from generated reports without scanning private logs or writing source files."
SUMMARY_FIELDS = [
"decision",
"week_id",
"daily_report_count",
"opportunity_count",
"unique_opportunity_count",
"ready_for_approval_review_count",
"proposal_review_count",
"observe_more_evidence_count",
"report_only_count",
"top_score",
"skill_count",
"actionable_portfolio_issue_count",
"release_lock_ready",
"evidence_consistency_ok",
"public_world_class_ready",
"world_class_pending_count",
"writes_source_files",
"auto_patch_enabled",
"failure_count",
]
def utc_now() -> str:
return datetime.now(timezone.utc).replace(microsecond=0).isoformat().replace("+00:00", "Z")
def parse_date(value: str) -> date:
candidate = value[:10] if len(value) >= 10 else value
try:
return date.fromisoformat(candidate)
except ValueError:
return date.today()
def week_id(generated_at: str) -> str:
iso = parse_date(generated_at).isocalendar()
return f"{iso.year}-W{iso.week:02d}"
def display_path(path: Path, root: Path) -> str:
try:
return str(path.resolve().relative_to(root.resolve()))
except ValueError:
return f"[external-explicit-source]/{path.name}"
def resolve_output(skill_dir: Path, value: str) -> Path:
path = Path(value)
return path if path.is_absolute() else skill_dir / path
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 write_json(path: Path, payload: dict[str, Any]) -> None:
path.parent.mkdir(parents=True, exist_ok=True)
path.write_text(json.dumps(payload, ensure_ascii=False, indent=2) + "\n", encoding="utf-8")
def write_text(path: Path, text: str) -> None:
path.parent.mkdir(parents=True, exist_ok=True)
path.write_text(text, encoding="utf-8")
def default_weekly_path(skill_dir: Path, generated_at: str, suffix: str) -> Path:
return skill_dir / "reports" / "skillops" / "weekly" / f"{week_id(generated_at)}.{suffix}"
def daily_report_paths(skill_dir: Path, explicit_paths: list[Path]) -> list[Path]:
if explicit_paths:
return [path if path.is_absolute() else skill_dir / path for path in explicit_paths]
daily_dir = skill_dir / "reports" / "skillops" / "daily"
if not daily_dir.exists():
return []
return sorted(daily_dir.glob("*.json"))
def _as_int(value: Any, default: int = 0) -> int:
if isinstance(value, bool):
return default
try:
return int(value)
except (TypeError, ValueError):
return default
def _dedupe_opportunities(daily_reports: list[dict[str, Any]]) -> list[dict[str, Any]]:
by_id: dict[str, dict[str, Any]] = {}
for report in daily_reports:
report_date = str(report.get("generated_at") or "")[:10]
for item in report.get("opportunities", []):
if not isinstance(item, dict):
continue
opportunity_id = str(item.get("opportunity_id") or "")
if not opportunity_id:
continue
current = {**item, "daily_report_date": report_date}
existing = by_id.get(opportunity_id)
if existing is None or _as_int(current.get("score")) > _as_int(existing.get("score")):
by_id[opportunity_id] = current
return sorted(by_id.values(), key=lambda item: (-_as_int(item.get("score")), str(item.get("opportunity_id"))))
def _counter(items: list[dict[str, Any]], key: str) -> dict[str, int]:
values = Counter(str(item.get(key) or "") for item in items)
values.pop("", None)
return dict(sorted(values.items()))
def _portfolio_issue_count(atlas_summary: dict[str, Any]) -> int:
keys = [
"actionable_route_collision_count",
"actionable_owner_gap_count",
"actionable_stale_count",
"actionable_drift_signal_count",
"no_route_opportunity_count",
]
return sum(_as_int(atlas_summary.get(key)) for key in keys)
def build_actions(summary: dict[str, Any], opportunities: list[dict[str, Any]], atlas_summary: dict[str, Any]) -> list[dict[str, str]]:
actions: list[dict[str, str]] = []
if summary["ready_for_approval_review_count"]:
actions.append(
{
"key": "review-ready-opportunities",
"priority": "high",
"action": "Review the top ready-for-approval SkillOps opportunities before preparing any approval ledger entry.",
}
)
elif opportunities:
actions.append(
{
"key": "review-proposal-queue",
"priority": "medium",
"action": "Review proposal-level SkillOps opportunities and keep low-evidence items in observation.",
}
)
if summary["actionable_portfolio_issue_count"]:
actions.append(
{
"key": "triage-skill-library",
"priority": "high",
"action": "Triage actionable route collisions, owner gaps, stale skills, drift signals, or no-route opportunities from Skill Atlas.",
}
)
if summary["world_class_pending_count"]:
actions.append(
{
"key": "close-world-class-evidence",
"priority": "high",
"action": "Collect accepted external or human evidence for pending world-class ledger entries before public claims.",
}
)
if summary["evidence_consistency_ok"] is not True:
actions.append(
{
"key": "refresh-evidence-consistency",
"priority": "high",
"action": "Regenerate evidence consistency before using this curator report for release decisions.",
}
)
if not atlas_summary:
actions.append(
{
"key": "refresh-skill-atlas",
"priority": "medium",
"action": "Generate Skill Atlas so weekly curation can see portfolio-level drift and stale-skill signals.",
}
)
if not actions:
actions.append({"key": "monitor", "priority": "low", "action": "No curation action required beyond routine monitoring."})
return actions
def build_report(skill_dir: Path, generated_at: str, daily_json: list[Path] | None = None) -> dict[str, Any]:
skill_dir = skill_dir.resolve()
reports_dir = skill_dir / "reports"
failures: list[str] = []
daily_paths = daily_report_paths(skill_dir, daily_json or [])
daily_reports = []
for path in daily_paths:
payload = load_json(path)
if payload.get("ok") is True:
daily_reports.append(payload)
else:
failures.append(f"Daily SkillOps report is missing or invalid: {display_path(path, skill_dir)}")
opportunities = _dedupe_opportunities(daily_reports)
decision_counts = _counter(opportunities, "decision")
action_counts = _counter(opportunities, "action_type")
risk_counts = _counter(opportunities, "risk_level")
atlas = load_json(reports_dir / "skill_atlas.json")
atlas_summary = atlas.get("summary", {}) if isinstance(atlas.get("summary"), dict) else {}
benchmark = load_json(reports_dir / "benchmark_reproducibility.json")
benchmark_summary = benchmark.get("summary", {}) if isinstance(benchmark.get("summary"), dict) else {}
consistency = load_json(reports_dir / "evidence_consistency.json")
ledger = load_json(reports_dir / "world_class_evidence_ledger.json")
ledger_summary = ledger.get("summary", {}) if isinstance(ledger.get("summary"), dict) else {}
portfolio_issue_count = _portfolio_issue_count(atlas_summary)
failure_count = len(failures)
summary = {
"decision": "blocked"
if failure_count
else "curator-review"
if opportunities or portfolio_issue_count or _as_int(ledger_summary.get("pending_count"))
else "monitor",
"week_id": week_id(generated_at),
"daily_report_count": len(daily_reports),
"opportunity_count": sum(_as_int(report.get("opportunity_summary", {}).get("opportunity_count")) for report in daily_reports),
"unique_opportunity_count": len(opportunities),
"ready_for_approval_review_count": decision_counts.get("ready_for_approval_review", 0),
"proposal_review_count": decision_counts.get("proposal_review", 0),
"observe_more_evidence_count": decision_counts.get("observe_more_evidence", 0),
"report_only_count": decision_counts.get("report_only", 0),
"top_score": _as_int(opportunities[0].get("score")) if opportunities else 0,
"skill_count": _as_int(atlas_summary.get("skill_count")),
"actionable_portfolio_issue_count": portfolio_issue_count,
"release_lock_ready": benchmark_summary.get("release_lock_ready") is True,
"evidence_consistency_ok": consistency.get("ok") is True,
"public_world_class_ready": ledger_summary.get("ready_to_claim_world_class") is True,
"world_class_pending_count": _as_int(ledger_summary.get("pending_count")),
"writes_source_files": False,
"auto_patch_enabled": False,
"failure_count": failure_count,
}
actions = build_actions(summary, opportunities, atlas_summary)
curator_contract = {
"schema_version": "1.0",
"contract": "weekly-skillops-curator-report",
"cadence": "weekly",
"source_of_truth": [
"reports/skillops/daily/*.json",
"reports/skill_atlas.json",
"reports/benchmark_reproducibility.json",
"reports/evidence_consistency.json",
"reports/world_class_evidence_ledger.json",
],
"raw_content_stored": False,
"redacted_or_generated_evidence_only": True,
"proposal_only": True,
"writes_source_files": False,
"auto_patch_enabled": False,
"approval_required_for_writes": True,
"counts_as_world_class_evidence": False,
}
report = {
"schema_version": "1.0",
"ok": failure_count == 0,
"generated_at": generated_at,
"skill_dir": display_path(skill_dir, skill_dir),
**{key: summary[key] for key in SUMMARY_FIELDS},
"summary": summary,
"curator_contract": curator_contract,
"report_contract": {
"schema_version": "1.0",
"contract": "weekly-skillops-curator-report",
"top_level_mirrors_summary": True,
"summary_fields": SUMMARY_FIELDS,
},
"daily_reports": [display_path(path, skill_dir) for path in daily_paths],
"opportunity_summary": {
"action_type_counts": action_counts,
"decision_counts": decision_counts,
"risk_level_counts": risk_counts,
},
"curator_queue": opportunities[:20],
"portfolio": {
"skill_count": summary["skill_count"],
"actionable_issue_count": portfolio_issue_count,
"route_collision_count": _as_int(atlas_summary.get("route_collision_count")),
"actionable_route_collision_count": _as_int(atlas_summary.get("actionable_route_collision_count")),
"owner_gap_count": _as_int(atlas_summary.get("owner_gap_count")),
"actionable_owner_gap_count": _as_int(atlas_summary.get("actionable_owner_gap_count")),
"stale_count": _as_int(atlas_summary.get("stale_count")),
"actionable_stale_count": _as_int(atlas_summary.get("actionable_stale_count")),
"drift_signal_count": _as_int(atlas_summary.get("drift_signal_count")),
"actionable_drift_signal_count": _as_int(atlas_summary.get("actionable_drift_signal_count")),
"no_route_opportunity_count": _as_int(atlas_summary.get("no_route_opportunity_count")),
},
"release_state": {
"release_lock_ready": summary["release_lock_ready"],
"evidence_consistency_ok": summary["evidence_consistency_ok"],
"public_world_class_ready": summary["public_world_class_ready"],
"world_class_pending_count": summary["world_class_pending_count"],
},
"actions": actions,
"failures": failures,
"source_reports": {
"daily_dir": "reports/skillops/daily",
"skill_atlas": "reports/skill_atlas.json",
"benchmark_reproducibility": "reports/benchmark_reproducibility.json",
"evidence_consistency": "reports/evidence_consistency.json",
"world_class_ledger": "reports/world_class_evidence_ledger.json",
},
"artifacts": {
"json": str(default_weekly_path(skill_dir, generated_at, "json").relative_to(skill_dir)),
"markdown": str(default_weekly_path(skill_dir, generated_at, "md").relative_to(skill_dir)),
},
}
return report
def render_markdown(report: dict[str, Any]) -> str:
summary = report["summary"]
lines = [
"# Weekly SkillOps Curator Report",
"",
f"Generated at: `{report['generated_at']}`",
f"Week: `{summary['week_id']}`",
"",
"## Summary",
"",
]
for key in [
"decision",
"daily_report_count",
"unique_opportunity_count",
"ready_for_approval_review_count",
"proposal_review_count",
"top_score",
"skill_count",
"actionable_portfolio_issue_count",
"release_lock_ready",
"evidence_consistency_ok",
"public_world_class_ready",
"world_class_pending_count",
]:
lines.append(f"- {key}: `{summary[key]}`")
lines.extend(
[
"",
"This report is a weekly curator cockpit for generated SkillOps evidence. It does not scan private logs, write source files, apply patches, or count as world-class evidence.",
"",
"## Curator Boundary",
"",
]
)
contract = report["curator_contract"]
for key in [
"raw_content_stored",
"redacted_or_generated_evidence_only",
"proposal_only",
"writes_source_files",
"auto_patch_enabled",
"approval_required_for_writes",
"counts_as_world_class_evidence",
]:
lines.append(f"- {key}: `{str(contract[key]).lower()}`")
lines.extend(["", "## Actions", ""])
for action in report["actions"]:
lines.append(f"- `{action['priority']}` {action['action']}")
lines.extend(["", "## Curator Queue", ""])
if not report["curator_queue"]:
lines.append("- No weekly curator opportunities.")
for item in report["curator_queue"]:
lines.extend(
[
f"### {item.get('title', '')}",
"",
f"- ID: `{item.get('opportunity_id', '')}`",
f"- Action: `{item.get('action_type', '')}`",
f"- Decision: `{item.get('decision', '')}`",
f"- Score: `{item.get('score', 0)}`",
f"- Risk: `{item.get('risk_level', '')}`",
f"- Source day: `{item.get('daily_report_date', '')}`",
f"- Policy: {item.get('policy_reason', '')}",
"",
]
)
lines.extend(["## Portfolio Signals", ""])
for key, value in report["portfolio"].items():
lines.append(f"- {key}: `{value}`")
lines.extend(["", "## Evidence", ""])
for label, path in report["source_reports"].items():
lines.append(f"- {label}: `{path}`")
if report["failures"]:
lines.extend(["", "## Failures", ""])
lines.extend(f"- {failure}" for failure in report["failures"])
return "\n".join(lines).rstrip() + "\n"
def main() -> None:
parser = argparse.ArgumentParser(description="Render a weekly SkillOps curator report from generated SkillOps evidence.")
parser.add_argument("skill_dir", nargs="?", default=".")
parser.add_argument("--daily-json", action="append", default=[], help="Explicit Daily SkillOps JSON report to include.")
parser.add_argument("--output-json")
parser.add_argument("--output-md")
parser.add_argument("--generated-at", default=utc_now())
args = parser.parse_args()
skill_dir = Path(args.skill_dir).resolve()
report = build_report(skill_dir, args.generated_at, [Path(item) for item in args.daily_json])
output_json = resolve_output(skill_dir, args.output_json) if args.output_json else default_weekly_path(skill_dir, args.generated_at, "json")
output_md = resolve_output(skill_dir, args.output_md) if args.output_md else default_weekly_path(skill_dir, args.generated_at, "md")
report["artifacts"] = {
"json": display_path(output_json, skill_dir),
"markdown": display_path(output_md, skill_dir),
}
write_json(output_json, report)
write_text(output_md, render_markdown(report))
print(json.dumps(report, ensure_ascii=False, indent=2))
if not report["ok"]:
raise SystemExit(2)
if __name__ == "__main__":
main()